diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 82933e08a27..0c4f2b65a07 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -134,17 +134,17 @@ library Constants { uint256 internal constant L2_GAS_PER_NOTE_HASH = 32; uint256 internal constant L2_GAS_PER_NULLIFIER = 64; uint256 internal constant CANONICAL_KEY_REGISTRY_ADDRESS = - 9694109890306420370616891858093188542026876097103155811681068343994212062621; + 6823425185167517386380694778823032861295161686691976789058601691508103815523; uint256 internal constant CANONICAL_AUTH_REGISTRY_ADDRESS = - 16522644890256297179255458951626875692461008240031142745359776058397274208468; + 19361441716519463065948254497947932755739298943049449145365332870925554042208; uint256 internal constant DEPLOYER_CONTRACT_ADDRESS = - 19310994760783330368337163480198602393920956587162708699802190083077641908361; + 17119407406465801909352274670277571579675739451008438338071219340964365249977; uint256 internal constant REGISTERER_CONTRACT_ADDRESS = - 2631409926445785927331173506476539962589925110142857699603561302478860342858; + 12405643717676802437578418009978929188439257864607100766293128479227092050857; uint256 internal constant FEE_JUICE_ADDRESS = - 10248142274714515101077825679585135641434041564851038865006795089686437446849; + 12096583827711775893711303288210371301779120762215263550909768879597884314839; uint256 internal constant ROUTER_ADDRESS = - 7268799613082469933251235702514160327341161584122631177360064643484764773587; + 13369993014609648298719593339393818582619449364029983937306132176549332172208; uint256 internal constant AZTEC_ADDRESS_LENGTH = 1; uint256 internal constant GAS_FEES_LENGTH = 2; uint256 internal constant GAS_LENGTH = 2; @@ -193,16 +193,16 @@ library Constants { uint256 internal constant PUBLIC_CONTEXT_INPUTS_LENGTH = 42; uint256 internal constant AGGREGATION_OBJECT_LENGTH = 16; uint256 internal constant SCOPED_READ_REQUEST_LEN = 3; - uint256 internal constant PUBLIC_DATA_READ_LENGTH = 2; + uint256 internal constant PUBLIC_DATA_READ_LENGTH = 3; uint256 internal constant PRIVATE_VALIDATION_REQUESTS_LENGTH = 772; - uint256 internal constant PUBLIC_VALIDATION_REQUESTS_LENGTH = 770; + uint256 internal constant PUBLIC_VALIDATION_REQUESTS_LENGTH = 834; uint256 internal constant PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; uint256 internal constant COMBINED_ACCUMULATED_DATA_LENGTH = 610; uint256 internal constant COMBINED_CONSTANT_DATA_LENGTH = 43; uint256 internal constant PRIVATE_ACCUMULATED_DATA_LENGTH = 1336; uint256 internal constant PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2167; uint256 internal constant PUBLIC_ACCUMULATED_DATA_LENGTH = 1311; - uint256 internal constant PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 3885; + uint256 internal constant PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 3949; uint256 internal constant KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 663; uint256 internal constant CONSTANT_ROLLUP_DATA_LENGTH = 12; uint256 internal constant BASE_OR_MERGE_PUBLIC_INPUTS_LENGTH = 29; diff --git a/noir-projects/aztec-nr/authwit/src/auth.nr b/noir-projects/aztec-nr/authwit/src/auth.nr index a533bd589b5..6dca5f75fd8 100644 --- a/noir-projects/aztec-nr/authwit/src/auth.nr +++ b/noir-projects/aztec-nr/authwit/src/auth.nr @@ -170,7 +170,7 @@ use dep::aztec::{prelude::Deserialize, context::{PrivateContext, PublicContext, * * * --- FAQ --- - * Q: Why are we using a success flag of `keccak256("IS_VALID()")` instead of just returning a boolean? + * Q: Why are we using a success flag of `poseidon2_hash_bytes("IS_VALID()")` instead of just returning a boolean? * A: We want to make sure that we don't accidentally return `true` if there is a collision in the function selector. * By returning a hash of `IS_VALID()`, it becomes very unlikely that there is both a collision and we return * a success flag. @@ -189,7 +189,7 @@ use dep::aztec::{prelude::Deserialize, context::{PrivateContext, PublicContext, * chain to avoid a case where the same message could be used across multiple chains. */ -global IS_VALID_SELECTOR = 0xabf64ad4; // 4 first bytes of keccak256("IS_VALID()") +global IS_VALID_SELECTOR = 0x47dacd73; // 4 last bytes of poseidon2_hash_bytes("IS_VALID()") /** * Assert that `on_behalf_of` have authorized the current call with a valid authentication witness diff --git a/noir-projects/aztec-nr/aztec/src/deploy.nr b/noir-projects/aztec-nr/aztec/src/deploy.nr index 947a0eb85d8..d916b384f80 100644 --- a/noir-projects/aztec-nr/aztec/src/deploy.nr +++ b/noir-projects/aztec-nr/aztec/src/deploy.nr @@ -25,7 +25,9 @@ pub fn deploy_contract(context: &mut PrivateContext, target: AztecAddress) { let _call_result = context.call_private_function( DEPLOYER_CONTRACT_ADDRESS, - FunctionSelector::from_field(0x7ebd3690), + comptime { + FunctionSelector::from_signature("deploy(Field,(Field),Field,(Field),bool)") + }, serialized_args ); } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr index 2ba0ec2a8b7..4ba224c2761 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr @@ -273,7 +273,7 @@ mod test { // The following value was generated by `encrypted_event_log_incoming_body.test.ts` // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. let event_body_ciphertext_from_typescript = [ - 226, 240, 253, 6, 28, 52, 19, 131, 33, 132, 178, 212, 245, 62, 14, 190, 147, 228, 160, 190, 146, 61, 95, 203, 124, 153, 68, 168, 17, 150, 92, 0, 99, 214, 85, 64, 191, 78, 157, 131, 149, 96, 236, 253, 96, 172, 157, 30, 185, 29, 14, 152, 216, 130, 219, 151, 80, 185, 43, 223, 167, 8, 89, 189, 88, 188, 101, 137, 255, 136, 84, 252, 79, 18, 52, 3, 110, 54, 54, 206, 244, 209, 246, 226, 207, 247, 143, 253, 211, 75, 160, 224, 172, 41, 45, 7, 208, 137, 90, 56, 59, 4, 234, 48, 53, 23, 130, 230, 49, 249, 142, 243, 170, 72, 183, 242, 49, 124, 46, 52, 198, 75, 55, 102, 56, 89, 254, 67, 59, 157, 249, 120, 184, 67, 154, 16, 148, 227, 93, 37, 120, 199, 93, 166, 80, 127, 173, 52, 80, 135, 87, 1, 168, 164, 51, 48, 126, 120, 47, 102, 211, 227, 234, 170, 208, 99, 111, 198, 170, 226, 156, 244, 241, 174, 206, 30 + 226, 240, 253, 6, 28, 52, 19, 131, 33, 132, 178, 212, 245, 62, 14, 190, 147, 228, 160, 190, 146, 61, 95, 203, 124, 153, 68, 168, 17, 150, 92, 0, 99, 214, 85, 64, 191, 78, 157, 131, 149, 96, 236, 253, 96, 172, 157, 30, 201, 247, 40, 80, 60, 188, 158, 251, 242, 103, 197, 79, 165, 195, 10, 160, 255, 35, 167, 152, 25, 233, 77, 145, 214, 243, 210, 119, 0, 20, 29, 95, 15, 63, 33, 190, 184, 67, 254, 96, 128, 243, 220, 228, 201, 130, 86, 163, 52, 127, 111, 10, 212, 7, 160, 16, 87, 13, 39, 11, 5, 1, 164, 65, 8, 56, 82, 245, 13, 68, 176, 90, 100, 69, 243, 78, 117, 188, 221, 34, 178, 31, 155, 89, 143, 176, 129, 118, 36, 236, 64, 179, 52, 239, 184, 51, 51, 199, 221, 49, 81, 197, 17, 199, 192, 99, 49, 168, 157, 164, 190, 33, 240, 182, 214, 173, 7, 156, 102, 95, 65, 217, 225, 123, 42, 21, 124, 144 ]; assert_eq(event_body_ciphertext_from_typescript.len(), ciphertext.len()); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/event_selector.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/event_selector.nr index 25a41ad1b35..b03f665d227 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/event_selector.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/event_selector.nr @@ -51,14 +51,12 @@ impl EventSelector { pub fn from_signature(signature: str) -> Self { let bytes = signature.as_bytes(); - let hash = std::hash::keccak256(bytes, bytes.len() as u32); + let hash = crate::hash::poseidon2_hash_bytes(bytes); - let mut selector_be_bytes = [0; SELECTOR_SIZE]; - for i in 0..SELECTOR_SIZE { - selector_be_bytes[i] = hash[i]; - } + // We choose the last SELECTOR_SIZE bytes of the hash to avoid getting the first byte that is not full + let hash_bytes = hash.to_be_bytes::(); - EventSelector::from_field(field_from_bytes(selector_be_bytes, true)) + EventSelector::from_field(field_from_bytes(hash_bytes, true)) } pub fn zero() -> Self { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/function_selector.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/function_selector.nr index 80075b751bd..26ae4aeac2c 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/function_selector.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/function_selector.nr @@ -51,17 +51,27 @@ impl FunctionSelector { pub fn from_signature(signature: str) -> Self { let bytes = signature.as_bytes(); - let hash = std::hash::keccak256(bytes, bytes.len() as u32); + let hash = crate::hash::poseidon2_hash_bytes(bytes); - let mut selector_be_bytes = [0; SELECTOR_SIZE]; - for i in 0..SELECTOR_SIZE { - selector_be_bytes[i] = hash[i]; - } + // We choose the last SELECTOR_SIZE bytes of the hash to avoid getting the first byte that is not full + let hash_bytes = hash.to_be_bytes::(); - FunctionSelector::from_field(field_from_bytes(selector_be_bytes, true)) + FunctionSelector::from_field(field_from_bytes(hash_bytes, true)) } pub fn zero() -> Self { Self { inner: 0 } } } + +#[test] +fn test_is_valid_selector() { + let selector = FunctionSelector::from_signature("IS_VALID()"); + assert_eq(selector.to_field(), 0x73cdda47); +} + +#[test] +fn test_long_selector() { + let selector = FunctionSelector::from_signature("foo_and_bar_and_baz_and_foo_bar_baz_and_bar_foo"); + assert_eq(selector.to_field(), 0x7590a997); +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 45d5b997272..ed33835c719 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -191,12 +191,12 @@ global L2_GAS_PER_NOTE_HASH: u32 = 32; global L2_GAS_PER_NULLIFIER: u32 = 64; // CANONICAL CONTRACT ADDRESSES -global CANONICAL_KEY_REGISTRY_ADDRESS = AztecAddress::from_field(0x156eabf84e3ea50d40e3330224f2d2e81648fff8f1f7ec1bc6d2873cca6e959d); -global CANONICAL_AUTH_REGISTRY_ADDRESS = AztecAddress::from_field(0x24877c50868f86712240eb535d90d1c97403d074805dd3758c3aecb02958f8d4); -global DEPLOYER_CONTRACT_ADDRESS = AztecAddress::from_field(0x2ab1a2bd6d07d8d61ea56d85861446349e52c6b7c0612b702cb1e6db6ad0b089); -global REGISTERER_CONTRACT_ADDRESS = AztecAddress::from_field(0x05d15342d76e46e5be07d3cda0d753158431cdc5e39d29ce4e8fe1f5c070564a); -global FEE_JUICE_ADDRESS = AztecAddress::from_field(0x16a83e3395bc921a2441db55dce24f0e0932636901a2e676fa68b9b2b9a644c1); -global ROUTER_ADDRESS = AztecAddress::from_field(0x1011feaa54609098a884322267ec754c637b280c15aa79c3be9f1394e2b29cd3); +global CANONICAL_KEY_REGISTRY_ADDRESS = AztecAddress::from_field(0x0f15ebfaa7e1bb0d1c542c954a8f605909d0fa0a5fd829122bfdec15b4ada163); +global CANONICAL_AUTH_REGISTRY_ADDRESS = AztecAddress::from_field(0x2ace300b02ca5ab0a25052b1e852913a47292096997ca09f758c0e3624e84560); +global DEPLOYER_CONTRACT_ADDRESS = AztecAddress::from_field(0x25d93dc07b5baaf53a98caeae2679df3528cb83e11e2640a57a0a53abbaaadb9); +global REGISTERER_CONTRACT_ADDRESS = AztecAddress::from_field(0x1b6d5873cef5a35f681ab9468527f356c96e09b3c64603aef404ec2ad80aa3a9); +global FEE_JUICE_ADDRESS = AztecAddress::from_field(0x1abe6c7f5c4caf04cbf7556495e08ad9c0a225a5f9d33554ae07285b13c494d7); +global ROUTER_ADDRESS = AztecAddress::from_field(0x1d8f25db3e8faa6a96cb1ecf57876a2ee04581deb3c4f181488ccd817abcbdb0); // LENGTH OF STRUCTS SERIALIZED TO FIELDS global AZTEC_ADDRESS_LENGTH = 1; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr index 74c49d25d48..2b8832111a2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr @@ -15,6 +15,7 @@ use crate::{ recursion::verification_key::VerificationKey, traits::{is_empty, ToField}, utils::field::field_from_bytes_32_trunc }; +use super::utils::field::field_from_bytes; pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { let sha256_hashed = std::hash::sha256(bytes_to_hash); @@ -276,6 +277,36 @@ pub fn poseidon2_hash_with_separator( sponge.squeeze() } +#[no_predicates] +pub fn poseidon2_hash_bytes(inputs: [u8; N]) -> Field { + // We manually hash the inputs here, since we cannot express with the type system a constant size inputs array of Math.ceil(N/31) + let mut in_len = N / 31; + let mut has_padding = false; + if N % 31 != 0 { + in_len += 1; + has_padding = true; + } + + let two_pow_64 = 18446744073709551616; + let iv : Field = (in_len as Field) * two_pow_64; + let mut sponge = std::hash::poseidon2::Poseidon2::new(iv); + + let mut current_field = [0; 31]; + for i in 0..inputs.len() { + let index = i % 31; + current_field[index] = inputs[i]; + if index == 30 { + sponge.absorb(field_from_bytes(current_field, false)); + current_field = [0; 31]; + } + } + if has_padding { + sponge.absorb(field_from_bytes(current_field, false)); + } + + sponge.squeeze() +} + #[test] fn smoke_sha256_to_field() { let full_buffer = [ diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 9a657dcf9d0..e8f06054cf7 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -441,13 +441,15 @@ name = "aztec_macros" version = "0.34.0" dependencies = [ "acvm", + "bn254_blackbox_solver", "convert_case 0.6.0", + "hex", "im", "iter-extended", + "itertools", "noirc_errors", "noirc_frontend", "regex", - "tiny-keccak", ] [[package]] @@ -1124,12 +1126,6 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.9" @@ -4543,15 +4539,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinytemplate" version = "1.2.1" diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs index 43ee6a9ddd2..952c4498d84 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -13,7 +13,10 @@ mod schnorr; use ark_ec::AffineRepr; pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; pub use generator::generators::derive_generators; -pub use poseidon2::{field_from_hex, poseidon2_permutation, Poseidon2Config, POSEIDON2_CONFIG}; +pub use poseidon2::{ + field_from_hex, poseidon2_permutation, poseidon_hash, Poseidon2Config, Poseidon2Sponge, + POSEIDON2_CONFIG, +}; // Temporary hack, this ensure that we always use a bn254 field here // without polluting the feature flags of the `acir_field` crate. diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs index dd3e8b725c2..64823e37029 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs @@ -543,6 +543,75 @@ impl<'a> Poseidon2<'a> { } } +/// Performs a poseidon hash with a sponge construction equivalent to the one in poseidon2.nr +pub fn poseidon_hash(inputs: &[FieldElement]) -> Result { + let two_pow_64 = 18446744073709551616_u128.into(); + let iv = FieldElement::from(inputs.len()) * two_pow_64; + let mut sponge = Poseidon2Sponge::new(iv, 3); + for input in inputs.iter() { + sponge.absorb(*input)?; + } + sponge.squeeze() +} + +pub struct Poseidon2Sponge<'a> { + rate: usize, + poseidon: Poseidon2<'a>, + squeezed: bool, + cache: Vec, + state: Vec, +} + +impl<'a> Poseidon2Sponge<'a> { + pub fn new(iv: FieldElement, rate: usize) -> Poseidon2Sponge<'a> { + let mut result = Poseidon2Sponge { + cache: Vec::with_capacity(rate), + state: vec![FieldElement::zero(); rate + 1], + squeezed: false, + rate, + poseidon: Poseidon2::new(), + }; + result.state[rate] = iv; + result + } + + fn perform_duplex(&mut self) -> Result<(), BlackBoxResolutionError> { + // zero-pad the cache + for _ in self.cache.len()..self.rate { + self.cache.push(FieldElement::zero()); + } + // add the cache into sponge state + for i in 0..self.rate { + self.state[i] += self.cache[i]; + } + self.state = self.poseidon.permutation(&self.state, 4)?; + Ok(()) + } + + pub fn absorb(&mut self, input: FieldElement) -> Result<(), BlackBoxResolutionError> { + assert!(!self.squeezed); + if self.cache.len() == self.rate { + // If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache + self.perform_duplex()?; + self.cache = vec![input]; + } else { + // If we're absorbing, and the cache is not full, add the input into the cache + self.cache.push(input); + } + Ok(()) + } + + pub fn squeeze(&mut self) -> Result { + assert!(!self.squeezed); + // If we're in absorb mode, apply sponge permutation to compress the cache. + self.perform_duplex()?; + self.squeezed = true; + + // Pop one item off the top of the permutation and return it. + Ok(self.state[0]) + } +} + #[cfg(test)] mod test { use acir::AcirField; @@ -562,4 +631,19 @@ mod test { ]; assert_eq!(result, expected_result); } + + #[test] + fn hash_smoke_test() { + let fields = [ + FieldElement::from(1u128), + FieldElement::from(2u128), + FieldElement::from(3u128), + FieldElement::from(4u128), + ]; + let result = super::poseidon_hash(&fields).expect("should hash successfully"); + assert_eq!( + result, + field_from_hex("130bf204a32cac1f0ace56c78b731aa3809f06df2731ebcf6b3464a15788b1b9"), + ); + } } diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index 258379cd7b8..e7bee99b74e 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -17,7 +17,9 @@ acvm.workspace = true noirc_frontend.workspace = true noirc_errors.workspace = true iter-extended.workspace = true +bn254_blackbox_solver.workspace = true convert_case = "0.6.0" im.workspace = true regex = "1.10" -tiny-keccak = { version = "2.0.0", features = ["keccak"] } +itertools = "^0.10" +hex.workspace = true diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index e2de30d6d93..522489daa1b 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -1,5 +1,6 @@ use acvm::acir::AcirField; +use bn254_blackbox_solver::poseidon_hash; use noirc_errors::Location; use noirc_frontend::ast::{Documented, Ident, NoirFunction, UnresolvedTypeData}; use noirc_frontend::{ @@ -9,7 +10,7 @@ use noirc_frontend::{ Type, }; -use tiny_keccak::{Hasher, Keccak}; +use itertools::Itertools; use crate::utils::parse_utils::parse_program; use crate::utils::{ @@ -251,7 +252,7 @@ pub fn generate_contract_interface( module_name, stubs.iter().map(|(src, _)| src.to_owned()).collect::>().join("\n"), if has_storage_layout { storage_layout_getter.clone() } else { "".to_string() }, - if has_storage_layout { format!("#[contract_library_method]\n{}", storage_layout_getter) } else { "".to_string() } + if has_storage_layout { format!("#[contract_library_method]\n{}", storage_layout_getter) } else { "".to_string() } ); let (contract_interface_ast, errors) = parse_program(&contract_interface, empty_spans); @@ -296,14 +297,8 @@ fn compute_fn_signature_hash(fn_name: &str, parameters: &[Type]) -> u32 { fn_name, parameters.iter().map(signature_of_type).collect::>().join(",") ); - let mut keccak = Keccak::v256(); - let mut result = [0u8; 32]; - keccak.update(signature.as_bytes()); - keccak.finalize(&mut result); - // Take the first 4 bytes of the hash and convert them to an integer - // If you change the following value you have to change NUM_BYTES_PER_NOTE_TYPE_ID in l1_note_payload.ts as well - let num_bytes_per_note_type_id = 4; - u32::from_be_bytes(result[0..num_bytes_per_note_type_id].try_into().unwrap()) + + hash_to_selector(&signature) } // Updates the function signatures in the contract interface with the actual ones, replacing the placeholder. @@ -429,3 +424,45 @@ pub fn update_fn_signatures_in_contract_interface( } Ok(()) } + +fn poseidon2_hash_bytes(inputs: Vec) -> FieldElement { + let fields: Vec<_> = inputs + .into_iter() + .chunks(31) + .into_iter() + .map(|bytes_chunk| { + let mut chunk_as_vec: Vec = bytes_chunk.collect(); + chunk_as_vec.extend(std::iter::repeat(0).take(32 - chunk_as_vec.len())); + // Build a little endian field element + chunk_as_vec.reverse(); + FieldElement::from_be_bytes_reduce(&chunk_as_vec) + }) + .collect(); + + poseidon_hash(&fields).expect("Poseidon hash failed") +} + +pub(crate) fn hash_to_selector(inputs: &str) -> u32 { + let hash = poseidon2_hash_bytes(inputs.as_bytes().to_vec()).to_be_bytes(); + // Take the last 4 bytes of the hash and convert them to an integer + // If you change the following value you have to change NUM_BYTES_PER_NOTE_TYPE_ID in l1_note_payload.ts as well + let num_bytes_per_note_type_id = 4; + u32::from_be_bytes(hash[(32 - num_bytes_per_note_type_id)..32].try_into().unwrap()) +} + +#[cfg(test)] +mod test { + use crate::transforms::contract_interface::hash_to_selector; + + #[test] + fn test_selector_is_valid() { + let selector = hash_to_selector("IS_VALID()"); + assert_eq!(hex::encode(&selector.to_be_bytes()), "73cdda47"); + } + + #[test] + fn test_long_selector() { + let selector = hash_to_selector("foo_and_bar_and_baz_and_foo_bar_baz_and_bar_foo"); + assert_eq!(hex::encode(&selector.to_be_bytes()), "7590a997"); + } +} diff --git a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs index d9c4a594fc6..b5d04e87e0a 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -12,8 +12,6 @@ use noirc_frontend::{ use acvm::AcirField; use regex::Regex; -// TODO(#7165): nuke the following dependency from here and Cargo.toml -use tiny_keccak::{Hasher, Keccak}; use crate::utils::parse_utils::parse_program; use crate::{ @@ -28,6 +26,8 @@ use crate::{ }, }; +use super::contract_interface::hash_to_selector; + // Automatic implementation of most of the methods in the NoteInterface trait, guiding the user with meaningful error messages in case some // methods must be implemented manually. pub fn generate_note_interface_impl( @@ -751,14 +751,7 @@ fn generate_note_deserialize_content_source( // Utility function to generate the note type id as a Field fn compute_note_type_id(note_type: &str) -> u32 { // TODO(#4519) Improve automatic note id generation and assignment - let mut keccak = Keccak::v256(); - let mut result = [0u8; 32]; - keccak.update(note_type.as_bytes()); - keccak.finalize(&mut result); - // Take the first 4 bytes of the hash and convert them to an integer - // If you change the following value you have to change NUM_BYTES_PER_NOTE_TYPE_ID in l1_note_payload.ts as well - let num_bytes_per_note_type_id = 4; - u32::from_be_bytes(result[0..num_bytes_per_note_type_id].try_into().unwrap()) + hash_to_selector(note_type) } pub fn inject_note_exports( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 8ca66112766..695f6eb7d16 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -169,6 +169,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "struct_def_name" => struct_def_name(interner, arguments, location), "struct_def_set_fields" => struct_def_set_fields(interner, arguments, location), "to_le_radix" => to_le_radix(arguments, return_type, location), + "to_be_radix" => to_be_radix(arguments, return_type, location), "trait_constraint_eq" => trait_constraint_eq(arguments, location), "trait_constraint_hash" => trait_constraint_hash(arguments, location), "trait_def_as_trait_constraint" => { @@ -770,7 +771,7 @@ fn to_le_radix( }; // Decompose the integer into its radix digits in little endian form. - let decomposed_integer = compute_to_radix(value, radix); + let decomposed_integer = compute_to_radix_le(value, radix); let decomposed_integer = vecmap(0..limb_count as usize, |i| match decomposed_integer.get(i) { Some(digit) => Value::U8(*digit), None => Value::U8(0), @@ -781,7 +782,41 @@ fn to_le_radix( )) } -fn compute_to_radix(field: FieldElement, radix: u32) -> Vec { +fn to_be_radix( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + let (value, radix) = check_two_arguments(arguments, location)?; + + let value = get_field(value)?; + let radix = get_u32(radix)?; + let limb_count = if let Type::Array(length, _) = return_type { + if let Type::Constant(limb_count) = *length { + limb_count + } else { + return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); + } + } else { + return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); + }; + + // Decompose the integer into its radix digits in little endian form. + let decomposed_integer = compute_to_radix_le(value, radix); + + // Iterate in reverse to get the big endian result. + let decomposed_integer = + vecmap((0..limb_count as usize).rev(), |i| match decomposed_integer.get(i) { + Some(digit) => Value::U8(*digit), + None => Value::U8(0), + }); + Ok(Value::Array( + decomposed_integer.into(), + Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), + )) +} + +fn compute_to_radix_le(field: FieldElement, radix: u32) -> Vec { let bit_size = u32::BITS - (radix - 1).leading_zeros(); let radix_big = BigUint::from(radix); assert_eq!(BigUint::from(2u128).pow(bit_size), radix_big, "ICE: Radix must be a power of 2"); diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 96be63148a3..2f5baddd9ec 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -119,14 +119,14 @@ export const L2_GAS_PER_LOG_BYTE = 4; export const L2_GAS_PER_NOTE_HASH = 32; export const L2_GAS_PER_NULLIFIER = 64; export const CANONICAL_KEY_REGISTRY_ADDRESS = - 9694109890306420370616891858093188542026876097103155811681068343994212062621n; + 6823425185167517386380694778823032861295161686691976789058601691508103815523n; export const CANONICAL_AUTH_REGISTRY_ADDRESS = - 16522644890256297179255458951626875692461008240031142745359776058397274208468n; -export const DEPLOYER_CONTRACT_ADDRESS = 19310994760783330368337163480198602393920956587162708699802190083077641908361n; + 19361441716519463065948254497947932755739298943049449145365332870925554042208n; +export const DEPLOYER_CONTRACT_ADDRESS = 17119407406465801909352274670277571579675739451008438338071219340964365249977n; export const REGISTERER_CONTRACT_ADDRESS = - 2631409926445785927331173506476539962589925110142857699603561302478860342858n; -export const FEE_JUICE_ADDRESS = 10248142274714515101077825679585135641434041564851038865006795089686437446849n; -export const ROUTER_ADDRESS = 7268799613082469933251235702514160327341161584122631177360064643484764773587n; + 12405643717676802437578418009978929188439257864607100766293128479227092050857n; +export const FEE_JUICE_ADDRESS = 12096583827711775893711303288210371301779120762215263550909768879597884314839n; +export const ROUTER_ADDRESS = 13369993014609648298719593339393818582619449364029983937306132176549332172208n; export const AZTEC_ADDRESS_LENGTH = 1; export const GAS_FEES_LENGTH = 2; export const GAS_LENGTH = 2; @@ -175,16 +175,16 @@ export const PRIVATE_CALL_STACK_ITEM_LENGTH = 649; export const PUBLIC_CONTEXT_INPUTS_LENGTH = 42; export const AGGREGATION_OBJECT_LENGTH = 16; export const SCOPED_READ_REQUEST_LEN = 3; -export const PUBLIC_DATA_READ_LENGTH = 2; +export const PUBLIC_DATA_READ_LENGTH = 3; export const PRIVATE_VALIDATION_REQUESTS_LENGTH = 772; -export const PUBLIC_VALIDATION_REQUESTS_LENGTH = 770; +export const PUBLIC_VALIDATION_REQUESTS_LENGTH = 834; export const PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; export const COMBINED_ACCUMULATED_DATA_LENGTH = 610; export const COMBINED_CONSTANT_DATA_LENGTH = 43; export const PRIVATE_ACCUMULATED_DATA_LENGTH = 1336; export const PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2167; export const PUBLIC_ACCUMULATED_DATA_LENGTH = 1311; -export const PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 3885; +export const PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 3949; export const KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 663; export const CONSTANT_ROLLUP_DATA_LENGTH = 12; export const BASE_OR_MERGE_PUBLIC_INPUTS_LENGTH = 29; diff --git a/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap index b0374a11e52..a8a7d2f571b 100644 --- a/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap +++ b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap @@ -2,7 +2,7 @@ exports[`ContractAddress computeContractAddressFromInstance 1`] = `"0x0ea56faa48431d99cc2d073463d1b718c11174bb551a3d1d6f296b0096089dbb"`; -exports[`ContractAddress computeInitializationHash 1`] = `Fr<0x2f11768c825fac132c632372d7c6d657fd471a9f9db05a153c6927a24863ec53>`; +exports[`ContractAddress computeInitializationHash 1`] = `Fr<0x0a79e35b159f2755e79e7ac21aed31964a6446d279670b499595da24c66144ff>`; exports[`ContractAddress computePartialAddress 1`] = `Fr<0x2521255ebd14e8e3e7cd1e8a27d26a902ee9e74905b711950d580e826ba9010d>`; diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index cb4655f5561..f19e5b05136 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -12,6 +12,7 @@ import { type Wallet, deriveKeys, retryUntil, + sleep, } from '@aztec/aztec.js'; import { times } from '@aztec/foundation/collection'; import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; @@ -333,6 +334,7 @@ describe('e2e_block_building', () => { minTxsPerBlock: 0, skipProtocolContracts: true, })); + await sleep(1000); const account = getSchnorrAccount(pxe, Fr.random(), Fq.random(), Fr.random()); await account.waitSetup(); diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index 576a8f2da17..416f2af3ce2 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -65,7 +65,7 @@ describe('Logs', () => { expect(decryptedLog0?.payload.contractAddress).toStrictEqual(testLogContract.address); expect(decryptedLog0?.payload.randomness).toStrictEqual(randomness[0]); expect(decryptedLog0?.payload.eventTypeId).toStrictEqual( - EventSelector.fromField(new Fr(0x00000000000000000000000000000000000000000000000000000000aa533f60)), + EventSelector.fromField(new Fr(0x00000000000000000000000000000000000000000000000000000000d45c041b)), ); // We decode our event into the event type @@ -89,7 +89,7 @@ describe('Logs', () => { expect(decryptedLog1?.payload.contractAddress).toStrictEqual(testLogContract.address); expect(decryptedLog1?.payload.randomness).toStrictEqual(randomness[1]); expect(decryptedLog1?.payload.eventTypeId).toStrictEqual( - EventSelector.fromField(new Fr(0x00000000000000000000000000000000000000000000000000000000d1be0447)), + EventSelector.fromField(new Fr(0x00000000000000000000000000000000000000000000000000000000031b1167)), ); // We check our second event, which is a different type diff --git a/yarn-project/foundation/src/abi/__snapshots__/function_selector.test.ts.snap b/yarn-project/foundation/src/abi/__snapshots__/function_selector.test.ts.snap index 0336e9c4e93..c59cdc3f81f 100644 --- a/yarn-project/foundation/src/abi/__snapshots__/function_selector.test.ts.snap +++ b/yarn-project/foundation/src/abi/__snapshots__/function_selector.test.ts.snap @@ -1,3 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`FunctionSelector computes a function selector from signature 1`] = `Selector<0xa9059cbb>`; +exports[`FunctionSelector computes a function selector from a long string 1`] = `"7590a997"`; + +exports[`FunctionSelector computes a function selector from signature 1`] = `"73cdda47"`; diff --git a/yarn-project/foundation/src/abi/event_selector.ts b/yarn-project/foundation/src/abi/event_selector.ts index d7cbe951a46..f7c8ae06d99 100644 --- a/yarn-project/foundation/src/abi/event_selector.ts +++ b/yarn-project/foundation/src/abi/event_selector.ts @@ -1,5 +1,5 @@ import { fromHex, toBigIntBE } from '../bigint-buffer/index.js'; -import { keccak256, randomBytes } from '../crypto/index.js'; +import { poseidon2HashBytes, randomBytes } from '../crypto/index.js'; import { type Fr } from '../fields/fields.js'; import { BufferReader } from '../serialize/buffer_reader.js'; import { Selector } from './selector.js'; @@ -44,7 +44,10 @@ export class EventSelector extends Selector { if (/\s/.test(signature)) { throw new Error('Signature cannot contain whitespace'); } - return EventSelector.fromBuffer(keccak256(Buffer.from(signature)).subarray(0, Selector.SIZE)); + const hash = poseidon2HashBytes(Buffer.from(signature)); + // We take the last Selector.SIZE big endian bytes + const bytes = hash.toBuffer().slice(-Selector.SIZE); + return EventSelector.fromBuffer(bytes); } /** diff --git a/yarn-project/foundation/src/abi/function_selector.test.ts b/yarn-project/foundation/src/abi/function_selector.test.ts index 6cb6155e922..a3f6d055354 100644 --- a/yarn-project/foundation/src/abi/function_selector.test.ts +++ b/yarn-project/foundation/src/abi/function_selector.test.ts @@ -23,7 +23,12 @@ describe('FunctionSelector', () => { }); it('computes a function selector from signature', () => { - const res = FunctionSelector.fromSignature('transfer(address,uint256)'); - expect(res).toMatchSnapshot(); + const res = FunctionSelector.fromSignature('IS_VALID()'); + expect(res.toBuffer().toString('hex')).toMatchSnapshot(); + }); + + it('computes a function selector from a long string', () => { + const res = FunctionSelector.fromSignature('foo_and_bar_and_baz_and_foo_bar_baz_and_bar_foo'); + expect(res.toBuffer().toString('hex')).toMatchSnapshot(); }); }); diff --git a/yarn-project/foundation/src/abi/function_selector.ts b/yarn-project/foundation/src/abi/function_selector.ts index c37df24d839..7641234f9ab 100644 --- a/yarn-project/foundation/src/abi/function_selector.ts +++ b/yarn-project/foundation/src/abi/function_selector.ts @@ -1,5 +1,5 @@ import { fromHex, toBigIntBE } from '../bigint-buffer/index.js'; -import { keccak256, randomBytes } from '../crypto/index.js'; +import { poseidon2HashBytes, randomBytes } from '../crypto/index.js'; import { type Fr } from '../fields/fields.js'; import { BufferReader } from '../serialize/buffer_reader.js'; import { FieldReader } from '../serialize/field_reader.js'; @@ -73,7 +73,10 @@ export class FunctionSelector extends Selector { if (/\s/.test(signature)) { throw new Error('Signature cannot contain whitespace'); } - return FunctionSelector.fromBuffer(keccak256(Buffer.from(signature)).subarray(0, Selector.SIZE)); + const hash = poseidon2HashBytes(Buffer.from(signature)); + // We take the last Selector.SIZE big endian bytes + const bytes = hash.toBuffer().slice(-Selector.SIZE); + return FunctionSelector.fromBuffer(bytes); } /** diff --git a/yarn-project/foundation/src/crypto/poseidon/index.ts b/yarn-project/foundation/src/crypto/poseidon/index.ts index ad94ab3da96..1e1274ec781 100644 --- a/yarn-project/foundation/src/crypto/poseidon/index.ts +++ b/yarn-project/foundation/src/crypto/poseidon/index.ts @@ -57,3 +57,25 @@ export function poseidon2Permutation(input: Fieldable[]): Fr[] { // assert(res.length === 4, 'Output state must be of size 4'); return res.map(o => Fr.fromBuffer(Buffer.from(o.toBuffer()))); } + +export function poseidon2HashBytes(input: Buffer): Fr { + const inputFields = []; + for (let i = 0; i < input.length; i += 31) { + const fieldBytes = Buffer.alloc(32, 0); + input.slice(i, i + 31).copy(fieldBytes); + + // Noir builds the bytes as little-endian, so we need to reverse them. + fieldBytes.reverse(); + inputFields.push(Fr.fromBuffer(fieldBytes)); + } + + return Fr.fromBuffer( + Buffer.from( + BarretenbergSync.getSingleton() + .poseidon2Hash( + inputFields.map(i => new FrBarretenberg(i.toBuffer())), // TODO(#4189): remove this stupid conversion + ) + .toBuffer(), + ), + ); +}