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 let u_val: u128 = if *i >= 0 {
322 *i as u128
323 } else if *i >= -(1i128 << 63) {
324 (*i + (1i128 << 64)) as u128
325 } else {
326 *i as u128
327 };
328 let bytes = u_val.to_be_bytes();
329 let mut padded = [0u8; 32];
330 padded[16..].copy_from_slice(&bytes);
331 out.push(Fr::from(padded));
332 }
333 AbiValue::Array(items) => {
334 for item in items {
335 flatten_abi_value(item, out);
336 }
337 }
338 AbiValue::String(s) => {
339 for byte in s.bytes() {
340 out.push(Fr::from(u64::from(byte)));
341 }
342 }
343 AbiValue::Struct(map) => {
344 for value in map.values() {
346 flatten_abi_value(value, out);
347 }
348 }
349 AbiValue::Tuple(items) => {
350 for item in items {
351 flatten_abi_value(item, out);
352 }
353 }
354 }
355}
356
357pub fn compute_inner_auth_wit_hash_from_action(caller: &AztecAddress, call: &FunctionCall) -> Fr {
363 let args_as_fields = abi_values_to_fields(&call.args);
364 let args_hash = compute_var_args_hash(&args_as_fields);
365 compute_inner_auth_wit_hash(&[caller.0, call.selector.to_field(), args_hash])
366}
367
368#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
373#[serde(rename_all = "camelCase")]
374pub struct ChainInfo {
375 pub chain_id: Fr,
377 pub version: Fr,
379}
380
381#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
386#[serde(tag = "type", rename_all = "snake_case")]
387pub enum MessageHashOrIntent {
388 Hash {
390 hash: Fr,
392 },
393 Intent {
395 caller: AztecAddress,
397 call: FunctionCall,
399 },
400 InnerHash {
405 consumer: AztecAddress,
407 inner_hash: Fr,
409 },
410}
411
412pub fn compute_auth_wit_message_hash(intent: &MessageHashOrIntent, chain_info: &ChainInfo) -> Fr {
425 match intent {
426 MessageHashOrIntent::Hash { hash } => *hash,
427 MessageHashOrIntent::Intent { caller, call } => {
428 let inner_hash = compute_inner_auth_wit_hash_from_action(caller, call);
429 compute_outer_auth_wit_hash(
430 &call.to,
431 &chain_info.chain_id,
432 &chain_info.version,
433 &inner_hash,
434 )
435 }
436 MessageHashOrIntent::InnerHash {
437 consumer,
438 inner_hash,
439 } => compute_outer_auth_wit_hash(
440 consumer,
441 &chain_info.chain_id,
442 &chain_info.version,
443 inner_hash,
444 ),
445 }
446}
447
448pub fn compute_initialization_hash(
458 init_fn: Option<&FunctionArtifact>,
459 args: &[AbiValue],
460) -> Result<Fr, Error> {
461 match init_fn {
462 None => Ok(Fr::zero()),
463 Some(func) => {
464 let selector = func.selector.unwrap_or_else(|| {
465 FunctionSelector::from_name_and_parameters(&func.name, &func.parameters)
466 });
467 let encoded_args = encode_arguments(func, args)?;
468 let args_hash = compute_var_args_hash(&encoded_args);
469 Ok(poseidon2_hash_with_separator(
470 &[selector.to_field(), args_hash],
471 domain_separator::INITIALIZER,
472 ))
473 }
474 }
475}
476
477pub fn compute_initialization_hash_from_encoded(selector: Fr, encoded_args: &[Fr]) -> Fr {
479 let args_hash = compute_var_args_hash(encoded_args);
480 poseidon2_hash_with_separator(&[selector, args_hash], domain_separator::INITIALIZER)
481}
482
483pub fn compute_private_functions_root(private_functions: &mut [(FunctionSelector, Fr)]) -> Fr {
492 let tree_height = constants::FUNCTION_TREE_HEIGHT;
493 let num_leaves = 1usize << tree_height; private_functions.sort_by_key(|(sel, _)| u32::from_be_bytes(sel.0));
497
498 let zero_leaf = poseidon2_hash(&[Fr::zero(), Fr::zero()]);
500 let mut leaves: Vec<Fr> = Vec::with_capacity(num_leaves);
501 for (sel, vk_hash) in private_functions.iter() {
502 let leaf = poseidon2_hash_with_separator(
503 &[sel.to_field(), *vk_hash],
504 domain_separator::PRIVATE_FUNCTION_LEAF,
505 );
506 leaves.push(leaf);
507 }
508 leaves.resize(num_leaves, zero_leaf);
510
511 poseidon_merkle_root(&leaves)
513}
514
515fn poseidon_merkle_root(leaves: &[Fr]) -> Fr {
517 if leaves.is_empty() {
518 return Fr::zero();
519 }
520 if leaves.len() == 1 {
521 return leaves[0];
522 }
523
524 let mut current = leaves.to_vec();
525 while current.len() > 1 {
526 let mut next = Vec::with_capacity(current.len().div_ceil(2));
527 for chunk in current.chunks(2) {
528 let left = chunk[0];
529 let right = if chunk.len() > 1 {
530 chunk[1]
531 } else {
532 Fr::zero()
533 };
534 next.push(poseidon2_hash(&[left, right]));
535 }
536 current = next;
537 }
538 current[0]
539}
540
541fn sha256_merkle_root(leaves: &[Fr]) -> Fr {
542 if leaves.is_empty() {
543 return Fr::zero();
544 }
545 if leaves.len() == 1 {
546 return leaves[0];
547 }
548
549 let mut current = leaves.to_vec();
550 while current.len() > 1 {
551 let mut next = Vec::with_capacity(current.len().div_ceil(2));
552 for chunk in current.chunks(2) {
553 let left = chunk[0].to_be_bytes();
554 let right = chunk.get(1).unwrap_or(&Fr::zero()).to_be_bytes();
555 next.push(sha256_to_field(
556 &[left.as_slice(), right.as_slice()].concat(),
557 ));
558 }
559 current = next;
560 }
561 current[0]
562}
563
564pub fn sha256_to_field_pub(data: &[u8]) -> Fr {
570 sha256_to_field(data)
571}
572
573fn sha256_to_field(data: &[u8]) -> Fr {
574 let hash = Sha256::digest(data);
575 let mut bytes = [0u8; 32];
576 bytes[1..].copy_from_slice(&hash[..31]);
578 Fr::from(bytes)
579}
580
581pub fn compute_artifact_hash(artifact: &ContractArtifact) -> Fr {
586 let private_fn_tree_root = compute_artifact_function_tree_root(artifact, false);
587 let unconstrained_fn_tree_root = compute_artifact_function_tree_root(artifact, true);
588 let metadata_hash = compute_artifact_metadata_hash(artifact);
589
590 let mut data = Vec::new();
591 data.push(1u8);
592 data.extend_from_slice(&private_fn_tree_root.to_be_bytes());
593 data.extend_from_slice(&unconstrained_fn_tree_root.to_be_bytes());
594 data.extend_from_slice(&metadata_hash.to_be_bytes());
595 sha256_to_field(&data)
596}
597
598fn canonical_json_string(value: &serde_json::Value) -> String {
599 match value {
600 serde_json::Value::Null => "null".to_owned(),
601 serde_json::Value::Bool(boolean) => boolean.to_string(),
602 serde_json::Value::Number(number) => number.to_string(),
603 serde_json::Value::String(string) => {
604 serde_json::to_string(string).unwrap_or_else(|_| "\"\"".to_owned())
605 }
606 serde_json::Value::Array(items) => {
607 let inner = items
608 .iter()
609 .map(canonical_json_string)
610 .collect::<Vec<_>>()
611 .join(",");
612 format!("[{inner}]")
613 }
614 serde_json::Value::Object(map) => {
615 let mut entries = map.iter().collect::<Vec<_>>();
616 entries.sort_by(|(left, _), (right, _)| left.cmp(right));
617 let inner = entries
618 .into_iter()
619 .map(|(key, value)| {
620 let key = serde_json::to_string(key).unwrap_or_else(|_| "\"\"".to_owned());
621 format!("{key}:{}", canonical_json_string(value))
622 })
623 .collect::<Vec<_>>()
624 .join(",");
625 format!("{{{inner}}}")
626 }
627 }
628}
629
630fn decode_artifact_bytes(encoded: &str) -> Vec<u8> {
631 if let Some(hex) = encoded.strip_prefix("0x") {
632 return hex::decode(hex).unwrap_or_else(|_| encoded.as_bytes().to_vec());
633 }
634
635 use base64::Engine;
636 base64::engine::general_purpose::STANDARD
637 .decode(encoded)
638 .unwrap_or_else(|_| encoded.as_bytes().to_vec())
639}
640
641fn compute_artifact_function_tree_root(artifact: &ContractArtifact, unconstrained: bool) -> Fr {
643 let functions: Vec<&FunctionArtifact> = artifact
644 .functions
645 .iter()
646 .filter(|f| {
647 if unconstrained {
648 f.function_type == FunctionType::Utility || f.is_unconstrained == Some(true)
649 } else {
650 f.function_type == FunctionType::Private
651 }
652 })
653 .collect();
654
655 if functions.is_empty() {
656 return Fr::zero();
657 }
658
659 let leaves: Vec<Fr> = functions
660 .iter()
661 .map(|func| {
662 let selector = func.selector.unwrap_or_else(|| {
663 FunctionSelector::from_name_and_parameters(&func.name, &func.parameters)
664 });
665 let metadata_hash = compute_function_metadata_hash(func);
666 let bytecode_hash = compute_function_bytecode_hash(func);
667
668 let mut leaf_data = Vec::new();
669 leaf_data.push(1u8);
670 leaf_data.extend_from_slice(&selector.0);
671 leaf_data.extend_from_slice(&metadata_hash.to_be_bytes());
672 leaf_data.extend_from_slice(&bytecode_hash.to_be_bytes());
673 sha256_to_field(&leaf_data)
674 })
675 .collect();
676
677 let height = if leaves.len() <= 1 {
678 0
679 } else {
680 (leaves.len() as f64).log2().ceil() as usize
681 };
682 let num_leaves = 1usize << height;
683 let mut padded = leaves;
684 padded.resize(num_leaves.max(1), Fr::zero());
685 sha256_merkle_root(&padded)
686}
687
688fn compute_function_metadata_hash(func: &FunctionArtifact) -> Fr {
690 let metadata = serde_json::to_value(&func.return_types).unwrap_or(serde_json::Value::Null);
691 let serialized = canonical_json_string(&metadata);
692 sha256_to_field(serialized.as_bytes())
693}
694
695fn compute_function_bytecode_hash(func: &FunctionArtifact) -> Fr {
697 match &func.bytecode {
698 Some(bc) if !bc.is_empty() => sha256_to_field(&decode_artifact_bytes(bc)),
699 _ => Fr::zero(),
700 }
701}
702
703fn compute_artifact_metadata_hash(artifact: &ContractArtifact) -> Fr {
705 let mut metadata = serde_json::Map::new();
706 metadata.insert(
707 "name".to_owned(),
708 serde_json::Value::String(artifact.name.clone()),
709 );
710 if let Some(outputs) = &artifact.outputs {
711 metadata.insert("outputs".to_owned(), outputs.clone());
712 }
713 let serialized = canonical_json_string(&serde_json::Value::Object(metadata));
714 sha256_to_field(serialized.as_bytes())
715}
716
717pub fn compute_public_bytecode_commitment(packed_bytecode: &[u8]) -> Fr {
721 let fields = crate::abi::buffer_as_fields(
722 packed_bytecode,
723 constants::MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
724 )
725 .expect("packed bytecode exceeds maximum field count");
726 let byte_length = fields[0].to_usize() as u64;
727 let length_in_fields = byte_length.div_ceil(31) as usize;
728
729 let separator = Fr::from(u64::from(domain_separator::PUBLIC_BYTECODE) + (byte_length << 32));
730 poseidon2_hash_with_separator_field(&fields[1..1 + length_in_fields], separator)
731}
732
733pub fn compute_contract_class_id(
737 artifact_hash: Fr,
738 private_functions_root: Fr,
739 public_bytecode_commitment: Fr,
740) -> Fr {
741 poseidon2_hash_with_separator(
742 &[
743 artifact_hash,
744 private_functions_root,
745 public_bytecode_commitment,
746 ],
747 domain_separator::CONTRACT_CLASS_ID,
748 )
749}
750
751pub fn compute_contract_class_id_from_artifact(artifact: &ContractArtifact) -> Result<Fr, Error> {
753 let artifact_hash = compute_artifact_hash(artifact);
754 let private_fns_root = compute_private_functions_root_from_artifact(artifact)?;
755 let public_bytecode = extract_packed_public_bytecode(artifact);
756 let public_bytecode_commitment = compute_public_bytecode_commitment(&public_bytecode);
757 Ok(compute_contract_class_id(
758 artifact_hash,
759 private_fns_root,
760 public_bytecode_commitment,
761 ))
762}
763
764pub fn compute_private_functions_root_from_artifact(
766 artifact: &ContractArtifact,
767) -> Result<Fr, Error> {
768 let mut private_fns: Vec<(FunctionSelector, Fr)> = artifact
769 .functions
770 .iter()
771 .filter(|f| f.function_type == FunctionType::Private)
772 .map(|f| {
773 let selector = f.selector.unwrap_or_else(|| {
774 FunctionSelector::from_name_and_parameters(&f.name, &f.parameters)
775 });
776 let vk_hash = f.verification_key_hash.unwrap_or(Fr::zero());
777 (selector, vk_hash)
778 })
779 .collect();
780
781 Ok(compute_private_functions_root(&mut private_fns))
782}
783
784fn extract_packed_public_bytecode(artifact: &ContractArtifact) -> Vec<u8> {
786 artifact
788 .functions
789 .iter()
790 .find(|f| f.function_type == FunctionType::Public && f.name == "public_dispatch")
791 .and_then(|f| f.bytecode.as_ref())
792 .map(|bc| decode_artifact_bytes(bc))
793 .unwrap_or_default()
794}
795
796pub fn compute_salted_initialization_hash(
804 salt: Fr,
805 initialization_hash: Fr,
806 deployer: AztecAddress,
807) -> Fr {
808 poseidon2_hash_with_separator(
809 &[salt, initialization_hash, deployer.0],
810 domain_separator::PARTIAL_ADDRESS,
811 )
812}
813
814pub fn compute_partial_address(
818 original_contract_class_id: Fr,
819 salted_initialization_hash: Fr,
820) -> Fr {
821 poseidon2_hash_with_separator(
822 &[original_contract_class_id, salted_initialization_hash],
823 domain_separator::PARTIAL_ADDRESS,
824 )
825}
826
827pub fn compute_address(
834 public_keys: &PublicKeys,
835 partial_address: &Fr,
836) -> Result<AztecAddress, Error> {
837 let public_keys_hash = public_keys.hash();
838 let preaddress = poseidon2_hash_with_separator(
839 &[public_keys_hash, *partial_address],
840 domain_separator::CONTRACT_ADDRESS_V1,
841 );
842
843 let preaddress_fq = Fq::from_be_bytes_mod_order(&preaddress.to_be_bytes());
846
847 let g = grumpkin::generator();
848 let preaddress_point = grumpkin::scalar_mul(&preaddress_fq, &g);
849
850 let ivpk_m = &public_keys.master_incoming_viewing_public_key;
851 let address_point = if ivpk_m.is_zero() {
853 preaddress_point
854 } else {
855 grumpkin::point_add(&preaddress_point, ivpk_m)
856 };
857
858 if address_point.is_infinite {
859 return Err(Error::InvalidData(
860 "address derivation resulted in point at infinity".to_owned(),
861 ));
862 }
863
864 Ok(AztecAddress(address_point.x))
865}
866
867pub fn compute_contract_address_from_instance(
876 instance: &ContractInstance,
877) -> Result<AztecAddress, Error> {
878 let salted_init_hash = compute_salted_initialization_hash(
879 instance.salt,
880 instance.initialization_hash,
881 instance.deployer,
882 );
883 let partial_address =
884 compute_partial_address(instance.original_contract_class_id, salted_init_hash);
885
886 compute_address(&instance.public_keys, &partial_address)
887}
888
889#[cfg(test)]
890#[allow(clippy::expect_used, clippy::panic)]
891mod tests {
892 use super::*;
893 use crate::abi::{buffer_as_fields, FunctionSelector, FunctionType};
894
895 #[test]
896 fn var_args_hash_empty_returns_zero() {
897 assert_eq!(compute_var_args_hash(&[]), Fr::zero());
898 }
899
900 #[test]
901 fn poseidon2_hash_known_vector() {
902 let result = poseidon2_hash(&[Fr::from(1u64)]);
905 let expected =
906 Fr::from_hex("0x168758332d5b3e2d13be8048c8011b454590e06c44bce7f702f09103eef5a373")
907 .expect("valid hex");
908 assert_eq!(
909 result, expected,
910 "Poseidon2 hash of [1] must match barretenberg test vector"
911 );
912 }
913
914 #[test]
915 fn poseidon2_hash_with_separator_prepends_separator() {
916 let a = Fr::from(10u64);
918 let b = Fr::from(20u64);
919 let sep = 42u32;
920
921 let result = poseidon2_hash_with_separator(&[a, b], sep);
922 let manual = poseidon2_hash(&[Fr::from(u64::from(sep)), a, b]);
923 assert_eq!(result, manual);
924 }
925
926 #[test]
927 fn secret_hash_uses_correct_separator() {
928 let secret = Fr::from(42u64);
929 let result = compute_secret_hash(&secret);
930 let expected = poseidon2_hash_with_separator(&[secret], domain_separator::SECRET_HASH);
931 assert_eq!(result, expected);
932 assert!(!result.is_zero());
934 }
935
936 #[test]
937 fn secret_hash_is_deterministic() {
938 let secret = Fr::from(12345u64);
939 let h1 = compute_secret_hash(&secret);
940 let h2 = compute_secret_hash(&secret);
941 assert_eq!(h1, h2);
942 }
943
944 #[test]
945 fn var_args_hash_single_element() {
946 let result = compute_var_args_hash(&[Fr::from(42u64)]);
947 let expected =
949 poseidon2_hash_with_separator(&[Fr::from(42u64)], domain_separator::FUNCTION_ARGS);
950 assert_eq!(result, expected);
951 }
952
953 #[test]
954 fn inner_auth_wit_hash_uses_correct_separator() {
955 let args = [Fr::from(1u64), Fr::from(2u64), Fr::from(3u64)];
956 let result = compute_inner_auth_wit_hash(&args);
957 let expected = poseidon2_hash_with_separator(&args, domain_separator::AUTHWIT_INNER);
958 assert_eq!(result, expected);
959 }
960
961 #[test]
962 fn outer_auth_wit_hash_uses_correct_separator() {
963 let consumer = AztecAddress(Fr::from(100u64));
964 let chain_id = Fr::from(31337u64);
965 let version = Fr::from(1u64);
966 let inner_hash = Fr::from(999u64);
967
968 let result = compute_outer_auth_wit_hash(&consumer, &chain_id, &version, &inner_hash);
969 let expected = poseidon2_hash_with_separator(
970 &[consumer.0, chain_id, version, inner_hash],
971 domain_separator::AUTHWIT_OUTER,
972 );
973 assert_eq!(result, expected);
974 }
975
976 #[test]
977 fn inner_auth_wit_hash_from_action() {
978 let caller = AztecAddress(Fr::from(1u64));
979 let call = FunctionCall {
980 to: AztecAddress(Fr::from(2u64)),
981 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
982 args: vec![AbiValue::Field(Fr::from(100u64))],
983 function_type: FunctionType::Private,
984 is_static: false,
985 hide_msg_sender: false,
986 };
987
988 let result = compute_inner_auth_wit_hash_from_action(&caller, &call);
989
990 let args_hash = compute_var_args_hash(&[Fr::from(100u64)]);
992 let selector_field = call.selector.to_field();
993 let expected = compute_inner_auth_wit_hash(&[caller.0, selector_field, args_hash]);
994 assert_eq!(result, expected);
995 }
996
997 #[test]
998 fn auth_wit_message_hash_passthrough() {
999 let hash = Fr::from(42u64);
1000 let chain_info = ChainInfo {
1001 chain_id: Fr::from(31337u64),
1002 version: Fr::from(1u64),
1003 };
1004 let result =
1005 compute_auth_wit_message_hash(&MessageHashOrIntent::Hash { hash }, &chain_info);
1006 assert_eq!(result, hash);
1007 }
1008
1009 #[test]
1010 fn auth_wit_message_hash_from_intent() {
1011 let caller = AztecAddress(Fr::from(10u64));
1012 let consumer = AztecAddress(Fr::from(20u64));
1013 let call = FunctionCall {
1014 to: consumer,
1015 selector: FunctionSelector::from_hex("0x11223344").expect("valid"),
1016 args: vec![],
1017 function_type: FunctionType::Private,
1018 is_static: false,
1019 hide_msg_sender: false,
1020 };
1021 let chain_info = ChainInfo {
1022 chain_id: Fr::from(31337u64),
1023 version: Fr::from(1u64),
1024 };
1025
1026 let result = compute_auth_wit_message_hash(
1027 &MessageHashOrIntent::Intent {
1028 caller,
1029 call: call.clone(),
1030 },
1031 &chain_info,
1032 );
1033
1034 let inner = compute_inner_auth_wit_hash_from_action(&caller, &call);
1036 let expected = compute_outer_auth_wit_hash(
1037 &consumer,
1038 &chain_info.chain_id,
1039 &chain_info.version,
1040 &inner,
1041 );
1042 assert_eq!(result, expected);
1043 }
1044
1045 #[test]
1046 fn auth_wit_message_hash_from_inner_hash() {
1047 let consumer = AztecAddress(Fr::from(20u64));
1048 let inner_hash = Fr::from(999u64);
1049 let chain_info = ChainInfo {
1050 chain_id: Fr::from(31337u64),
1051 version: Fr::from(1u64),
1052 };
1053
1054 let result = compute_auth_wit_message_hash(
1055 &MessageHashOrIntent::InnerHash {
1056 consumer,
1057 inner_hash,
1058 },
1059 &chain_info,
1060 );
1061
1062 let expected = compute_outer_auth_wit_hash(
1063 &consumer,
1064 &chain_info.chain_id,
1065 &chain_info.version,
1066 &inner_hash,
1067 );
1068 assert_eq!(result, expected);
1069 }
1070
1071 #[test]
1072 fn abi_values_to_fields_basic_types() {
1073 let values = vec![
1074 AbiValue::Field(Fr::from(1u64)),
1075 AbiValue::Boolean(true),
1076 AbiValue::Boolean(false),
1077 AbiValue::Integer(42),
1078 ];
1079 let fields = abi_values_to_fields(&values);
1080 assert_eq!(fields.len(), 4);
1081 assert_eq!(fields[0], Fr::from(1u64));
1082 assert_eq!(fields[1], Fr::one());
1083 assert_eq!(fields[2], Fr::zero());
1084 assert_eq!(fields[3], Fr::from(42u64));
1085 }
1086
1087 #[test]
1088 fn abi_values_to_fields_nested() {
1089 let values = vec![AbiValue::Array(vec![
1090 AbiValue::Field(Fr::from(1u64)),
1091 AbiValue::Field(Fr::from(2u64)),
1092 ])];
1093 let fields = abi_values_to_fields(&values);
1094 assert_eq!(fields.len(), 2);
1095 assert_eq!(fields[0], Fr::from(1u64));
1096 assert_eq!(fields[1], Fr::from(2u64));
1097 }
1098
1099 #[test]
1100 fn message_hash_or_intent_serde_roundtrip() {
1101 let variants = vec![
1102 MessageHashOrIntent::Hash {
1103 hash: Fr::from(42u64),
1104 },
1105 MessageHashOrIntent::Intent {
1106 caller: AztecAddress(Fr::from(1u64)),
1107 call: FunctionCall {
1108 to: AztecAddress(Fr::from(2u64)),
1109 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
1110 args: vec![],
1111 function_type: FunctionType::Private,
1112 is_static: false,
1113 hide_msg_sender: false,
1114 },
1115 },
1116 MessageHashOrIntent::InnerHash {
1117 consumer: AztecAddress(Fr::from(3u64)),
1118 inner_hash: Fr::from(999u64),
1119 },
1120 ];
1121
1122 for variant in variants {
1123 let json = serde_json::to_string(&variant).expect("serialize");
1124 let decoded: MessageHashOrIntent = serde_json::from_str(&json).expect("deserialize");
1125 assert_eq!(decoded, variant);
1126 }
1127 }
1128
1129 #[test]
1130 fn chain_info_serde_roundtrip() {
1131 let info = ChainInfo {
1132 chain_id: Fr::from(31337u64),
1133 version: Fr::from(1u64),
1134 };
1135 let json = serde_json::to_string(&info).expect("serialize");
1136 let decoded: ChainInfo = serde_json::from_str(&json).expect("deserialize");
1137 assert_eq!(decoded, info);
1138 }
1139
1140 #[test]
1143 fn initialization_hash_no_constructor_returns_zero() {
1144 let result = compute_initialization_hash(None, &[]).expect("no constructor");
1145 assert_eq!(result, Fr::zero());
1146 }
1147
1148 #[test]
1149 fn initialization_hash_with_constructor() {
1150 use crate::abi::AbiParameter;
1151 let func = FunctionArtifact {
1152 name: "constructor".to_owned(),
1153 function_type: FunctionType::Private,
1154 is_initializer: true,
1155 is_static: false,
1156 is_only_self: None,
1157 parameters: vec![AbiParameter {
1158 name: "admin".to_owned(),
1159 typ: crate::abi::AbiType::Field,
1160 visibility: None,
1161 }],
1162 return_types: vec![],
1163 error_types: None,
1164 selector: Some(FunctionSelector::from_hex("0xe5fb6c81").expect("valid")),
1165 bytecode: None,
1166 verification_key_hash: None,
1167 verification_key: None,
1168 custom_attributes: None,
1169 is_unconstrained: None,
1170 debug_symbols: None,
1171 };
1172 let args = vec![AbiValue::Field(Fr::from(42u64))];
1173 let result = compute_initialization_hash(Some(&func), &args).expect("init hash");
1174 assert_ne!(result, Fr::zero());
1175 }
1176
1177 #[test]
1178 fn initialization_hash_from_encoded() {
1179 let selector = Fr::from(12345u64);
1180 let args = vec![Fr::from(1u64), Fr::from(2u64)];
1181 let result = compute_initialization_hash_from_encoded(selector, &args);
1182 let args_hash = compute_var_args_hash(&args);
1183 let expected =
1184 poseidon2_hash_with_separator(&[selector, args_hash], domain_separator::INITIALIZER);
1185 assert_eq!(result, expected);
1186 }
1187
1188 #[test]
1189 fn private_functions_root_empty() {
1190 let root = compute_private_functions_root(&mut []);
1191 assert_ne!(root, Fr::zero()); }
1194
1195 #[test]
1196 fn contract_class_id_deterministic() {
1197 let artifact_hash = Fr::from(1u64);
1198 let root = Fr::from(2u64);
1199 let commitment = Fr::from(3u64);
1200 let id1 = compute_contract_class_id(artifact_hash, root, commitment);
1201 let id2 = compute_contract_class_id(artifact_hash, root, commitment);
1202 assert_eq!(id1, id2);
1203 assert_ne!(id1, Fr::zero());
1204 }
1205
1206 #[test]
1207 fn buffer_as_fields_basic() {
1208 let data = vec![0u8; 31];
1209 let fields = buffer_as_fields(&data, 100).expect("encode");
1210 assert_eq!(fields.len(), 100);
1213 assert_eq!(fields[0], Fr::from(31u64)); }
1215
1216 #[test]
1217 fn buffer_as_fields_multiple_chunks() {
1218 let data = vec![0xffu8; 62]; let fields = buffer_as_fields(&data, 100).expect("encode");
1220 assert_eq!(fields.len(), 100);
1221 assert_eq!(fields[0], Fr::from(62u64)); }
1223
1224 #[test]
1225 fn public_bytecode_commitment_empty() {
1226 let result = compute_public_bytecode_commitment(&[]);
1227 assert_ne!(result, Fr::zero());
1229 }
1230
1231 #[test]
1232 fn public_bytecode_commitment_non_empty() {
1233 let data = vec![0x01u8; 100];
1234 let result = compute_public_bytecode_commitment(&data);
1235 assert_ne!(result, Fr::zero());
1236 }
1237
1238 #[test]
1239 fn salted_initialization_hash_uses_partial_address_separator() {
1240 let salt = Fr::from(1u64);
1241 let init_hash = Fr::from(2u64);
1242 let deployer = AztecAddress(Fr::from(3u64));
1243 let result = compute_salted_initialization_hash(salt, init_hash, deployer);
1244 let expected = poseidon2_hash_with_separator(
1245 &[salt, init_hash, deployer.0],
1246 domain_separator::PARTIAL_ADDRESS,
1247 );
1248 assert_eq!(result, expected);
1249 }
1250
1251 #[test]
1252 fn partial_address_uses_correct_separator() {
1253 let class_id = Fr::from(100u64);
1254 let salted = Fr::from(200u64);
1255 let result = compute_partial_address(class_id, salted);
1256 let expected =
1257 poseidon2_hash_with_separator(&[class_id, salted], domain_separator::PARTIAL_ADDRESS);
1258 assert_eq!(result, expected);
1259 }
1260
1261 #[test]
1262 fn contract_address_from_instance_default_keys() {
1263 use crate::types::{ContractInstance, PublicKeys};
1264 let instance = ContractInstance {
1265 version: 1,
1266 salt: Fr::from(42u64),
1267 deployer: AztecAddress(Fr::zero()),
1268 current_contract_class_id: Fr::from(100u64),
1269 original_contract_class_id: Fr::from(100u64),
1270 initialization_hash: Fr::zero(),
1271 public_keys: PublicKeys::default(),
1272 };
1273 let address =
1274 compute_contract_address_from_instance(&instance).expect("address derivation");
1275 assert_ne!(address.0, Fr::zero());
1276 }
1277
1278 #[test]
1279 fn contract_address_is_deterministic() {
1280 use crate::types::{ContractInstance, PublicKeys};
1281 let instance = ContractInstance {
1282 version: 1,
1283 salt: Fr::from(99u64),
1284 deployer: AztecAddress(Fr::from(1u64)),
1285 current_contract_class_id: Fr::from(200u64),
1286 original_contract_class_id: Fr::from(200u64),
1287 initialization_hash: Fr::from(300u64),
1288 public_keys: PublicKeys::default(),
1289 };
1290 let addr1 = compute_contract_address_from_instance(&instance).expect("addr1");
1291 let addr2 = compute_contract_address_from_instance(&instance).expect("addr2");
1292 assert_eq!(addr1, addr2);
1293 }
1294
1295 #[test]
1296 fn artifact_hash_deterministic() {
1297 let artifact = ContractArtifact {
1298 name: "Test".to_owned(),
1299 functions: vec![],
1300 outputs: None,
1301 file_map: None,
1302 context_inputs_sizes: None,
1303 };
1304 let h1 = compute_artifact_hash(&artifact);
1305 let h2 = compute_artifact_hash(&artifact);
1306 assert_eq!(h1, h2);
1307 }
1308
1309 #[test]
1310 fn class_id_from_artifact_no_functions() {
1311 let artifact = ContractArtifact {
1312 name: "Empty".to_owned(),
1313 functions: vec![],
1314 outputs: None,
1315 file_map: None,
1316 context_inputs_sizes: None,
1317 };
1318 let id = compute_contract_class_id_from_artifact(&artifact).expect("class id");
1319 assert_ne!(id, Fr::zero());
1320 }
1321
1322 #[test]
1323 fn protocol_contracts_hash_is_deterministic() {
1324 let h1 = compute_protocol_contracts_hash();
1325 let h2 = compute_protocol_contracts_hash();
1326 assert_eq!(h1, h2);
1327 assert_ne!(h1, Fr::zero());
1328 assert_eq!(
1329 h1,
1330 Fr::from_hex("0x2672340d9a0107a7b81e6d10d25b854debe613f3272e8738e8df0ca2ff297141")
1331 .expect("valid expected protocol contracts hash")
1332 );
1333 }
1334}