diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md index 8b01fa450622..053d3ccdb9d1 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md @@ -219,7 +219,7 @@ struct CustomNote { } ``` -### After expansaion +### After expansion ```rust impl CustomNote { @@ -255,13 +255,12 @@ impl CustomNote { ) } - fn to_be_bytes(self, storage_slot: Field) -> [u8; 128] { - assert(128 == 2 * 32 + 64, "Note byte length must be equal to (serialized_length * 32) + 64 bytes"); + fn to_be_bytes(self, storage_slot: Point) -> [u8; CUSTOM_NOTE_BYTES_LEN] { let serialized_note = self.serialize_content(); - let mut buffer: [u8; 128] = [0; 128]; + let mut buffer: [u8; CUSTOM_NOTE_BYTES_LEN] = [0; CUSTOM_NOTE_BYTES_LEN]; - let storage_slot_bytes = storage_slot.to_be_bytes(32); + let storage_slot_bytes = point_to_bytes(storage_slot); let note_type_id_bytes = CustomNote::get_note_type_id().to_be_bytes(32); for i in 0..32 { diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index afd0a4490fa7..31a68bb3856c 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -95,7 +95,6 @@ library Constants { uint256 internal constant ARGS_HASH_CHUNK_LENGTH = 16; uint256 internal constant ARGS_HASH_CHUNK_COUNT = 16; uint256 internal constant MAX_ARGS_LENGTH = 256; - uint256 internal constant INITIALIZATION_SLOT_SEPARATOR = 1000000000; uint256 internal constant INITIAL_L2_BLOCK_NUM = 1; uint256 internal constant BLOB_SIZE_IN_BYTES = 126976; uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 20000; @@ -124,15 +123,15 @@ 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 = - 2153455745675440165069577621832684870696142028027528497509357256345838682961; + 21002604897410093708477100201638914385100863925743623823186725421598769862581; uint256 internal constant CANONICAL_AUTH_REGISTRY_ADDRESS = - 18091885756106795278141309801070173692350235742979924147720536894670507925831; + 18844197343959337477226781811009542647774288930267535903153059057819715808176; uint256 internal constant DEPLOYER_CONTRACT_ADDRESS = - 19511485909966796736993840362353440247573331327062358513665772226446629198132; + 3077749756291741958160703262435693846649629333047756518291243914166007507388; uint256 internal constant REGISTERER_CONTRACT_ADDRESS = - 13402924717071282069537366635406026232165444473509746327951838324587448220160; + 9106537597827539209583757446796074130561720707558015678434966723134574857086; uint256 internal constant GAS_TOKEN_ADDRESS = - 3159976153131520272419617514531889581796079438158800470341967144801191524489; + 18052122229623850597530611627473354790534583502914175510126040829737657095769; uint256 internal constant AZTEC_ADDRESS_LENGTH = 1; uint256 internal constant GAS_FEES_LENGTH = 2; uint256 internal constant GAS_LENGTH = 2; diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index 32afffb88ae8..89b82c852db3 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -165,20 +165,27 @@ impl PublicContext { da_gas_left() } - fn raw_storage_read(_self: Self, storage_slot: Field) -> [Field; N] { - storage_read(storage_slot) + fn raw_storage_read(_self: Self, contract_storage_index: Field) -> [Field; N] { + storage_read(contract_storage_index) } - fn storage_read(self, storage_slot: Field) -> T where T: Deserialize { - T::deserialize(self.raw_storage_read(storage_slot)) + fn storage_read( + self, + contract_storage_index: Field + ) -> T where T: Deserialize { + T::deserialize(self.raw_storage_read(contract_storage_index)) } - fn raw_storage_write(_self: Self, storage_slot: Field, values: [Field; N]) { - storage_write(storage_slot, values); + fn raw_storage_write(_self: Self, contract_storage_index: Field, values: [Field; N]) { + storage_write(contract_storage_index, values); } - fn storage_write(self, storage_slot: Field, value: T) where T: Serialize { - self.raw_storage_write(storage_slot, value.serialize()); + fn storage_write( + self, + contract_storage_index: Field, + value: T + ) where T: Serialize { + self.raw_storage_write(contract_storage_index, value.serialize()); } } @@ -274,12 +281,12 @@ unconstrained fn call_static( call_static_opcode(gas, address, args, function_selector) } -unconstrained fn storage_read(storage_slot: Field) -> [Field; N] { - storage_read_opcode(storage_slot, N as Field) +unconstrained fn storage_read(contract_storage_index: Field) -> [Field; N] { + storage_read_opcode(contract_storage_index, N as Field) } -unconstrained fn storage_write(storage_slot: Field, values: [Field; N]) { - storage_write_opcode(storage_slot, values); +unconstrained fn storage_write(contract_storage_index: Field, values: [Field; N]) { + storage_write_opcode(contract_storage_index, values); } impl Empty for PublicContext { @@ -373,10 +380,16 @@ unconstrained fn call_static_opcode( // ^ return data ^ success #[oracle(avmOpcodeStorageRead)] -unconstrained fn storage_read_opcode(storage_slot: Field, length: Field) -> [Field; N] {} +unconstrained fn storage_read_opcode( + contract_storage_index: Field, + length: Field +) -> [Field; N] {} #[oracle(avmOpcodeStorageWrite)] -unconstrained fn storage_write_opcode(storage_slot: Field, values: [Field; N]) {} +unconstrained fn storage_write_opcode( + contract_storage_index: Field, + values: [Field; N] +) {} struct FunctionReturns { values: [Field; N] diff --git a/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr b/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr index 6df0103556a7..02b585625d1d 100644 --- a/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr @@ -47,14 +47,21 @@ impl UnconstrainedContext { self.chain_id } - unconstrained fn raw_storage_read(self: Self, storage_slot: Field) -> [Field; N] { - storage_read(self.this_address(), storage_slot, self.block_number()) + unconstrained fn raw_storage_read( + self: Self, + contract_storage_index: Field + ) -> [Field; N] { + storage_read( + self.this_address(), + contract_storage_index, + self.block_number() + ) } unconstrained fn storage_read( self, - storage_slot: Field + contract_storage_index: Field ) -> T where T: Deserialize { - T::deserialize(self.raw_storage_read(storage_slot)) + T::deserialize(self.raw_storage_read(contract_storage_index)) } } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr index 3b9122568b99..7af387f456af 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr @@ -9,7 +9,7 @@ use dep::protocol_types::{ unconstrained fn compute_unconstrained( contract_address: AztecAddress, - storage_slot: Field, + storage_slot: Point, ovsk_app: Field, ovpk: Point, ivpk: Point, @@ -29,7 +29,7 @@ unconstrained fn compute_unconstrained( contract_address: AztecAddress, - storage_slot: Field, + storage_slot: Point, ovsk_app: Field, ovpk: Point, ivpk: Point, @@ -55,7 +55,7 @@ fn emit_with_keys( ovpk: Point, ivpk: Point, recipient: AztecAddress, - inner_compute: fn(AztecAddress, Field, Field, Point, Point, AztecAddress, Note) -> ([u8; M], Field) + inner_compute: fn(AztecAddress, Point, Field, Point, Point, AztecAddress, Note) -> ([u8; M], Field) ) where Note: NoteInterface, [Field; N]: LensForEncryptedLog { let note_header = note.get_header(); let note_hash_counter = note_header.note_hash_counter; diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr index 386e2338a6fa..57bdbb9279c6 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr @@ -45,7 +45,7 @@ fn test_encrypted_log_header() { let ciphertext = header.compute_ciphertext(secret, point); let expected_header_ciphertext = [ - 166, 212, 106, 246, 139, 59, 228, 9, 133, 152, 127, 172, 141, 166, 237, 199, 55, 203, 226, 19, 114, 103, 58, 237, 108, 231, 35, 198, 54, 61, 190, 255, 241, 225, 151, 180, 6, 163, 124, 27, 151, 78, 237, 65, 120, 106, 255, 236 + 226, 240, 253, 6, 28, 52, 19, 131, 33, 132, 178, 212, 245, 62, 14, 190, 194, 44, 7, 131, 160, 83, 64, 181, 98, 38, 153, 214, 62, 171, 253, 161, 111, 191, 28, 247, 216, 26, 222, 171, 176, 218, 48, 209, 73, 89, 200, 209 ]; assert_eq(ciphertext, expected_header_ciphertext); 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 6b25525a7ae0..2d40a5182dce 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 @@ -10,7 +10,7 @@ struct EncryptedLogIncomingBody { } impl EncryptedLogIncomingBody { - pub fn from_note(note: T, storage_slot: Field) -> Self where T: NoteInterface { + pub fn from_note(note: T, storage_slot: Point) -> Self where T: NoteInterface { let mut plaintext = note.to_be_bytes(storage_slot); EncryptedLogIncomingBody { plaintext } } @@ -37,16 +37,16 @@ impl EncryptedLogIncomingBody { } mod test { - use crate::encrypted_logs::incoming_body::EncryptedLogIncomingBody; use dep::protocol_types::{ address::AztecAddress, traits::Empty, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - scalar::Scalar, point::Point, traits::Serialize, abis::event_selector::EventSelector + scalar::Scalar, point::Point, traits::Serialize, generators::Ga1, + abis::event_selector::EventSelector }; use crate::{ note::{note_header::NoteHeader, note_interface::NoteInterface}, - event::event_interface::EventInterface, oracle::unsafe_rand::unsafe_rand, - context::PrivateContext + encrypted_logs::incoming_body::EncryptedLogIncomingBody, event::event_interface::EventInterface, + oracle::unsafe_rand::unsafe_rand, context::PrivateContext, utils::point::point_to_bytes }; struct AddressNote { @@ -60,7 +60,9 @@ mod test { global ADDRESS_NOTE_BYTES_LEN = 32 * 3 + 64; impl NoteInterface for AddressNote { - fn compute_note_content_hash(_self: Self) -> Field {1} + fn compute_note_content_hash(self) -> Point { + Ga1 + } fn get_note_type_id() -> Field { 1 @@ -82,12 +84,12 @@ mod test { AddressNote { address: AztecAddress::from_field(fields[0]), owner: AztecAddress::from_field(fields[1]), randomness: fields[2], header: NoteHeader::empty() } } - fn to_be_bytes(self, storage_slot: Field) -> [u8; ADDRESS_NOTE_BYTES_LEN] { + fn to_be_bytes(self, storage_slot: Point) -> [u8; ADDRESS_NOTE_BYTES_LEN] { let serialized_note = self.serialize_content(); let mut buffer: [u8; ADDRESS_NOTE_BYTES_LEN] = [0; ADDRESS_NOTE_BYTES_LEN]; - let storage_slot_bytes = storage_slot.to_be_bytes(32); + let storage_slot_bytes = point_to_bytes(storage_slot); let note_type_id_bytes = AddressNote::get_note_type_id().to_be_bytes(32); for i in 0..32 { @@ -112,14 +114,19 @@ mod test { } #[test] - fn test_encrypted_note_log_incoming_body() { + fn test_encrypted_note_log_incoming_body_matches_typescript() { + // All the values in this test were copied over from `encrypted_note_log_incoming_body.test.ts` let note = AddressNote::new( AztecAddress::from_field(0x1), AztecAddress::from_field(0x2), 3 ); - let storage_slot = 2; + let storage_slot = Point { + x: 0x1d83b1af3f569775af9e3fdae19b84590245098f46d4a407b5963f313000ce37, + y: 0x1537c632779932ccbc415d91dd70801f88ad410fff48179886d3dce035582d76, + is_infinite: false + }; let eph_sk = Scalar { lo: 0x00000000000000000000000000000000649e7ca01d9de27b21624098b897babd, @@ -131,18 +138,25 @@ mod test { is_infinite: false }; + /// 1. `EncryptedLogIncomingBody::from_note` calls `note.to_be_bytes(storage_slot)` function which serializes + /// the note to bytes - note that in the case of `AddressNote` the `to_be_bytes` function was automatically + /// implemented by Aztec macros. let body = EncryptedLogIncomingBody::from_note(note, storage_slot); + /// 2. `body.compute_ciphertext(...)` function then derives symmetric key from `eph_sk` and `ivpk` and encrypts + // the note plaintext using AES-128. let ciphertext = body.compute_ciphertext(eph_sk, ivpk); - let expected_note_body_ciphertext = [ - 166, 212, 106, 246, 139, 59, 228, 9, 133, 152, 127, 172, 141, 166, 237, 199, 195, 85, 255, 81, 66, 72, 192, 192, 96, 10, 54, 139, 136, 153, 252, 114, 248, 128, 253, 66, 249, 16, 71, 45, 2, 213, 250, 193, 241, 75, 90, 70, 39, 26, 104, 139, 20, 45, 1, 1, 166, 72, 133, 55, 247, 142, 150, 215, 217, 224, 84, 23, 245, 71, 207, 166, 136, 34, 221, 76, 90, 166, 44, 217, 246, 98, 157, 34, 198, 164, 99, 117, 15, 185, 145, 231, 189, 140, 201, 241, 135, 94, 71, 131, 156, 86, 144, 131, 248, 242, 83, 101, 18, 189, 1, 94, 25, 238, 76, 106, 85, 205, 4, 70, 21, 9, 64, 63, 27, 164, 73, 181, 75, 199, 86, 255, 105, 239, 216, 34, 217, 184, 154, 76, 67, 1, 210, 251, 23, 185, 114, 146, 195, 28, 76, 219, 150, 175, 37, 76, 144, 227, 99, 243, 123, 161, 66, 171, 148, 181, 162, 2, 196, 53, 207, 154, 114, 166, 155, 166 + // The following value was generated by `encrypted_note_log_incoming_body.test.ts`. + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let note_body_ciphertext_from_typescript = [ + 75, 3, 86, 165, 50, 163, 229, 200, 67, 137, 31, 92, 122, 27, 14, 158, 201, 248, 29, 236, 102, 216, 246, 64, 166, 1, 166, 221, 92, 19, 78, 19, 213, 197, 44, 130, 105, 145, 239, 139, 176, 255, 148, 41, 72, 212, 75, 176, 216, 99, 170, 9, 151, 25, 135, 140, 53, 123, 253, 52, 119, 14, 65, 131, 127, 177, 227, 219, 157, 38, 90, 161, 32, 7, 27, 138, 152, 196, 60, 240, 106, 73, 121, 227, 190, 14, 142, 61, 119, 47, 201, 29, 250, 68, 53, 62, 200, 33, 207, 190, 156, 84, 9, 115, 122, 14, 147, 171, 83, 111, 2, 140, 9, 33, 33, 30, 65, 242, 133, 134, 59, 254, 202, 2, 237, 246, 68, 153, 62, 237, 123, 90, 169, 84, 62, 55, 214, 102, 26, 79, 209, 175, 255, 223, 110, 6, 119, 61, 142, 36, 81, 155, 0, 178, 60, 85, 120, 152, 177, 115, 14, 62, 105, 42, 178, 231, 173, 245, 88, 245, 89, 122, 32, 49, 205, 151 ]; - assert_eq(expected_note_body_ciphertext.len(), ciphertext.len()); + assert_eq(note_body_ciphertext_from_typescript.len(), ciphertext.len()); - for i in 0..expected_note_body_ciphertext.len() { - assert_eq(ciphertext[i], expected_note_body_ciphertext[i]); + for i in 0..note_body_ciphertext_from_typescript.len() { + assert_eq(ciphertext[i], note_body_ciphertext_from_typescript[i]); } } @@ -237,14 +251,16 @@ mod test { let ciphertext = body.compute_ciphertext(eph_sk, ivpk); - let expected_event_body_ciphertext = [ - 166, 212, 106, 246, 139, 59, 228, 9, 133, 152, 127, 172, 141, 166, 237, 199, 195, 85, 255, 81, 66, 72, 192, 192, 96, 10, 54, 139, 136, 153, 252, 114, 248, 128, 253, 66, 249, 16, 71, 45, 2, 213, 250, 193, 241, 75, 90, 70, 19, 153, 62, 117, 71, 55, 48, 114, 160, 232, 97, 118, 93, 53, 145, 92, 0, 225, 51, 81, 156, 69, 72, 224, 10, 89, 32, 121, 167, 197, 84, 245, 188, 235, 143, 202, 179, 197, 164, 121, 11, 105, 116, 239, 46, 222, 50, 138, 112, 237, 97, 8, 176, 199, 1, 151, 89, 218, 60, 45, 91, 85, 16, 38, 195, 127, 157, 182, 0, 10, 232, 184, 148, 76, 244, 63, 40, 222, 219, 139, 236, 169, 213, 17, 32, 210, 50, 6, 5, 83, 80, 1, 111, 246, 197, 83, 166, 71, 31, 246, 234, 75, 12, 151, 227, 247, 143, 229, 95, 219, 159, 75, 174, 232, 64, 7, 102, 76, 207, 45, 143, 208, 101, 113, 175, 37, 83, 166 + // 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 ]; - assert_eq(expected_event_body_ciphertext.len(), ciphertext.len()); + assert_eq(event_body_ciphertext_from_typescript.len(), ciphertext.len()); - for i in 0..expected_event_body_ciphertext.len() { - assert_eq(ciphertext[i], expected_event_body_ciphertext[i]); + for i in 0..event_body_ciphertext_from_typescript.len() { + assert_eq(ciphertext[i], event_body_ciphertext_from_typescript[i]); } } } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr index 59b6268a816c..06de95353a4d 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr @@ -17,6 +17,8 @@ impl EncryptedLogOutgoingBody { Self { eph_sk, recipient, recipient_ivpk } } + /// Encrypts ephemeral secret key and recipient's ivpk --> with this information the recipient of outgoing will + /// be able to derive the key with which the incoming log can be decrypted. pub fn compute_ciphertext(self, ovsk_app: Scalar, eph_pk: Point) -> [u8; 144] { // Again, we could compute `eph_pk` here, but we keep the interface more similar // and also make it easier to optimise it later as we just pass it along @@ -68,7 +70,7 @@ mod test { use crate::context::PrivateContext; #[test] - fn test_encrypted_log_outgoing_body() { + fn test_encrypted_log_outgoing_body_matches_typescript() { let eph_sk = Scalar { lo: 0x00000000000000000000000000000000d0d302ee245dfaf2807e604eec4715fe, hi: 0x000000000000000000000000000000000f096b423017226a18461115fa8d34bb @@ -91,13 +93,15 @@ mod test { let ciphertext = body.compute_ciphertext(sender_ovsk_app, eph_pk); - let expected_outgoing_body_ciphertext = [ - 127, 84, 96, 176, 101, 107, 236, 57, 68, 8, 53, 202, 138, 74, 186, 54, 74, 193, 245, 7, 109, 59, 218, 33, 1, 31, 205, 225, 241, 209, 64, 222, 94, 245, 4, 150, 47, 241, 187, 64, 152, 20, 102, 158, 200, 217, 213, 82, 1, 240, 170, 185, 51, 80, 27, 109, 63, 231, 235, 120, 174, 44, 133, 248, 10, 97, 60, 40, 222, 190, 147, 76, 187, 48, 91, 206, 48, 106, 56, 118, 38, 127, 82, 4, 182, 188, 44, 224, 31, 129, 47, 107, 134, 252, 20, 25, 249, 193, 215, 137, 195, 43, 98, 42, 54, 96, 254, 89, 134, 31, 103, 142, 16, 43, 92, 211, 145, 113, 217, 253, 161, 240, 121, 205, 146, 200, 168, 160, 221, 32, 229, 116, 26, 216, 86, 189, 78, 120, 10, 224, 85, 52, 40, 244 + // The following value was generated by `encrypted_log_outgoing_body.test.ts` + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let outgoing_body_ciphertext_from_typescript = [ + 126, 10, 214, 39, 130, 143, 96, 143, 79, 143, 22, 36, 55, 41, 234, 255, 226, 26, 138, 236, 91, 188, 204, 216, 172, 133, 134, 69, 161, 237, 134, 5, 75, 192, 10, 6, 229, 54, 194, 56, 103, 243, 57, 248, 147, 237, 4, 3, 39, 28, 226, 30, 237, 228, 212, 115, 246, 244, 105, 39, 129, 119, 126, 207, 176, 14, 75, 134, 241, 23, 2, 187, 239, 86, 47, 56, 239, 20, 92, 176, 70, 12, 219, 226, 150, 70, 192, 43, 125, 53, 230, 153, 135, 228, 210, 197, 227, 106, 242, 138, 119, 83, 182, 150, 233, 111, 9, 104, 128, 222, 85, 136, 205, 244, 77, 230, 210, 217, 223, 106, 220, 4, 115, 33, 157, 212, 217, 133, 87, 179, 67, 158, 81, 85, 226, 105, 22, 8, 154, 130, 193, 214, 144, 212 ]; - for i in 0..expected_outgoing_body_ciphertext.len() { - assert_eq(ciphertext[i], expected_outgoing_body_ciphertext[i]); + for i in 0..outgoing_body_ciphertext_from_typescript.len() { + assert_eq(ciphertext[i], outgoing_body_ciphertext_from_typescript[i]); } - assert_eq(expected_outgoing_body_ciphertext.len(), ciphertext.len()); + assert_eq(outgoing_body_ciphertext_from_typescript.len(), ciphertext.len()); } } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr index 4c7601db80c7..89295f22b9db 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -71,7 +71,7 @@ pub fn compute_encrypted_event_log pub fn compute_encrypted_note_log( contract_address: AztecAddress, - storage_slot: Field, + storage_slot: Point, ovsk_app: Field, ovpk: Point, ivpk: Point, @@ -124,6 +124,9 @@ pub fn compute_encrypted_note_log( /// Converts a base field elememt to scalar field element. /// This is fine because modulus of the base field is smaller than the modulus of the scalar field. +/// TODO(#7551): Replace the contents of this function with decompose and move the function to scalar.nr +/// - in plenty of the places we then convert the outputs of decompose(...) to Scalar struct so having +// this as a utility function would be useful. fn fr_to_fq(r: Field) -> Scalar { let r_bytes = r.to_be_bytes(32); @@ -147,9 +150,14 @@ mod test { use std::test::OracleMock; #[test] - fn test_compute_encrypted_note_log() { + fn test_encrypted_note_log_matches_typescript() { + // All the values in this test were copied over from `tagged_log.test.ts` let contract_address = AztecAddress::from_field(0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04); - let storage_slot = 0x0fe46be583b71f4ab5b70c2657ff1d05cccf1d292a9369628d1a194f944e6599; + let storage_slot = Point { + x: 0x1d83b1af3f569775af9e3fdae19b84590245098f46d4a407b5963f313000ce37, + y: 0x1537c632779932ccbc415d91dd70801f88ad410fff48179886d3dce035582d76, + is_infinite: false + }; let ovsk_app = 0x1b99ba138fa7ef8a2f122a98dd80c8ee70d447218dd780f45e165ac17ca38a5e; let ovpk_m = Point { x: 0x1961448682803198631f299340e4206bb12809d4bebbf012b30f59af73ba1a15, @@ -180,12 +188,14 @@ mod test { note ); - let expected_encrypted_note_log = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 159, 53, 114, 117, 237, 57, 131, 19, 111, 150, 50, 83, 173, 155, 234, 225, 71, 187, 141, 79, 245, 43, 111, 83, 219, 149, 124, 68, 12, 244, 253, 216, 0, 62, 108, 232, 118, 80, 87, 140, 215, 185, 111, 48, 128, 236, 110, 92, 46, 205, 7, 226, 131, 66, 205, 0, 103, 83, 217, 90, 60, 138, 6, 172, 246, 129, 92, 172, 69, 73, 77, 65, 147, 18, 231, 20, 35, 217, 180, 253, 72, 242, 32, 57, 45, 11, 2, 235, 24, 96, 244, 224, 33, 61, 151, 225, 136, 173, 178, 40, 2, 125, 229, 20, 220, 82, 28, 191, 147, 133, 137, 1, 45, 243, 229, 140, 115, 165, 150, 154, 96, 22, 120, 223, 237, 213, 182, 252, 192, 8, 132, 43, 21, 56, 243, 116, 144, 182, 75, 16, 30, 222, 222, 60, 205, 147, 214, 53, 41, 62, 53, 16, 147, 117, 72, 169, 220, 125, 208, 210, 45, 65, 233, 40, 87, 88, 140, 237, 200, 161, 9, 86, 82, 128, 191, 51, 4, 195, 243, 100, 102, 240, 54, 129, 176, 116, 139, 73, 27, 98, 222, 1, 243, 199, 72, 238, 213, 66, 91, 159, 183, 143, 36, 103, 94, 5, 62, 50, 13, 217, 161, 79, 30, 231, 41, 228, 109, 139, 243, 119, 166, 54, 37, 250, 193, 6, 67, 29, 148, 185, 153, 58, 64, 210, 164, 219, 165, 80, 35, 75, 109, 177, 14, 168, 136, 105, 21, 235, 62, 159, 71, 61, 245, 193, 234, 169, 100, 165, 8, 222, 157, 239, 41, 221, 223, 67, 80, 61, 252, 54, 27, 100, 1, 104, 2, 121, 62, 41, 23, 132, 15, 124, 120, 21, 198, 113, 151, 172, 42, 161, 64, 240, 166, 205, 80, 169, 58, 191, 111, 130, 55, 58, 141, 26, 97, 118, 114, 216, 69, 207, 212, 227, 250, 199, 21, 72, 144, 85, 43, 76, 213, 28, 132, 134, 16, 221, 105, 112, 82, 238, 114, 61, 36, 144, 179, 178, 68, 198, 162, 212, 85, 100, 116, 186, 131, 232, 33, 229, 101, 251, 5, 251 + // The following value was generated by `tagged_log.test.ts` + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let encrypted_note_log_from_typescript = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 126, 79, 51, 186, 47, 71, 253, 172, 99, 112, 241, 59, 197, 241, 107, 186, 232, 87, 187, 230, 171, 62, 228, 234, 42, 51, 145, 146, 238, 242, 42, 71, 206, 13, 244, 66, 111, 195, 20, 203, 98, 148, 204, 242, 145, 183, 156, 29, 141, 54, 44, 220, 194, 35, 229, 16, 32, 204, 211, 49, 142, 112, 82, 202, 116, 241, 254, 146, 42, 217, 20, 189, 70, 228, 182, 171, 205, 104, 27, 99, 171, 28, 91, 244, 21, 30, 130, 240, 5, 72, 174, 124, 97, 197, 157, 248, 204, 203, 140, 171, 181, 152, 130, 169, 179, 41, 52, 173, 45, 43, 198, 1, 152, 72, 158, 249, 11, 41, 9, 160, 48, 78, 123, 132, 203, 140, 215, 13, 22, 201, 88, 255, 139, 154, 76, 20, 63, 134, 125, 108, 239, 208, 63, 59, 33, 117, 139, 225, 184, 0, 64, 153, 21, 131, 204, 111, 41, 84, 23, 144, 222, 245, 200, 12, 234, 11, 48, 10, 221, 20, 252, 38, 122, 40, 249, 66, 248, 197, 198, 209, 79, 20, 59, 66, 197, 215, 16, 18, 145, 228, 239, 124, 81, 67, 103, 49, 196, 58, 228, 195, 64, 199, 243, 184, 112, 173, 29, 196, 215, 77, 217, 85, 82, 149, 113, 76, 201, 93, 95, 148, 37, 95, 222, 233, 210, 150, 1, 182, 28, 132, 59, 148, 156, 129, 36, 230, 55, 199, 149, 36, 88, 50, 143, 204, 153, 32, 104, 29, 137, 19, 31, 178, 137, 117, 157, 20, 8, 128, 230, 250, 75, 44, 54, 128, 2, 241, 208, 153, 7, 214, 252, 57, 152, 75, 69, 57, 248, 179, 156, 145, 92, 165, 13, 116, 53, 33, 42, 183, 231, 77, 203, 170, 125, 20, 121, 48, 136, 194, 94, 139, 207, 148, 218, 248, 129, 231, 129, 191, 45, 239, 108, 60, 34, 82, 117, 186, 180, 199, 77, 169, 213, 238, 47, 37, 0, 40, 12, 110, 216, 175, 23, 59, 81, 117, 164, 24, 80, 241, 40, 126, 179, 230, 78, 145, 75, 102, 204, 12, 30, 146, 240, 222 ]; - for i in 0..expected_encrypted_note_log.len() { - assert_eq(log[i], expected_encrypted_note_log[i]); + for i in 0..encrypted_note_log_from_typescript.len() { + assert_eq(log[i], encrypted_note_log_from_typescript[i]); } - assert_eq(expected_encrypted_note_log.len(), log.len()); + assert_eq(encrypted_note_log_from_typescript.len(), log.len()); } } diff --git a/noir-projects/aztec-nr/aztec/src/generators.nr b/noir-projects/aztec-nr/aztec/src/generators.nr deleted file mode 100644 index 02eae3307fb2..000000000000 --- a/noir-projects/aztec-nr/aztec/src/generators.nr +++ /dev/null @@ -1,20 +0,0 @@ -use dep::protocol_types::point::Point; - -// A set of generators generated with `derive_generators(...)` function from noir::std -global Ga1 = Point { x: 0x30426e64aee30e998c13c8ceecda3a77807dbead52bc2f3bf0eae851b4b710c1, y: 0x113156a068f603023240c96b4da5474667db3b8711c521c748212a15bc034ea6, is_infinite: false }; -global Ga2 = Point { x: 0x2825c79cc6a5cbbeef7d6a8f1b6a12b312aa338440aefeb4396148c89147c049, y: 0x129bfd1da54b7062d6b544e7e36b90736350f6fba01228c41c72099509f5701e, is_infinite: false }; -global Ga3 = Point { x: 0x0edb1e293c3ce91bfc04e3ceaa50d2c541fa9d091c72eb403efb1cfa2cb3357f, y: 0x1341d675fa030ece3113ad53ca34fd13b19b6e9762046734f414824c4d6ade35, is_infinite: false }; - -mod test { - use crate::generators::{Ga1, Ga2, Ga3}; - use dep::protocol_types::point::Point; - use std::hash::derive_generators; - - #[test] -fn test_generators() { - let generators: [Point; 3] = derive_generators("aztec_nr_generators".as_bytes(), 0); - assert_eq(generators[0], Ga1); - assert_eq(generators[1], Ga2); - assert_eq(generators[2], Ga3); - } -} diff --git a/noir-projects/aztec-nr/aztec/src/history/public_storage.nr b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr index 6f1922122e7e..3f60aa24fde6 100644 --- a/noir-projects/aztec-nr/aztec/src/history/public_storage.nr +++ b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr @@ -1,27 +1,24 @@ use dep::protocol_types::{ - constants::GENERATOR_INDEX__PUBLIC_LEAF_INDEX, hash::pedersen_hash, address::AztecAddress, - header::Header, utils::field::full_field_less_than + data::hash::compute_public_data_tree_index, address::AztecAddress, header::Header, + utils::field::full_field_less_than, point::Point }; use std::merkle::compute_merkle_root; use crate::{context::PrivateContext, oracle::get_public_data_witness::get_public_data_witness}; trait PublicStorageHistoricalRead { - fn public_storage_historical_read(header: Header, storage_slot: Field, contract_address: AztecAddress) -> Field; + fn public_storage_historical_read(header: Header, contract_storage_index: Field, contract_address: AztecAddress) -> Field; } impl PublicStorageHistoricalRead for Header { - fn public_storage_historical_read(self, storage_slot: Field, contract_address: AztecAddress) -> Field { - // 1) Compute the leaf slot by siloing the storage slot with the contract address - let public_value_leaf_slot = pedersen_hash( - [contract_address.to_field(), storage_slot], - GENERATOR_INDEX__PUBLIC_LEAF_INDEX - ); + fn public_storage_historical_read(self, contract_storage_index: Field, contract_address: AztecAddress) -> Field { + // 1) Compute the storage index by siloing the contract storage index with the contract address + let public_data_tree_index = compute_public_data_tree_index(contract_address, contract_storage_index); // 2) Get the membership witness of the slot let witness = get_public_data_witness( self.global_variables.block_number as u32, - public_value_leaf_slot + public_data_tree_index ); // 3) Extract the value from the witness leaf and check that the storage slot is correct @@ -30,15 +27,15 @@ impl PublicStorageHistoricalRead for Header { // Here we have two cases. Code based on same checks in `validate_public_data_reads` in `base_rollup_inputs` // 1. The value is the same as the one in the witness // 2. The value was never initialized and is zero - let is_less_than_slot = full_field_less_than(preimage.slot, public_value_leaf_slot); - let is_next_greater_than = full_field_less_than(public_value_leaf_slot, preimage.next_slot); + let is_less_than_slot = full_field_less_than(preimage.slot, public_data_tree_index); + let is_next_greater_than = full_field_less_than(public_data_tree_index, preimage.next_slot); let is_max = ((preimage.next_index == 0) & (preimage.next_slot == 0)); let is_in_range = is_less_than_slot & (is_next_greater_than | is_max); let value = if is_in_range { 0 } else { - assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot doesn't match witness"); + assert_eq(preimage.slot, public_data_tree_index, "Public data tree index doesn't match witness"); preimage.value }; diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters.nr b/noir-projects/aztec-nr/aztec/src/keys/getters.nr index 372e3a433a53..b5aafe7ca206 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters.nr @@ -1,7 +1,7 @@ use dep::protocol_types::{ header::Header, abis::validation_requests::KeyValidationRequest, address::AztecAddress, - constants::CANONICAL_KEY_REGISTRY_ADDRESS, point::Point, storage::map::derive_storage_slot_in_map, - traits::is_empty + constants::CANONICAL_KEY_REGISTRY_ADDRESS, point::Point, slots::derive_base_slot, + slots::derive_storage_slot_in_map, traits::is_empty }; use crate::{ context::PrivateContext, @@ -68,8 +68,8 @@ fn fetch_key_from_registry( address: AztecAddress, header: Header ) -> Point { - let x_coordinate_map_slot = key_index * 2 + 1; - let y_coordinate_map_slot = x_coordinate_map_slot + 1; + let x_coordinate_map_slot = derive_base_slot(key_index * 2 + 1); + let y_coordinate_map_slot = derive_base_slot(key_index * 2 + 2); let x_coordinate_derived_slot = derive_storage_slot_in_map(x_coordinate_map_slot, address); let y_coordinate_derived_slot = derive_storage_slot_in_map(y_coordinate_map_slot, address); diff --git a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr index f84dd3fe861e..dfe007756d9d 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr @@ -30,7 +30,7 @@ fn check_point_to_symmetric_key() { let key = point_to_symmetric_key(secret, point); // The following value gets updated when running encrypt_buffer.test.ts with AZTEC_GENERATE_TEST_DATA=1 let expected_key = [ - 217, 245, 196, 116, 55, 39, 202, 184, 117, 231, 19, 56, 102, 254, 94, 14, 172, 169, 123, 96, 61, 247, 209, 140, 4, 132, 119, 222, 79, 1, 154, 136 + 251, 232, 177, 34, 2, 174, 35, 92, 165, 118, 168, 3, 153, 140, 46, 210, 203, 154, 184, 158, 236, 33, 95, 77, 93, 120, 72, 88, 190, 209, 64, 159 ]; assert_eq(key, expected_key); } diff --git a/noir-projects/aztec-nr/aztec/src/lib.nr b/noir-projects/aztec-nr/aztec/src/lib.nr index af7f03c1f516..d4b1abf35e71 100644 --- a/noir-projects/aztec-nr/aztec/src/lib.nr +++ b/noir-projects/aztec-nr/aztec/src/lib.nr @@ -1,6 +1,5 @@ mod context; mod deploy; -mod generators; mod hash; mod history; mod initializer; diff --git a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr index 2209157faf91..8e9d2eb96f01 100644 --- a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr +++ b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr @@ -4,10 +4,11 @@ use crate::note::{ utils::{compute_inner_note_hash, compute_note_hash_for_consumption}, note_emission::NoteEmission }; use crate::oracle::notes::{notify_created_note, notify_nullified_note}; +use dep::protocol_types::point::Point; pub fn create_note( context: &mut PrivateContext, - storage_slot: Field, + storage_slot: Point, note: &mut Note ) -> NoteEmission where Note: NoteInterface { let contract_address = (*context).this_address(); @@ -23,20 +24,20 @@ pub fn create_note( storage_slot, Note::get_note_type_id(), serialized_note, - inner_note_hash, + inner_note_hash.x, note_hash_counter ) == 0 ); - context.push_note_hash(inner_note_hash); + context.push_note_hash(inner_note_hash.x); NoteEmission::new(*note) } pub fn create_note_hash_from_public( context: &mut PublicContext, - storage_slot: Field, + storage_slot: Point, note: &mut Note ) where Note: NoteInterface { let contract_address = (*context).this_address(); @@ -45,7 +46,7 @@ pub fn create_note_hash_from_public( note.set_header(header); let inner_note_hash = compute_inner_note_hash(*note); - context.push_note_hash(inner_note_hash); + context.push_note_hash(inner_note_hash.x); } pub fn destroy_note( diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr index b1176829c76f..7c256895857c 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr @@ -1,4 +1,4 @@ -use dep::protocol_types::{constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, GET_NOTES_ORACLE_RETURN_LENGTH}}; +use dep::protocol_types::{constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, GET_NOTES_ORACLE_RETURN_LENGTH}, point::Point}; use crate::context::PrivateContext; use crate::note::{ constants::{GET_NOTE_ORACLE_RETURN_LENGTH, MAX_NOTES_PER_PAGE, VIEW_NOTE_ORACLE_RETURN_LENGTH}, @@ -33,7 +33,7 @@ fn extract_property_value_from_selector( fn check_note_header( context: PrivateContext, - storage_slot: Field, + storage_slot: Point, note: Note ) where Note: NoteInterface { let header = note.get_header(); @@ -91,7 +91,7 @@ fn check_notes_order( pub fn get_note( context: &mut PrivateContext, - storage_slot: Field + storage_slot: Point ) -> Note where Note: NoteInterface { let note = get_note_internal(storage_slot); @@ -105,7 +105,7 @@ pub fn get_note( pub fn get_notes( context: &mut PrivateContext, - storage_slot: Field, + storage_slot: Point, options: NoteGetterOptions ) -> BoundedVec where Note: NoteInterface + Eq { let opt_notes = get_notes_internal(storage_slot, options); @@ -115,7 +115,7 @@ pub fn get_notes( fn constrain_get_notes_internal( context: &mut PrivateContext, - storage_slot: Field, + storage_slot: Point, opt_notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], options: NoteGetterOptions ) -> BoundedVec where Note: NoteInterface + Eq { @@ -157,7 +157,7 @@ fn constrain_get_notes_internal( notes } -unconstrained fn get_note_internal(storage_slot: Field) -> Note where Note: NoteInterface { +unconstrained fn get_note_internal(storage_slot: Point) -> Note where Note: NoteInterface { let placeholder_note = [Option::none()]; let placeholder_fields = [0; GET_NOTE_ORACLE_RETURN_LENGTH]; let placeholder_note_length = [0; N]; @@ -183,7 +183,7 @@ unconstrained fn get_note_internal(storage_slot: F } unconstrained fn get_notes_internal( - storage_slot: Field, + storage_slot: Point, options: NoteGetterOptions ) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where Note: NoteInterface { // This function simply performs some transformations from NoteGetterOptions into the types required by the oracle. @@ -215,7 +215,7 @@ unconstrained fn get_notes_internal( } unconstrained pub fn view_notes( - storage_slot: Field, + storage_slot: Point, options: NoteViewerOptions ) -> BoundedVec where Note: NoteInterface { let (num_selects, select_by_indexes, select_by_offsets, select_by_lengths, select_values, select_comparators, sort_by_indexes, sort_by_offsets, sort_by_lengths, sort_order) = flatten_options(options.selects, options.sorts); diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr index 2be975ac51cb..cfbe4f11e57a 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter/test.nr @@ -1,4 +1,4 @@ -use dep::protocol_types::constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL; +use dep::protocol_types::{address::AztecAddress, constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, point::Point}; use crate::{ context::PrivateContext, note::{ @@ -8,11 +8,14 @@ use crate::{ }, oracle::execution::get_contract_address }; -use dep::protocol_types::address::AztecAddress; use crate::test::{helpers::test_environment::TestEnvironment, mocks::mock_note::MockNote}; -global storage_slot: Field = 42; +global storage_slot = Point { + x: 0x1d83b1af3f569775af9e3fdae19b84590245098f46d4a407b5963f313000ce37, + y: 0x1537c632779932ccbc415d91dd70801f88ad410fff48179886d3dce035582d76, + is_infinite: false +}; fn setup() -> TestEnvironment { TestEnvironment::new() diff --git a/noir-projects/aztec-nr/aztec/src/note/note_header.nr b/noir-projects/aztec-nr/aztec/src/note/note_header.nr index 1cec738dd5cd..15e2a581be28 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_header.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_header.nr @@ -1,10 +1,11 @@ -use dep::protocol_types::address::AztecAddress; -use dep::protocol_types::traits::{Empty, Eq, Serialize}; +use dep::protocol_types::{address::AztecAddress, traits::{Empty, Eq, Serialize}, point::Point}; + +global NOTE_HEADER_LENGTH: Field = 6; struct NoteHeader { contract_address: AztecAddress, nonce: Field, - storage_slot: Field, + storage_slot: Point, // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386) // Check the nonce to see whether a note is transient or not. note_hash_counter: u32, // a note_hash_counter of 0 means non-transient @@ -12,7 +13,7 @@ struct NoteHeader { impl Empty for NoteHeader { fn empty() -> Self { - NoteHeader { contract_address: AztecAddress::zero(), nonce: 0, storage_slot: 0, note_hash_counter: 0 } + NoteHeader { contract_address: AztecAddress::zero(), nonce: 0, storage_slot: Point::empty(), note_hash_counter: 0 } } } @@ -26,13 +27,17 @@ impl Eq for NoteHeader { } impl NoteHeader { - pub fn new(contract_address: AztecAddress, nonce: Field, storage_slot: Field) -> Self { + pub fn new(contract_address: AztecAddress, nonce: Field, storage_slot: Point) -> Self { NoteHeader { contract_address, nonce, storage_slot, note_hash_counter: 0 } } } -impl Serialize<4> for NoteHeader { - fn serialize(self) -> [Field; 4] { - [self.contract_address.to_field(), self.nonce, self.storage_slot, self.note_hash_counter as Field] +impl Serialize for NoteHeader { + /// The following method is used by implementations of the Serialize trait for notes --> the implementation + // of the Serialize trait for notes needs to be implemented when the note is passed as an argument to a contract + // function --> in that situation the serialize method is called by aztec-nr when computing an arguments hash. + fn serialize(self) -> [Field; NOTE_HEADER_LENGTH] { + // Note: If you change this function don't forget to update implementations of Serialize trait for notes. + [self.contract_address.to_field(), self.nonce, self.storage_slot.x, self.storage_slot.y, self.storage_slot.is_infinite as Field, self.note_hash_counter as Field] } } diff --git a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr index 317059d90e97..f70384a18095 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr @@ -1,5 +1,6 @@ use crate::context::PrivateContext; use crate::note::note_header::NoteHeader; +use dep::protocol_types::point::Point; // docs:start:note_interface trait NoteInterface { @@ -14,7 +15,7 @@ trait NoteInterface { fn deserialize_content(fields: [Field; N]) -> Self; // Autogenerated by the #[aztec(note)] macro unless it is overridden by a custom implementation - fn compute_note_content_hash(self) -> Field; + fn compute_note_content_hash(self) -> Point; // Autogenerated by the #[aztec(note)] macro unless it is overridden by a custom implementation fn get_header(self) -> NoteHeader; @@ -26,7 +27,7 @@ trait NoteInterface { fn get_note_type_id() -> Field; // Autogenerated by the #[aztec(note)] macro unless it is overridden by a custom implementation - fn to_be_bytes(self, storage_slot: Field) -> [u8; M]; + fn to_be_bytes(self, storage_slot: Point) -> [u8; M]; } // docs:end:note_interface diff --git a/noir-projects/aztec-nr/aztec/src/note/utils.nr b/noir-projects/aztec-nr/aztec/src/note/utils.nr index dfe87cc43466..3cd770063293 100644 --- a/noir-projects/aztec-nr/aztec/src/note/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/note/utils.nr @@ -1,22 +1,18 @@ use crate::{context::PrivateContext, note::{note_header::NoteHeader, note_interface::NoteInterface}}; use dep::protocol_types::{ - constants::GENERATOR_INDEX__INNER_NOTE_HASH, hash::{ pedersen_hash, compute_unique_note_hash, compute_siloed_note_hash as compute_siloed_note_hash, compute_siloed_nullifier as compute_siloed_nullifier_from_preimage }, - utils::arr_copy_slice + point::Point, utils::arr_copy_slice }; -pub fn compute_inner_note_hash_from_preimage(storage_slot: Field, note_content_hash: Field) -> Field { - pedersen_hash( - [storage_slot, note_content_hash], - GENERATOR_INDEX__INNER_NOTE_HASH - ) +pub fn compute_inner_note_hash_from_preimage(storage_slot: Point, note_content_hash: Point) -> Point { + storage_slot + note_content_hash } -pub fn compute_inner_note_hash(note: Note) -> Field where Note: NoteInterface { +pub fn compute_inner_note_hash(note: Note) -> Point where Note: NoteInterface { let header = note.get_header(); let note_hash = note.compute_note_content_hash(); @@ -33,7 +29,7 @@ pub fn compute_siloed_nullifier( compute_siloed_nullifier_from_preimage(header.contract_address, inner_nullifier) } -fn compute_note_hash_for_read_request_from_innter_and_nonce( +fn compute_note_hash_for_read_request_from_inner_and_nonce( inner_note_hash: Field, nonce: Field ) -> Field { @@ -50,7 +46,7 @@ pub fn compute_note_hash_for_read_request(note: No let inner_note_hash = compute_inner_note_hash(note); let nonce = note.get_header().nonce; - compute_note_hash_for_read_request_from_innter_and_nonce(inner_note_hash, nonce) + compute_note_hash_for_read_request_from_inner_and_nonce(inner_note_hash.x, nonce) } pub fn compute_note_hash_for_consumption(note: Note) -> Field where Note: NoteInterface { @@ -64,13 +60,13 @@ pub fn compute_note_hash_for_consumption(note: Not if (header.note_hash_counter != 0) { // If a note is transient, we just read the inner_note_hash (kernel will silo by contract address). - inner_note_hash + inner_note_hash.x } else { // If a note is not transient, that means we are reading a settled note (from tree) created in a // previous TX. So we need the siloed_note_hash which has already been hashed with // nonce and then contract address. This hash will match the existing leaf in the note hash // tree, so the kernel can just perform a membership check directly on this hash/leaf. - let unique_note_hash = compute_note_hash_for_read_request_from_innter_and_nonce(inner_note_hash, header.nonce); + let unique_note_hash = compute_note_hash_for_read_request_from_inner_and_nonce(inner_note_hash.x, header.nonce); compute_siloed_note_hash(header.contract_address, unique_note_hash) // IMPORTANT NOTE ON REDUNDANT SILOING BY CONTRACT ADDRESS: The note hash computed above is // "siloed" by contract address. When a note hash is computed solely for the purpose of @@ -92,8 +88,8 @@ pub fn compute_note_hash_and_optionally_a_nullifier [Field; PUBLIC_DATA_WITNESS] {} -unconstrained pub fn get_public_data_witness(block_number: u32, leaf_slot: Field) -> PublicDataWitness { - let fields = get_public_data_witness_oracle(block_number, leaf_slot); +unconstrained pub fn get_public_data_witness(block_number: u32, storage_index: Field) -> PublicDataWitness { + let fields = get_public_data_witness_oracle(block_number, storage_index); PublicDataWitness { index: fields[0], leaf_preimage: PublicDataTreeLeafPreimage { slot: fields[1], value: fields[2], next_index: fields[3] as u32, next_slot: fields[4] }, diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr index e1384ffc17a4..72b7a80820f0 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr @@ -28,7 +28,7 @@ unconstrained pub fn emit_encrypted_event_log( #[oracle(computeEncryptedNoteLog)] unconstrained fn compute_encrypted_note_log_oracle( _contract_address: AztecAddress, - _storage_slot: Field, + _storage_slot: Point, _note_type_id: Field, _ovsk_app: Field, _ovpk_m: Point, @@ -39,7 +39,7 @@ unconstrained fn compute_encrypted_note_log_oracle( unconstrained pub fn compute_encrypted_note_log( contract_address: AztecAddress, - storage_slot: Field, + storage_slot: Point, note_type_id: Field, ovsk_app: Field, ovpk_m: Point, diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 9b7755fd1350..02a47f6379e7 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -1,10 +1,10 @@ use crate::note::{note_header::NoteHeader, note_interface::NoteInterface}; -use dep::protocol_types::{address::AztecAddress, utils::arr_copy_slice}; +use dep::protocol_types::{address::AztecAddress, point::Point, utils::arr_copy_slice}; #[oracle(notifyCreatedNote)] unconstrained fn notify_created_note_oracle( - _storage_slot: Field, + _storage_slot: Point, _note_type_id: Field, _serialized_note: [Field; N], _inner_note_hash: Field, @@ -12,7 +12,7 @@ unconstrained fn notify_created_note_oracle( ) -> Field {} unconstrained pub fn notify_created_note( - storage_slot: Field, + storage_slot: Point, note_type_id: Field, serialized_note: [Field; N], inner_note_hash: Field, @@ -40,7 +40,7 @@ unconstrained pub fn notify_nullified_note( #[oracle(getNotes)] unconstrained fn get_notes_oracle( - _storage_slot: Field, + _storage_slot: Point, _num_selects: u8, _select_by_indexes: [u8; N], _select_by_offsets: [u8; N], @@ -59,7 +59,7 @@ unconstrained fn get_notes_oracle( ) -> [Field; S] {} unconstrained fn get_notes_oracle_wrapper( - storage_slot: Field, + storage_slot: Point, num_selects: u8, select_by_indexes: [u8; N], select_by_offsets: [u8; N], @@ -97,7 +97,7 @@ unconstrained fn get_notes_oracle_wrapper( } unconstrained pub fn get_notes( - storage_slot: Field, + storage_slot: Point, num_selects: u8, select_by_indexes: [u8; M], select_by_offsets: [u8; M], diff --git a/noir-projects/aztec-nr/aztec/src/oracle/storage.nr b/noir-projects/aztec-nr/aztec/src/oracle/storage.nr index 462740170ed1..eb84b52ad4cb 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/storage.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/storage.nr @@ -1,16 +1,16 @@ use dep::protocol_types::{address::AztecAddress, traits::Deserialize}; #[oracle(storageRead)] -unconstrained fn storage_read_oracle(address: Field, storage_slot: Field, block_number: Field, length: Field) -> [Field; N] {} +unconstrained fn storage_read_oracle(address: Field, contract_storage_index: Field, block_number: Field, length: Field) -> [Field; N] {} unconstrained pub fn raw_storage_read( address: AztecAddress, - storage_slot: Field, + contract_storage_index: Field, block_number: u32 ) -> [Field; N] { storage_read_oracle( address.to_field(), - storage_slot, + contract_storage_index, block_number as Field, N as Field ) @@ -18,10 +18,10 @@ unconstrained pub fn raw_storage_read( unconstrained pub fn storage_read( address: AztecAddress, - storage_slot: Field, + contract_storage_index: Field, block_number: u32 ) -> T where T: Deserialize { - T::deserialize(raw_storage_read(address, storage_slot, block_number)) + T::deserialize(raw_storage_read(address, contract_storage_index, block_number)) } mod tests { @@ -32,7 +32,7 @@ mod tests { use crate::test::mocks::mock_struct::MockStruct; global address = AztecAddress::from_field(29); - global slot = 7; + global contract_storage_index = 7; global block_number = 17; #[test] @@ -41,7 +41,7 @@ mod tests { let _ = OracleMock::mock("storageRead").returns(written.serialize()); - let read: [Field; 2] = raw_storage_read(address, slot, block_number); + let read: [Field; 2] = raw_storage_read(address, contract_storage_index, block_number); assert_eq(read[0], 13); assert_eq(read[1], 42); } @@ -52,7 +52,7 @@ mod tests { let _ = OracleMock::mock("storageRead").returns(written.serialize()); - let read: MockStruct = storage_read(address, slot, block_number); + let read: MockStruct = storage_read(address, contract_storage_index, block_number); assert_eq(read.a, 13); assert_eq(read.b, 42); } diff --git a/noir-projects/aztec-nr/aztec/src/prelude.nr b/noir-projects/aztec-nr/aztec/src/prelude.nr index a280ee031546..5fa0c6433371 100644 --- a/noir-projects/aztec-nr/aztec/src/prelude.nr +++ b/noir-projects/aztec-nr/aztec/src/prelude.nr @@ -1,7 +1,7 @@ // docs:start:prelude use dep::protocol_types::{ - address::{AztecAddress, EthAddress}, abis::function_selector::FunctionSelector, - traits::{Serialize, Deserialize} + address::{AztecAddress, EthAddress}, abis::function_selector::FunctionSelector, point::Point, + scalar::Scalar, traits::{Serialize, Deserialize} }; use crate::{ state_vars::{ diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/map.nr b/noir-projects/aztec-nr/aztec/src/state_vars/map.nr index a4e030728286..572805af7a88 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/map.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/map.nr @@ -1,11 +1,11 @@ -use dep::protocol_types::{hash::pedersen_hash, storage::map::derive_storage_slot_in_map, traits::ToField}; +use dep::protocol_types::{point::Point, slots::derive_storage_slot_in_map, traits::ToField}; use crate::state_vars::storage::Storage; // docs:start:map struct Map { context: Context, - storage_slot: Field, - state_var_constructor: fn(Context, Field) -> V, + storage_slot: Point, + state_var_constructor: fn(Context, Point) -> V, } // docs:end:map @@ -15,10 +15,9 @@ impl Map { // docs:start:new pub fn new( context: Context, - storage_slot: Field, - state_var_constructor: fn(Context, Field) -> V + storage_slot: Point, + state_var_constructor: fn(Context, Point) -> V ) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); Map { context, storage_slot, state_var_constructor } } // docs:end:new diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr index 95ee56edefbe..1372a716f9ca 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr @@ -1,4 +1,7 @@ -use dep::protocol_types::{address::AztecAddress, constants::GENERATOR_INDEX__INITIALIZATION_NULLIFIER, hash::pedersen_hash}; +use dep::protocol_types::{ + address::AztecAddress, constants::GENERATOR_INDEX__INITIALIZATION_NULLIFIER, hash::pedersen_hash, + point::Point +}; use crate::context::{PrivateContext, UnconstrainedContext}; use crate::note::{ @@ -11,7 +14,7 @@ use crate::state_vars::storage::Storage; // docs:start:struct struct PrivateImmutable { context: Context, - storage_slot: Field, + storage_slot: Point, } // docs:end:struct @@ -19,8 +22,7 @@ impl Storage for PrivateImmutable {} impl PrivateImmutable { // docs:start:new - pub fn new(context: Context, storage_slot: Field) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + pub fn new(context: Context, storage_slot: Point) -> Self { Self { context, storage_slot } } // docs:end:new @@ -33,7 +35,7 @@ impl PrivateImmutable { // e.g. the initial assignment to `my_map.at(msg.sender)` will leak: `msg.sender`, the fact that an element of `my_map` was assigned-to for the first time, and the contract_address. pub fn compute_initialization_nullifier(self) -> Field { pedersen_hash( - [self.storage_slot], + [self.storage_slot.x], GENERATOR_INDEX__INITIALIZATION_NULLIFIER ) } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr index 497f9dc987bd..8e89559fb47f 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr @@ -1,4 +1,7 @@ -use dep::protocol_types::{address::AztecAddress, constants::GENERATOR_INDEX__INITIALIZATION_NULLIFIER, hash::pedersen_hash}; +use dep::protocol_types::{ + address::AztecAddress, constants::GENERATOR_INDEX__INITIALIZATION_NULLIFIER, hash::pedersen_hash, + point::Point +}; use crate::context::{PrivateContext, UnconstrainedContext}; use crate::note::{ @@ -11,7 +14,7 @@ use crate::state_vars::storage::Storage; // docs:start:struct struct PrivateMutable { context: Context, - storage_slot: Field + storage_slot: Point } // docs:end:struct @@ -21,8 +24,7 @@ impl Storage for PrivateMutable {} impl PrivateMutable { // docs:start:new - pub fn new(context: Context, storage_slot: Field) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + pub fn new(context: Context, storage_slot: Point) -> Self { Self { context, storage_slot } } // docs:end:new @@ -37,7 +39,7 @@ impl PrivateMutable { // For example, if the `compute_note_hash_and_nullifier()` method injects the secret key of a note owner into the computed nullifier's preimage. pub fn compute_initialization_nullifier(self) -> Field { pedersen_hash( - [self.storage_slot], + [self.storage_slot.x], GENERATOR_INDEX__INITIALIZATION_NULLIFIER ) } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr index 2ba214cec65b..37e91666aad9 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable/test.nr @@ -6,7 +6,11 @@ use crate::{ use crate::test::{mocks::mock_note::MockNote, helpers::{cheatcodes, test_environment::TestEnvironment}}; use std::test::OracleMock; -global storage_slot = 17; +global storage_slot = Point { + x: 0x1d83b1af3f569775af9e3fdae19b84590245098f46d4a407b5963f313000ce37, + y: 0x1537c632779932ccbc415d91dd70801f88ad410fff48179886d3dce035582d76, + is_infinite: false +}; fn setup() -> TestEnvironment { TestEnvironment::new() diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr index 7902602ca09e..e4bdb8344cee 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr @@ -1,4 +1,4 @@ -use dep::protocol_types::{constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, abis::read_request::ReadRequest}; +use dep::protocol_types::{constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, abis::read_request::ReadRequest, point::Point}; use crate::context::{PrivateContext, PublicContext, UnconstrainedContext}; use crate::note::{ constants::MAX_NOTES_PER_PAGE, lifecycle::{create_note, create_note_hash_from_public, destroy_note}, @@ -11,7 +11,7 @@ use crate::state_vars::storage::Storage; // docs:start:struct struct PrivateSet { context: Context, - storage_slot: Field, + storage_slot: Point, } // docs:end:struct @@ -19,8 +19,8 @@ impl Storage for PrivateSet {} impl PrivateSet { // docs:start:new - pub fn new(context: Context, storage_slot: Field) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + pub fn new(context: Context, storage_slot: Point) -> Self { + // TODO(#7515): Consider checking that storage_slot is a valid slot on curve PrivateSet { context, storage_slot } } // docs:end:new diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr index 0ee3326bd84f..f56c0ce81bb9 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr @@ -1,28 +1,23 @@ -use crate::{ - context::{PublicContext, UnconstrainedContext}, oracle::storage::storage_read, - state_vars::storage::Storage -}; -use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}}; +use crate::{context::{PublicContext, UnconstrainedContext}, oracle::storage::storage_read}; +use dep::protocol_types::{slots::INITIALIZATION_CONTRACT_STORAGE_INDEX_SEPARATOR, point::Point, traits::{Deserialize, Serialize}}; // Just like SharedImmutable but without the ability to read from private functions. // docs:start:public_immutable_struct struct PublicImmutable { context: Context, - storage_slot: Field, + contract_storage_index: Field, } // docs:end:public_immutable_struct -impl Storage for PublicImmutable {} - impl PublicImmutable { // docs:start:public_immutable_struct_new pub fn new( // Note: Passing the contexts to new(...) just to have an interface compatible with a Map. context: Context, - storage_slot: Field + storage_slot: Point ) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); - PublicImmutable { context, storage_slot } + let contract_storage_index = storage_slot.x; + PublicImmutable { context, contract_storage_index } } // docs:end:public_immutable_struct_new } @@ -31,26 +26,26 @@ impl PublicImmutable where T: Seria // docs:start:public_immutable_struct_write pub fn initialize(self, value: T) { // We check that the struct is not yet initialized by checking if the initialization slot is 0 - let initialization_slot = INITIALIZATION_SLOT_SEPARATOR + self.storage_slot; - let init_field: Field = self.context.storage_read(initialization_slot); + let initialization_contract_storage_index = INITIALIZATION_CONTRACT_STORAGE_INDEX_SEPARATOR + self.contract_storage_index; + let init_field: Field = self.context.storage_read(initialization_contract_storage_index); assert(init_field == 0, "PublicImmutable already initialized"); // We populate the initialization slot with a non-zero value to indicate that the struct is initialized - self.context.storage_write(initialization_slot, 0xdead); - self.context.storage_write(self.storage_slot, value); + self.context.storage_write(initialization_contract_storage_index, 0xdead); + self.context.storage_write(self.contract_storage_index, value); } // docs:end:public_immutable_struct_write // Note that we don't access the context, but we do call oracles that are only available in public // docs:start:public_immutable_struct_read pub fn read(self) -> T { - self.context.storage_read(self.storage_slot) + self.context.storage_read(self.contract_storage_index) } // docs:end:public_immutable_struct_read } impl PublicImmutablewhere T: Deserialize { unconstrained pub fn read(self) -> T { - self.context.storage_read(self.storage_slot) + self.context.storage_read(self.contract_storage_index) } } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr index 7614ffff1d10..07c3117acca0 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable/test.nr @@ -1,8 +1,12 @@ use crate::{context::PublicContext, state_vars::public_immutable::PublicImmutable}; use crate::test::{helpers::test_environment::TestEnvironment, mocks::mock_struct::MockStruct}; -use dep::protocol_types::traits::Serialize; +use dep::protocol_types::{point::Point, traits::Serialize}; -global storage_slot = 7; +global storage_slot = Point { + x: 0x1d83b1af3f569775af9e3fdae19b84590245098f46d4a407b5963f313000ce37, + y: 0x1537c632779932ccbc415d91dd70801f88ad410fff48179886d3dce035582d76, + is_infinite: false +}; fn setup() -> TestEnvironment { TestEnvironment::new() diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr index 6c300e7575f7..27b27041c1c1 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr @@ -1,26 +1,23 @@ use crate::context::{PublicContext, UnconstrainedContext}; use crate::oracle::storage::storage_read; -use dep::protocol_types::traits::{Deserialize, Serialize}; -use crate::state_vars::storage::Storage; +use dep::protocol_types::{point::Point, traits::{Deserialize, Serialize}}; // docs:start:public_mutable_struct struct PublicMutable { context: Context, - storage_slot: Field, + contract_storage_index: Field, } // docs:end:public_mutable_struct -impl Storage for PublicMutable {} - impl PublicMutable { // docs:start:public_mutable_struct_new pub fn new( // Note: Passing the contexts to new(...) just to have an interface compatible with a Map. context: Context, - storage_slot: Field + storage_slot: Point ) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); - PublicMutable { context, storage_slot } + let contract_storage_index = storage_slot.x; + PublicMutable { context, contract_storage_index } } // docs:end:public_mutable_struct_new } @@ -28,19 +25,19 @@ impl PublicMutable { impl PublicMutable where T: Serialize + Deserialize { // docs:start:public_mutable_struct_read pub fn read(self) -> T { - self.context.storage_read(self.storage_slot) + self.context.storage_read(self.contract_storage_index) } // docs:end:public_mutable_struct_read // docs:start:public_mutable_struct_write pub fn write(self, value: T) { - self.context.storage_write(self.storage_slot, value); + self.context.storage_write(self.contract_storage_index, value); } // docs:end:public_mutable_struct_write } impl PublicMutable where T: Deserialize { unconstrained pub fn read(self) -> T { - self.context.storage_read(self.storage_slot) + self.context.storage_read(self.contract_storage_index) } } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr index 39848738dbb8..739a2236e882 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr @@ -1,25 +1,20 @@ -use crate::{ - context::{PrivateContext, PublicContext, UnconstrainedContext}, oracle::storage::storage_read, - state_vars::storage::Storage -}; -use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}}; +use crate::{context::{PrivateContext, PublicContext, UnconstrainedContext}, oracle::storage::storage_read}; +use dep::protocol_types::{slots::INITIALIZATION_CONTRACT_STORAGE_INDEX_SEPARATOR, point::Point, traits::{Deserialize, Serialize}}; // Just like PublicImmutable but with the ability to read from private functions. struct SharedImmutable{ context: Context, - storage_slot: Field, + contract_storage_index: Field, } -impl Storage for SharedImmutable {} - impl SharedImmutable { pub fn new( // Note: Passing the contexts to new(...) just to have an interface compatible with a Map. context: Context, - storage_slot: Field + storage_slot: Point ) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); - Self { context, storage_slot } + let contract_storage_index = storage_slot.x; + Self { context, contract_storage_index } } } @@ -27,23 +22,23 @@ impl SharedImmutable where T: Serial // Intended to be only called once. pub fn initialize(self, value: T) { // We check that the struct is not yet initialized by checking if the initialization slot is 0 - let initialization_slot = INITIALIZATION_SLOT_SEPARATOR + self.storage_slot; - let init_field: Field = self.context.storage_read(initialization_slot); + let initialization_contract_storage_index = INITIALIZATION_CONTRACT_STORAGE_INDEX_SEPARATOR + self.contract_storage_index; + let init_field: Field = self.context.storage_read(initialization_contract_storage_index); assert(init_field == 0, "SharedImmutable already initialized"); // We populate the initialization slot with a non-zero value to indicate that the struct is initialized - self.context.storage_write(initialization_slot, 0xdead); - self.context.storage_write(self.storage_slot, value); + self.context.storage_write(initialization_contract_storage_index, 0xdead); + self.context.storage_write(self.contract_storage_index, value); } pub fn read_public(self) -> T { - self.context.storage_read(self.storage_slot) + self.context.storage_read(self.contract_storage_index) } } impl SharedImmutable where T: Serialize + Deserialize { unconstrained pub fn read_public(self) -> T { - self.context.storage_read(self.storage_slot) + self.context.storage_read(self.contract_storage_index) } } @@ -55,7 +50,7 @@ impl SharedImmutable where T: Seria for i in 0..fields.len() { fields[i] = header.public_storage_historical_read( - self.storage_slot + i as Field, + self.contract_storage_index + i as Field, (*self.context).this_address() ); } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr index 685e2e64f0be..e1ea396a542e 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr @@ -1,32 +1,21 @@ use dep::protocol_types::{ - hash::{pedersen_hash, poseidon2_hash}, header::Header, address::AztecAddress, + hash::{poseidon2_hash, pedersen_hash}, header::Header, address::AztecAddress, point::Point, traits::{FromField, ToField} }; - -use crate::context::{PrivateContext, PublicContext, UnconstrainedContext}; -use crate::state_vars::{ - storage::Storage, - shared_mutable::{scheduled_value_change::ScheduledValueChange, scheduled_delay_change::ScheduledDelayChange} +use crate::{ + context::{PrivateContext, PublicContext, UnconstrainedContext}, + state_vars::{shared_mutable::{scheduled_value_change::ScheduledValueChange, scheduled_delay_change::ScheduledDelayChange}}, + oracle::storage::storage_read }; -use crate::oracle::storage::storage_read; use dep::std::unsafe::zeroed; mod test; struct SharedMutable { context: Context, - storage_slot: Field, + contract_storage_index: Field, } -// This will make the Aztec macros require that T implements the Serialize trait, and allocate N storage slots to -// this state variable. This is incorrect, since what we actually store is: -// - a ScheduledValueChange, which requires 1 + 2 * M storage slots, where M is the serialization length of T -// - a ScheduledDelayChange, which requires another storage slot -// -// TODO https://github.com/AztecProtocol/aztec-packages/issues/5736: change the storage allocation scheme so that we -// can actually use it here -impl Storage for SharedMutable {} - // TODO: extract into a utils module once we can do arithmetic on generics, i.e. https://github.com/noir-lang/noir/issues/4784 fn concat_arrays(arr_n: [Field; N], arr_m: [Field; M]) -> [Field; O] { assert_eq(N + M, O); @@ -52,9 +41,9 @@ fn concat_arrays(arr_n: [Field; N], arr_m: [ // The delay for changing a value is initially equal to INITIAL_DELAY, but can be changed by calling // `schedule_delay_change`. impl SharedMutable where T: ToField + FromField + Eq { - pub fn new(context: Context, storage_slot: Field) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); - Self { context, storage_slot } + pub fn new(context: Context, storage_slot: Point) -> Self { + let contract_storage_index = storage_slot.x; + Self { context, contract_storage_index } } fn hash_scheduled_data( @@ -75,16 +64,16 @@ impl SharedMutable Field { - pedersen_hash([self.storage_slot, 0], 0) + fn get_value_change_contract_storage_index(self) -> Field { + pedersen_hash([self.contract_storage_index, 0], 0) } - fn get_delay_change_storage_slot(self) -> Field { - pedersen_hash([self.storage_slot, 1], 0) + fn get_delay_change_contract_storage_index(self) -> Field { + pedersen_hash([self.contract_storage_index, 1], 0) } - fn get_hash_storage_slot(self) -> Field { - pedersen_hash([self.storage_slot, 2], 0) + fn get_hash_contract_storage_index(self) -> Field { + pedersen_hash([self.contract_storage_index, 2], 0) } // It may seem odd that we take a header and address instead of reading from e.g. a PrivateContext, but this lets us @@ -102,10 +91,10 @@ impl SharedMutable SharedMutable ScheduledValueChange { - self.context.storage_read(self.get_value_change_storage_slot()) + self.context.storage_read(self.get_value_change_contract_storage_index()) } fn read_delay_change(self) -> ScheduledDelayChange { - self.context.storage_read(self.get_delay_change_storage_slot()) + self.context.storage_read(self.get_delay_change_contract_storage_index()) } fn write( @@ -190,10 +179,10 @@ impl SharedMutable SharedMutable ScheduledValueChange { - self.context.storage_read(self.get_value_change_storage_slot()) + self.context.storage_read(self.get_value_change_contract_storage_index()) } } unconstrained fn get_public_storage_hints( address: AztecAddress, - storage_slot: Field, + contract_storage_index: Field, block_number: u32 ) -> (ScheduledValueChange, ScheduledDelayChange) where T: ToField + FromField + Eq { + // The following is a bit of an ugly hack which works because SharedMutable immediately extracts the x-coordinate + // in the `new(...)` func. It's not straighforward to just pass contract storage index instead of the storage slot + // to the `new(...)` func because the `new(...)` method is called by macros and needs to have the same signature + // as the other storage variables. + let storage_slot = Point { x: contract_storage_index, y: 0, is_infinite: false }; + // This function cannot be part of the &mut PrivateContext impl because that'd mean that by passing `self` we'd also // be passing a mutable reference to an unconstrained function, which is not allowed. We therefore create a dummy // state variable here so that we can access the methods to compute storage slots. This will all be removed in the // future once we do proper storage slot allocation (#5492). let dummy: SharedMutable = SharedMutable::new((), storage_slot); - ( - storage_read(address, dummy.get_value_change_storage_slot(), block_number), storage_read(address, dummy.get_delay_change_storage_slot(), block_number) - ) + (storage_read( + address, + dummy.get_value_change_contract_storage_index(), + block_number + ), storage_read( + address, + dummy.get_delay_change_contract_storage_index(), + block_number + )) } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable_private_getter.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable_private_getter.nr index 2378a102ee4f..f0cfc6392547 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable_private_getter.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable_private_getter.nr @@ -1,6 +1,6 @@ use dep::protocol_types::{ - hash::{pedersen_hash, poseidon2_hash}, traits::{FromField, ToField}, address::AztecAddress, - header::Header + hash::{pedersen_hash, poseidon2_hash}, traits::{FromField, ToField}, point::Point, + address::AztecAddress, header::Header }; use crate::context::PrivateContext; @@ -17,7 +17,7 @@ struct SharedMutablePrivateGetter { // The contract address of the contract we want to read from other_contract_address: AztecAddress, // The storage slot where the SharedMutable is stored on the other contract - storage_slot: Field, + storage_slot: Point, } // We have this as a view-only interface to reading Shared Mutables in other contracts. @@ -26,9 +26,8 @@ impl SharedMutablePrivateGetter where T: Fr pub fn new( context: &mut PrivateContext, other_contract_address: AztecAddress, - storage_slot: Field + storage_slot: Point ) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); assert(other_contract_address.to_field() != 0, "Other contract address cannot be 0"); Self { context, other_contract_address, storage_slot } } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index 8d5923a7f204..eb8ffcbc0e87 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -7,14 +7,18 @@ use crate::{ test::helpers::test_environment::TestEnvironment }; -use dep::protocol_types::address::AztecAddress; +use dep::protocol_types::{address::AztecAddress, point::Point}; use dep::std::{test::OracleMock, unsafe::zeroed}; global new_value = 17; global new_delay = 20; -global storage_slot = 47; +global storage_slot = Point { + x: 0x1d83b1af3f569775af9e3fdae19b84590245098f46d4a407b5963f313000ce37, + y: 0x1537c632779932ccbc415d91dd70801f88ad410fff48179886d3dce035582d76, + is_infinite: false +}; global TEST_INITIAL_DELAY: u32 = 32; @@ -281,7 +285,7 @@ fn test_get_current_value_in_private_bad_value_hints() { let mocked: ScheduledValueChange = ScheduledValueChange::new(0, new_value + 1, schedule_block_number); let _ = OracleMock::mock("storageRead").with_params( ( - env.contract_address().to_field(), private_state_var.get_value_change_storage_slot(), schedule_block_number, 3 + env.contract_address().to_field(), private_state_var.get_value_change_contract_storage_index(), schedule_block_number, 3 ) ).returns(mocked.serialize()).times(1); @@ -301,7 +305,7 @@ fn test_get_current_value_in_private_bad_delay_hints() { let mocked: ScheduledDelayChange = ScheduledDelayChange::new(Option::none(), Option::some(42), schedule_block_number); let _ = OracleMock::mock("storageRead").with_params( ( - env.contract_address().to_field(), private_state_var.get_delay_change_storage_slot(), schedule_block_number, 1 + env.contract_address().to_field(), private_state_var.get_delay_change_contract_storage_index(), schedule_block_number, 1 ) ).returns(mocked.serialize()).times(1); @@ -318,7 +322,7 @@ fn test_get_current_value_in_private_bad_zero_hash_value_hints() { let mocked: ScheduledValueChange = ScheduledValueChange::new(0, new_value, 0); let _ = OracleMock::mock("storageRead").with_params( ( - env.contract_address().to_field(), state_var.get_value_change_storage_slot(), historical_block_number, 3 + env.contract_address().to_field(), state_var.get_value_change_contract_storage_index(), historical_block_number, 3 ) ).returns(mocked.serialize()).times(1); @@ -335,7 +339,7 @@ fn test_get_current_value_in_private_bad_zero_hash_delay_hints() { let mocked: ScheduledDelayChange = ScheduledDelayChange::new(Option::none(), Option::some(new_delay), 0); let _ = OracleMock::mock("storageRead").with_params( ( - env.contract_address().to_field(), state_var.get_delay_change_storage_slot(), historical_block_number, 1 + env.contract_address().to_field(), state_var.get_delay_change_contract_storage_index(), historical_block_number, 1 ) ).returns(mocked.serialize()).times(1); diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr b/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr index 8ae9faf228c4..b5fa2e8cd3ab 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr @@ -1,7 +1,7 @@ -use dep::protocol_types::traits::{Deserialize, Serialize}; +use dep::protocol_types::{point::Point, traits::{Deserialize, Serialize}}; trait Storage where T: Serialize + Deserialize { - fn get_storage_slot(self) -> Field { + fn get_storage_slot(self) -> Point { self.storage_slot } } @@ -10,6 +10,6 @@ trait Storage where T: Serialize + Deserialize { // Every entry in the storage struct will be exported in the compilation artifact as a // Storable entity, containing the storage slot struct Storable { - slot: Field, + slot: Point, } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index e8a03ea7e12a..88058168bb0e 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -1,6 +1,6 @@ use dep::protocol_types::{ abis::function_selector::FunctionSelector, address::{AztecAddress, PartialAddress}, - constants::CONTRACT_INSTANCE_LENGTH, contract_instance::ContractInstance + constants::CONTRACT_INSTANCE_LENGTH, contract_instance::ContractInstance, point::Point }; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::test::helpers::utils::{Deployer, TestAccount}; @@ -37,8 +37,8 @@ unconstrained pub fn deploy( ContractInstance::deserialize(instance_fields) } -unconstrained pub fn direct_storage_write(contract_address: AztecAddress, storage_slot: Field, fields: [Field; N]) { - let _hash = direct_storage_write_oracle(contract_address, storage_slot, fields); +unconstrained pub fn direct_storage_write(contract_address: AztecAddress, storage_slot: Point, fields: [Field; N]) { + let _hash = direct_storage_write_oracle(contract_address, storage_slot.x, fields); } unconstrained pub fn create_account() -> TestAccount { diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/keys.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/keys.nr index dcdc9efda7c7..536b1c38ba64 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/keys.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/keys.nr @@ -1,13 +1,13 @@ use dep::protocol_types::{ - address::AztecAddress, storage::map::derive_storage_slot_in_map, - constants::CANONICAL_KEY_REGISTRY_ADDRESS, point::Point + address::AztecAddress, slots::derive_storage_slot_in_map, constants::CANONICAL_KEY_REGISTRY_ADDRESS, + point::Point, slots::derive_base_slot }; use crate::test::helpers::cheatcodes; pub fn store_master_key(key_index: Field, address: AztecAddress, key: Point) { - let x_coordinate_map_slot = key_index * 2 + 1; - let y_coordinate_map_slot = x_coordinate_map_slot + 1; + let x_coordinate_map_slot = derive_base_slot(key_index * 2 + 1); + let y_coordinate_map_slot = derive_base_slot(key_index * 2 + 2); let x_coordinate_derived_slot = derive_storage_slot_in_map(x_coordinate_map_slot, address); let y_coordinate_derived_slot = derive_storage_slot_in_map(y_coordinate_map_slot, address); diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index f5c3436c9fe0..fa7477fc7dd1 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -1,7 +1,7 @@ use dep::protocol_types::{ abis::{function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs}, - address::{AztecAddress, PartialAddress}, storage::map::derive_storage_slot_in_map, - constants::CANONICAL_KEY_REGISTRY_ADDRESS, traits::Deserialize + address::{AztecAddress, PartialAddress}, constants::CANONICAL_KEY_REGISTRY_ADDRESS, + traits::Deserialize, point::Point }; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; @@ -210,7 +210,7 @@ impl TestEnvironment { pub fn store_note_in_cache( _self: Self, note: &mut Note, - storage_slot: Field, + storage_slot: Point, contract_address: AztecAddress ) where Note: NoteInterface { let original_contract_address = get_contract_address(); @@ -226,7 +226,7 @@ impl TestEnvironment { storage_slot, Note::get_note_type_id(), serialized_note, - inner_note_hash, + inner_note_hash.x, note_hash_counter ) == 0 diff --git a/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr index f5dd8c400c6f..f8686338d15e 100644 --- a/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr +++ b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr @@ -1,6 +1,9 @@ -use crate::{context::PrivateContext, note::{note_header::NoteHeader, note_interface::NoteInterface}}; +use crate::{ + context::PrivateContext, note::{note_header::NoteHeader, note_interface::NoteInterface}, + utils::point::point_to_bytes +}; -use dep::protocol_types::{address::AztecAddress, traits::Eq}; +use dep::protocol_types::{address::AztecAddress, point::Point, generators::Ga1, traits::Eq}; global MOCK_NOTE_LENGTH = 1; // MOCK_NOTE_LENGTH * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) @@ -23,8 +26,8 @@ impl NoteInterface for MockNote { } } - fn compute_note_content_hash(_self: Self) -> Field { - 0 + fn compute_note_content_hash(_self: Self) -> Point { + Ga1 } fn get_header(self) -> NoteHeader { @@ -47,12 +50,12 @@ impl NoteInterface for MockNote { (0, 0) } - fn to_be_bytes(self, storage_slot: Field) -> [u8; MOCK_NOTE_BYTES_LENGTH] { + fn to_be_bytes(self, storage_slot: Point) -> [u8; MOCK_NOTE_BYTES_LENGTH] { let serialized_note = self.serialize_content(); let mut buffer: [u8; MOCK_NOTE_BYTES_LENGTH] = [0; MOCK_NOTE_BYTES_LENGTH]; - let storage_slot_bytes = storage_slot.to_be_bytes(32); + let storage_slot_bytes = point_to_bytes(storage_slot); let note_type_id_bytes = MockNote::get_note_type_id().to_be_bytes(32); for i in 0..32 { @@ -80,7 +83,7 @@ impl Eq for MockNote { struct MockNoteBuilder { value: Field, contract_address: Option, - storage_slot: Option, + storage_slot: Option, } impl MockNoteBuilder { @@ -93,7 +96,7 @@ impl MockNoteBuilder { self } - fn storage_slot(&mut self, storage_slot: Field) -> &mut Self { + fn storage_slot(&mut self, storage_slot: Point) -> &mut Self { self.storage_slot = Option::some(storage_slot); self } diff --git a/noir-projects/aztec-nr/aztec/src/utils/point.nr b/noir-projects/aztec-nr/aztec/src/utils/point.nr index c2d0d2635d5c..20c6af687f83 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/point.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/point.nr @@ -14,7 +14,9 @@ pub fn point_to_bytes(pk: Point) -> [u8; 32] { let mut result = pk.x.to_be_bytes(32); // We store only a "sign" of the y coordinate because the rest can be derived from the x coordinate. To get - // the sign we check if the y coordinate is greater than the curve's order minus 1 divided by 2. + // the sign we check if the y coordinate is less or equal than the curve's order minus 1 divided by 2. + // Ideally we'd do `y <= MOD_DIV_2`, but there's no `lte` function, so instead we do `!(y > MOD_DIV_2)`, which is + // equivalent, and then rewrite that as `!(MOD_DIV_2 < y)`, since we also have no `gt` function. if !BN254_FR_MODULUS_DIV_2.lt(pk.y) { // y is <= (modulus - 1) / 2 so we set the sign bit to 1 // Here we leverage that field fits into 254 bits (log2(Fr.MODULUS) < 254) and given that we serialize Fr to 32 diff --git a/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr b/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr index 0d93ae6fc21d..898ea8263d4c 100644 --- a/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr +++ b/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr @@ -1,6 +1,5 @@ use dep::aztec::{ - context::PrivateContext, protocol_types::{address::AztecAddress}, - note::note_getter_options::NoteGetterOptions, state_vars::PrivateSet, + prelude::{AztecAddress, Point, NoteGetterOptions, PrivateSet, PrivateContext}, encrypted_logs::encrypted_note_emission::encode_and_encrypt_note }; use dep::value_note::{filter::filter_notes_min_sum, value_note::ValueNote}; @@ -8,13 +7,12 @@ use dep::value_note::{filter::filter_notes_min_sum, value_note::ValueNote}; struct EasyPrivateUint { context: Context, set: PrivateSet, - storage_slot: Field, + storage_slot: Point, } // Holds a note that can act similarly to an int. impl EasyPrivateUint { - pub fn new(context: Context, storage_slot: Field) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + pub fn new(context: Context, storage_slot: Point) -> Self { let set = PrivateSet::new(context, storage_slot); EasyPrivateUint { context, set, storage_slot } } diff --git a/noir-projects/aztec-nr/value-note/src/value_note.nr b/noir-projects/aztec-nr/value-note/src/value_note.nr index 1b135577ec73..726497e2c131 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -57,11 +57,15 @@ impl ValueNote { } } -impl Serialize<7> for ValueNote { - fn serialize(self) -> [Field; 7] { +impl Serialize<9> for ValueNote { + /// The following method needed to be implemented because the note is passed as an argument to a contract function + /// --> the serialize method is called by aztec-nr when computing an arguments hash. + /// Note that when the note is about to be encrypted and emitted as a log the to_be_bytes function auto-implemented + /// by aztec macros is called instead. + fn serialize(self) -> [Field; 9] { let header = self.header.serialize(); - [self.value, self.npk_m_hash, self.randomness, header[0], header[1], header[2], header[3]] + [self.value, self.npk_m_hash, self.randomness, header[0], header[1], header[2], header[3], header[4], header[5]] } } diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index 5bebeadf78d8..52508fc0a01a 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -28,7 +28,10 @@ contract AvmTest { use dep::aztec::protocol_types::constants::CONTRACT_INSTANCE_LENGTH; use dep::aztec::prelude::{Map, Deserialize}; use dep::aztec::state_vars::PublicMutable; - use dep::aztec::protocol_types::{address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH, point::Point, scalar::Scalar}; + use dep::aztec::protocol_types::{ + address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH, point::Point, + scalar::Scalar, slots::derive_storage_slot_in_map + }; use dep::aztec::oracle::get_contract_instance::{get_contract_instance_avm, get_contract_instance_internal_avm}; use dep::aztec::protocol_types::abis::function_selector::FunctionSelector; use dep::aztec::context::gas::GasOpts; @@ -73,18 +76,18 @@ contract AvmTest { } #[aztec(public)] - fn set_storage_map(to: AztecAddress, amount: u32) -> Field { + fn set_storage_map(to: AztecAddress, amount: u32) -> Point { storage.map.at(to).write(amount); // returns storage slot for key - std::hash::pedersen_hash([storage.map.storage_slot, to.to_field()]) + derive_storage_slot_in_map(storage.map.storage_slot, to) } #[aztec(public)] - fn add_storage_map(to: AztecAddress, amount: u32) -> Field { + fn add_storage_map(to: AztecAddress, amount: u32) -> Point { let new_balance = storage.map.at(to).read().add(amount); storage.map.at(to).write(new_balance); // returns storage slot for key - std::hash::pedersen_hash([storage.map.storage_slot, to.to_field()]) + derive_storage_slot_in_map(storage.map.storage_slot, to) } #[aztec(public)] diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr index 0d315f4e3141..975389bf52e8 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr @@ -1,4 +1,4 @@ -use dep::aztec::prelude::{AztecAddress, FunctionSelector, PrivateContext, NoteHeader, NoteGetterOptions, NoteViewerOptions}; +use dep::aztec::prelude::{AztecAddress, FunctionSelector, PrivateContext, NoteHeader, NoteGetterOptions, NoteViewerOptions, Point}; use dep::aztec::{ context::UnconstrainedContext, @@ -95,7 +95,7 @@ pub fn filter_cards( } impl Deck { - pub fn new(context: Context, storage_slot: Field) -> Self { + pub fn new(context: Context, storage_slot: Point) -> Self { let set = PrivateSet { context, storage_slot }; Deck { set } } diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index 97e5e40dec34..aa0ac75e9d1c 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -40,7 +40,7 @@ contract Counter { // docs:start:test_imports use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; - use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; + use dep::aztec::protocol_types::slots::derive_storage_slot_in_map; use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; use dep::aztec::note::note_viewer_options::NoteViewerOptions; // docs:end:test_imports diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr index 898ec765f1eb..9ef021dcf04a 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -16,7 +16,7 @@ contract DocsExample { use dep::aztec::prelude::{ AztecAddress, FunctionSelector, NoteHeader, NoteGetterOptions, NoteViewerOptions, PrivateContext, Map, PublicMutable, PublicImmutable, PrivateMutable, PrivateImmutable, - PrivateSet, SharedImmutable, Deserialize + PrivateSet, SharedImmutable, Deserialize, Point }; use dep::aztec::encrypted_logs::encrypted_note_emission::{encode_and_encrypt_note, encode_and_encrypt_note_with_keys}; use dep::aztec::note::note_getter_options::Comparator; @@ -57,37 +57,87 @@ contract DocsExample { fn init(context: Context) -> Self { Storage { // docs:start:storage-leader-init - leader: PublicMutable::new(context, 1), + leader: PublicMutable::new( + context, + Point { + x: 0x142d6868faa787849780a723886031bf588b51e4b2441bd56a43c3c72bdb8703, + y: 0x2f09ef226e0480cb6ce0167a9d23c2145a2d5e43ac174e24add8d74ce818b65a, + is_infinite: false + } + ), // docs:end:storage-leader-init // docs:start:start_vars_private_mutable - legendary_card: PrivateMutable::new(context, 3), + legendary_card: PrivateMutable::new( + context, + Point { + x: 0x198e10938d4d26290e6ade7d9e9ea87f5a4c701e9bd147627280ee276fcde336, + y: 0x0e530ece3dd640032714f5cd0e6962b86f5cec4983ff1efa69cab06122463938, + is_infinite: false + } + ), // docs:end:start_vars_private_mutable // just used for docs example (not for game play): // docs:start:state_vars-MapPrivateMutable profiles: Map::new( context, - 4, + Point { + x: 0x22ac8d641e92d7a37aa4844cf960a28c424803161b497027da41f3b017cf3fb9, + y: 0x126d7a194bba564d97f368bdf6705c7c9eee63a6ab193de975fae9eee1363ae0, + is_infinite: false + }, |context, slot| { PrivateMutable::new(context, slot) } ), // docs:end:state_vars-MapPrivateMutable // docs:start:storage-set-init - set: PrivateSet::new(context, 5), + set: PrivateSet::new( + context, + Point { + x: 0x2dcd7369f68ab040ceefa358a43b5ddf0732b9ab8e6af185d30acbf839a542b0, + y: 0x08b2d53e1bab1092588c21af19108bbd75616de5c8ee00961a3e903caecc7ca6, + is_infinite: false + } + ), // docs:end:storage-set-init - private_immutable: PrivateImmutable::new(context, 6), - shared_immutable: SharedImmutable::new(context, 7), + private_immutable: PrivateImmutable::new( + context, + Point { + x: 0x0f36bfc97f61a23fa3c80b5667d4d18c56e87048fad37a533363ed5b2ab67039, + y: 0x2782850331ec7617a09557275372abae8f2bb0b51005071d2d2d2c9ce53421c2, + is_infinite: false + } + ), + shared_immutable: SharedImmutable::new( + context, + Point { + x: 0x03d0f4524aa2db53c6d63b9b8a3ab02208c805ba7da32a1983d40b7ec6a6de88, + y: 0x1f431a0d6cbf170a81594cc99478b27f685b127e36b46427a785e373e1d37b75, + is_infinite: false + } + ), // docs:start:storage-minters-init minters: Map::new( context, - 8, + Point { + x: 0x005be2340655f5fdfaccc9cb9a0c4cfa2af31dd76217e5574e45db7c62ceb7e8, + y: 0x2287e3503fd3285ad205fc11ba372d5757a9354d16508f343dba1a72bf9c1aee, + is_infinite: false + }, |context, slot| { PublicMutable::new(context, slot) } ), // docs:end:storage-minters-init // docs:start:storage-public-immutable - public_immutable: PublicImmutable::new(context, 9)// docs:end:storage-public-immutable + public_immutable: PublicImmutable::new( + context, + Point { + x: 0x0e6997504ff8a5926e8c2065687fa566f1fab70ee275c1e18607275c9aae994e, + y: 0x0436a91e9c1a642d44040600fc72769d13c63969a4a03a80892305e37fa57d25, + is_infinite: false + } + )// docs:end:storage-public-immutable } } } diff --git a/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr index 96b258016260..5b028a667915 100644 --- a/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr @@ -20,7 +20,8 @@ contract GasToken { struct Storage { // This map is accessed directly by protocol circuits to check balances for fee payment. // Do not change this storage layout unless you also update the base rollup circuits. - balances: Map>, + // TODO(#7604): uncomment the following + // balances: Map>, portal_address: SharedImmutable, } @@ -96,8 +97,12 @@ contract GasToken { #[aztec(public)] #[aztec(internal)] fn _increase_public_balance(to: AztecAddress, amount: Field) { - let new_balance = storage.balances.at(to).read().add(U128::from_integer(amount)); - storage.balances.at(to).write(new_balance); + // let new_balance = storage.balances.at(to).read().add(U128::from_integer(amount)); + // storage.balances.at(to).write(new_balance); + + // TODO(#7604): nuke the following and uncomment above + let new_balance: U128 = context.storage_read(to.to_field()) + U128::from_integer(amount); + context.storage_write(to.to_field(), new_balance); } // TODO(palla/gas) Remove this function and use the private claim flow only @@ -110,8 +115,12 @@ contract GasToken { // Consume message and emit nullifier context.consume_l1_to_l2_message(content_hash, secret, portal_address, leaf_index); - let new_balance = storage.balances.at(to).read() + U128::from_integer(amount); - storage.balances.at(to).write(new_balance); + // let new_balance = storage.balances.at(to).read() + U128::from_integer(amount); + // storage.balances.at(to).write(new_balance); + + // TODO(#7604): nuke the following and uncomment above + let new_balance: U128 = context.storage_read(to.to_field()) + U128::from_integer(amount); + context.storage_write(to.to_field(), new_balance); } // TODO(@just-mitch): remove this function before mainnet deployment @@ -120,22 +129,32 @@ contract GasToken { #[aztec(public)] fn mint_public(to: AztecAddress, amount: Field) { let amount = U128::from_integer(amount); - let new_balance = storage.balances.at(to).read().add(amount); + // let new_balance = storage.balances.at(to).read().add(amount); - storage.balances.at(to).write(new_balance); + // storage.balances.at(to).write(new_balance); + + // TODO(#7604): nuke the following and uncomment above + let new_balance: U128 = context.storage_read(to.to_field()) + amount; + context.storage_write(to.to_field(), new_balance); } #[aztec(public)] #[aztec(view)] fn check_balance(fee_limit: Field) { let fee_limit = U128::from_integer(fee_limit); - assert(storage.balances.at(context.msg_sender()).read() >= fee_limit, "Balance too low"); + // assert(storage.balances.at(context.msg_sender()).read() >= fee_limit, "Balance too low"); + + // TODO(#7604): nuke the following and uncomment above + let balance: U128 = context.storage_read(context.msg_sender().to_field()); + assert(balance >= fee_limit, "Balance too low"); } // utility function for testing #[aztec(public)] #[aztec(view)] fn balance_of_public(owner: AztecAddress) -> pub Field { - storage.balances.at(owner).read().to_field() + // storage.balances.at(owner).read().to_field() + // TODO(#7604): nuke the following and uncomment above + context.storage_read(owner.to_field()) } } diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 2b370e1ec355..c63df3aa3208 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -196,7 +196,7 @@ contract InclusionProofs { assert_eq( // docs:start:public_storage_historical_read header.public_storage_historical_read( - storage.public_unused_value.storage_slot, + storage.public_unused_value.contract_storage_index, context.this_address() // docs:end:public_storage_historical_read ), 0 @@ -215,7 +215,10 @@ contract InclusionProofs { context.get_header() }; - let actual = header.public_storage_historical_read(storage.public_value.storage_slot, context.this_address()); + let actual = header.public_storage_historical_read( + storage.public_value.contract_storage_index, + context.this_address() + ); assert_eq(actual, expected, "Actual public value does not match expected"); } diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index fb6181311417..eb06e1c97846 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -244,7 +244,7 @@ contract Parent { } use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; - use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; + use dep::aztec::protocol_types::slots::derive_storage_slot_in_map; use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; use dep::aztec::note::note_viewer_options::NoteViewerOptions; use dep::child_contract::Child; diff --git a/noir-projects/noir-contracts/contracts/private_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/private_token_contract/src/main.nr index 3692df2c74d2..fd441b2b0999 100644 --- a/noir-projects/noir-contracts/contracts/private_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/private_token_contract/src/main.nr @@ -213,8 +213,8 @@ contract PrivateToken { let user_inner_note_hash = compute_inner_note_hash_from_preimage(PrivateToken::storage().balances.slot, user_note_content_hash); // 3. At last we emit the note hashes. - context.push_note_hash(fee_payer_inner_note_hash); - context.push_note_hash(user_inner_note_hash); + context.push_note_hash(fee_payer_inner_note_hash.x); + context.push_note_hash(user_inner_note_hash.x); // --> Once the tx is settled user and fee recipient can add the notes to their pixies. } diff --git a/noir-projects/noir-contracts/contracts/private_token_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/private_token_contract/src/test/utils.nr index 1825f0921d9a..f97e2706a79e 100644 --- a/noir-projects/noir-contracts/contracts/private_token_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/private_token_contract/src/test/utils.nr @@ -1,7 +1,6 @@ use dep::aztec::{ hash::compute_secret_hash, prelude::AztecAddress, test::helpers::{cheatcodes, test_environment::TestEnvironment}, - protocol_types::storage::map::derive_storage_slot_in_map, note::{note_getter::{MAX_NOTES_PER_PAGE, view_notes}, note_viewer_options::NoteViewerOptions}, oracle::{execution::get_contract_address, unsafe_rand::unsafe_rand, storage::storage_read}, context::PrivateContext diff --git a/noir-projects/noir-contracts/contracts/private_token_contract/src/types/balances_map.nr b/noir-projects/noir-contracts/contracts/private_token_contract/src/types/balances_map.nr index d70e62fa91c2..b28e82d4285e 100644 --- a/noir-projects/noir-contracts/contracts/private_token_contract/src/types/balances_map.nr +++ b/noir-projects/noir-contracts/contracts/private_token_contract/src/types/balances_map.nr @@ -1,6 +1,6 @@ use dep::aztec::prelude::{ AztecAddress, NoteGetterOptions, NoteViewerOptions, NoteHeader, NoteInterface, PrivateContext, - PrivateSet, Map + PrivateSet, Map, Point }; use dep::aztec::{ hash::pedersen_hash, protocol_types::constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, @@ -17,8 +17,7 @@ struct BalancesMap { } impl BalancesMap { - pub fn new(context: Context, storage_slot: Field) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + pub fn new(context: Context, storage_slot: Point) -> Self { Self { map: PrivateSet::new(context, storage_slot) } } } diff --git a/noir-projects/noir-contracts/contracts/private_token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/private_token_contract/src/types/token_note.nr index d2f0ddf2d38b..5d07451be362 100644 --- a/noir-projects/noir-contracts/contracts/private_token_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/private_token_contract/src/types/token_note.nr @@ -1,9 +1,11 @@ use dep::aztec::{ prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext}, - protocol_types::{constants::GENERATOR_INDEX__NOTE_NULLIFIER, point::Point, scalar::Scalar, hash::poseidon2_hash}, + protocol_types::{ + constants::GENERATOR_INDEX__NOTE_NULLIFIER, generators::{Ga1 as G_amt, Ga2 as G_npk, Ga3 as G_rnd}, + point::Point, scalar::Scalar, hash::poseidon2_hash +}, note::utils::compute_note_hash_for_consumption, oracle::unsafe_rand::unsafe_rand, - keys::getters::get_nsk_app, note::note_getter_options::PropertySelector, - generators::{Ga1 as G_amt, Ga2 as G_npk, Ga3 as G_rnd} + keys::getters::get_nsk_app, note::note_getter_options::PropertySelector }; use dep::std::field::bn254::decompose; use dep::std::embedded_curve_ops::multi_scalar_mul; @@ -28,7 +30,7 @@ trait PrivatelyRefundable { incomplete_fee_payer_point: Point, incomplete_user_point: Point, transaction_fee: Field - ) -> (Field, Field); + ) -> (Point, Point); } global TOKEN_NOTE_LEN: Field = 3; // 3 plus a header. @@ -71,7 +73,7 @@ impl NoteInterface for TokenNote { - fn compute_note_content_hash(self) -> Field { + fn compute_note_content_hash(self) -> Point { let (npk_lo, npk_hi) = decompose(self.npk_m_hash); let (random_lo, random_hi) = decompose(self.randomness); // We compute the note content hash as an x-coordinate of `G_amt * amount + G_npk * npk_m_hash + G_rnd * randomness` instead @@ -91,7 +93,7 @@ impl NoteInterface for TokenNote { lo: random_lo, hi: random_hi, }] - ).x + ) } } @@ -209,6 +211,7 @@ impl PrivatelyRefundable for TokenNote { fn generate_refund_points(fee_payer_npk_m_hash: Field, user_npk_m_hash: Field, funded_amount: Field, user_randomness: Field, fee_payer_randomness: Field) -> (Point, Point) { // 1. To be able to multiply generators with randomness and npk_m_hash using barretneberg's (BB) blackbox // function we first need to convert the fields to high and low limbs. + // TODO(#7606): replace decompose with from_field_unsafe let (fee_payer_randomness_lo, fee_payer_randomness_hi) = decompose(fee_payer_randomness); let (fee_payer_npk_m_hash_lo, fee_payer_npk_m_hash_hi) = decompose(fee_payer_npk_m_hash); @@ -253,7 +256,7 @@ impl PrivatelyRefundable for TokenNote { (incomplete_fee_payer_point, incomplete_user_point) } - fn complete_refund(incomplete_fee_payer_point: Point, incomplete_user_point: Point, transaction_fee: Field) -> (Field, Field) { + fn complete_refund(incomplete_fee_payer_point: Point, incomplete_user_point: Point, transaction_fee: Field) -> (Point, Point) { // 1. We convert the transaction fee to high and low limbs to be able to use BB API. let (transaction_fee_lo, transaction_fee_hi) = decompose(transaction_fee); @@ -273,7 +276,7 @@ impl PrivatelyRefundable for TokenNote { assert_eq(user_point.is_infinite, false); - // Finally we return the x-coordinates of the points which are the note content hashes. - (fee_payer_point.x, user_point.x) + // Finally we return the points which represent the note content hashes. + (fee_payer_point, user_point) } } diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index e58e9ca0ba4f..6a4c592e5613 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -14,7 +14,7 @@ contract Test { abis::private_circuit_public_inputs::PrivateCircuitPublicInputs, constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, CANONICAL_KEY_REGISTRY_ADDRESS}, traits::{Serialize, ToField, FromField}, point::Point, scalar::Scalar, - storage::map::derive_storage_slot_in_map + slots::derive_storage_slot_in_map, slots::S1 }; use dep::aztec::encrypted_logs::header::EncryptedLogHeader; @@ -93,7 +93,7 @@ contract Test { value: Field, owner: AztecAddress, outgoing_viewer: AztecAddress, - storage_slot: Field + storage_slot: Point ) { assert( storage_slot != storage.example_constant.get_storage_slot(), "this storage slot is reserved for example_constant" @@ -107,7 +107,7 @@ contract Test { } #[aztec(private)] - fn call_get_notes(storage_slot: Field, active_or_nullified: bool) -> Field { + fn call_get_notes(storage_slot: Point, active_or_nullified: bool) -> Field { assert( storage_slot != storage.example_constant.get_storage_slot(), "this storage slot is reserved for example_constant" ); @@ -123,7 +123,7 @@ contract Test { } #[aztec(private)] - fn call_get_notes_many(storage_slot: Field, active_or_nullified: bool) -> [Field; 2] { + fn call_get_notes_many(storage_slot: Point, active_or_nullified: bool) -> [Field; 2] { assert( storage_slot != storage.example_constant.get_storage_slot(), "this storage slot is reserved for example_constant" ); @@ -138,7 +138,7 @@ contract Test { [notes.get(0).value, notes.get(1).value] } - unconstrained fn call_view_notes(storage_slot: Field, active_or_nullified: bool) -> pub Field { + unconstrained fn call_view_notes(storage_slot: Point, active_or_nullified: bool) -> pub Field { assert( storage_slot != storage.example_constant.get_storage_slot(), "this storage slot is reserved for example_constant" ); @@ -153,7 +153,7 @@ contract Test { notes.get(0).value } - unconstrained fn call_view_notes_many(storage_slot: Field, active_or_nullified: bool) -> pub [Field; 2] { + unconstrained fn call_view_notes_many(storage_slot: Point, active_or_nullified: bool) -> pub [Field; 2] { assert( storage_slot != storage.example_constant.get_storage_slot(), "this storage slot is reserved for example_constant" ); @@ -169,7 +169,7 @@ contract Test { } #[aztec(private)] - fn call_destroy_note(storage_slot: Field) { + fn call_destroy_note(storage_slot: Point) { assert( storage_slot != storage.example_constant.get_storage_slot(), "this storage slot is reserved for example_constant" ); @@ -304,16 +304,16 @@ contract Test { #[aztec(private)] fn emit_encrypted_logs_nested(value: Field, owner: AztecAddress, outgoing_viewer: AztecAddress) { - let mut storage_slot = storage.example_constant.get_storage_slot() + 1; + let mut storage_slot = storage.example_constant.get_storage_slot() + S1; Test::at(context.this_address()).call_create_note(value, owner, outgoing_viewer, storage_slot).call(&mut context); - storage_slot += 1; + storage_slot += S1; let header = context.get_header(); let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = ValueNote::new(value + 1, owner_npk_m_hash); create_note(&mut context, storage_slot, &mut note).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), owner)); - storage_slot += 1; + storage_slot += S1; Test::at(context.this_address()).call_create_note(value + 2, owner, outgoing_viewer, storage_slot).call(&mut context); } @@ -416,7 +416,7 @@ contract Test { fn compute_incoming_log_body_ciphertext( secret: Scalar, point: Point, - storage_slot: Field, + storage_slot: Point, value: Field ) -> [u8; 112] { let note = TestNote::new(value); @@ -488,7 +488,7 @@ contract Test { #[aztec(private)] fn test_shared_mutable_private_getter( contract_address_to_read: AztecAddress, - storage_slot_of_shared_mutable: Field + storage_slot_of_shared_mutable: Point ) -> Field { // It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly let test: SharedMutablePrivateGetter = SharedMutablePrivateGetter::new( @@ -506,7 +506,7 @@ contract Test { // keys to the pxe, this function will return nothing, but the public key getters will return the correct value #[aztec(private)] fn test_shared_mutable_private_getter_for_registry_contract( - storage_slot_of_shared_mutable: Field, + storage_slot_of_shared_mutable: Point, address_to_get_in_registry: AztecAddress ) -> Field { // We have to derive this slot to get the location of the shared mutable inside the Map diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr index 882364d9f655..92d85070f6fb 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr @@ -1,4 +1,4 @@ -use dep::aztec::prelude::{AztecAddress, NoteGetterOptions, NoteViewerOptions, NoteHeader, NoteInterface, PrivateSet, Map}; +use dep::aztec::prelude::{AztecAddress, NoteGetterOptions, NoteViewerOptions, NoteHeader, NoteInterface, PrivateSet, Map, Point}; use dep::aztec::{ context::{PrivateContext, UnconstrainedContext}, hash::pedersen_hash, protocol_types::constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, @@ -11,8 +11,7 @@ struct BalancesMap { } impl BalancesMap { - pub fn new(context: Context, storage_slot: Field) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + pub fn new(context: Context, storage_slot: Point) -> Self { Self { map: Map::new( context, diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr index abbac9623032..54c5d2c5b775 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr @@ -1,7 +1,7 @@ use dep::aztec::{ hash::compute_secret_hash, prelude::AztecAddress, test::helpers::{cheatcodes, test_environment::TestEnvironment}, - protocol_types::storage::map::derive_storage_slot_in_map, + protocol_types::slots::derive_storage_slot_in_map, note::{note_getter::{MAX_NOTES_PER_PAGE, view_notes}, note_viewer_options::NoteViewerOptions}, oracle::{execution::{get_block_number, get_contract_address}, unsafe_rand::unsafe_rand, storage::storage_read} }; @@ -78,8 +78,12 @@ pub fn check_public_balance(token_contract_address: AztecAddress, address: Aztec let block_number = get_block_number(); let balances_slot = Token::storage().public_balances.slot; - let address_slot = derive_storage_slot_in_map(balances_slot, address); - let amount: U128 = storage_read(token_contract_address, address_slot, block_number); + let address_contract_storage_index = derive_storage_slot_in_map(balances_slot, address).x; + let amount: U128 = storage_read( + token_contract_address, + address_contract_storage_index, + block_number + ); assert(amount.to_field() == address_amount, "Public balance is not correct"); cheatcodes::set_contract_address(current_contract_address); } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr index 63a0092ea5c9..17d73b7401d3 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr @@ -1,4 +1,4 @@ -use dep::aztec::prelude::{AztecAddress, NoteGetterOptions, NoteViewerOptions, NoteHeader, NoteInterface, PrivateSet, Map}; +use dep::aztec::prelude::{AztecAddress, NoteGetterOptions, NoteViewerOptions, NoteHeader, NoteInterface, PrivateSet, Map, Point}; use dep::aztec::{ context::{PrivateContext, UnconstrainedContext}, hash::pedersen_hash, protocol_types::constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, @@ -14,8 +14,7 @@ struct BalancesMap { } impl BalancesMap { - pub fn new(context: Context, storage_slot: Field) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + pub fn new(context: Context, storage_slot: Point) -> Self { Self { map: Map::new( context, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr index 9a5a20b7937f..71062700b1ba 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -12,7 +12,8 @@ use dep::types::{ public_data_hint::PublicDataHint, hash::{compute_public_data_tree_index, compute_public_data_tree_value} }, - storage::map::derive_storage_slot_in_map, address::AztecAddress, + slots::{S1 as balances_slot_in_gas_token_contract, derive_storage_slot_in_map}, + address::AztecAddress, point::Point, abis::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_update_request::PublicDataUpdateRequest, public_data_read::PublicDataRead, @@ -413,11 +414,16 @@ fn insert_public_data_update_requests( ) } +// TODO(#7551): rename `leaf_slot` to `storage_index` everywhere and slot in PublicDataTreeLeaf +// and PublicDataTreeLeafPreimage etc. as storage_index as well fn compute_fee_payer_gas_token_balance_leaf_slot(fee_payer: AztecAddress) -> Field { - let balances_slot_in_gas_token_contract = 1; let gas_token = AztecAddress::from_field(GAS_TOKEN_ADDRESS); - let fee_payer_balance_slot_in_gas_token_contract = derive_storage_slot_in_map(balances_slot_in_gas_token_contract, fee_payer); - compute_public_data_tree_index(gas_token, fee_payer_balance_slot_in_gas_token_contract) + // let fee_payer_balance_slot_in_gas_token_contract = derive_storage_slot_in_map(balances_slot_in_gas_token_contract, fee_payer); + // let fee_payer_balance_contract_storage_index = fee_payer_balance_slot_in_gas_token_contract.x; + // compute_public_data_tree_index(gas_token, fee_payer_balance_contract_storage_index) + + // TODO(#7604): nuke the following and uncomment above + compute_public_data_tree_index(gas_token, fee_payer.to_field()) } #[test] diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr index 550669fc8e0a..d93204e67de2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_read.nr @@ -15,7 +15,7 @@ impl PublicDataRead { read_request: StorageRead ) -> PublicDataRead { PublicDataRead { - leaf_slot: compute_public_data_tree_index(contract_address, read_request.storage_slot), + leaf_slot: compute_public_data_tree_index(contract_address, read_request.storage_index), value: compute_public_data_tree_value(read_request.current_value) } } 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 a6a1b25dad3f..35c361d05766 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -125,11 +125,6 @@ global FUNCTION_SELECTOR_NUM_BYTES: Field = 4; global ARGS_HASH_CHUNK_LENGTH: u32 = 16; global ARGS_HASH_CHUNK_COUNT: u32 = 16; global MAX_ARGS_LENGTH: u32 = ARGS_HASH_CHUNK_COUNT * ARGS_HASH_CHUNK_LENGTH; -// The following is used in immutable state variables to compute an initialization slot whose value is used to -// determine whether a given variable has been initialized (by asserting that the value in the slot is 0). -// The initialization slot is computed by adding the constant below to the variable's storage slot. This constant has -// to be large enough so that it's ensured that it doesn't collide with storage slots of other variables. -global INITIALIZATION_SLOT_SEPARATOR: Field = 1000_000_000; global INITIAL_L2_BLOCK_NUM: Field = 1; global BLOB_SIZE_IN_BYTES: Field = 31 * 4096; @@ -179,11 +174,11 @@ global L2_GAS_PER_NOTE_HASH = 32; global L2_GAS_PER_NULLIFIER = 64; // CANONICAL CONTRACT ADDRESSES -global CANONICAL_KEY_REGISTRY_ADDRESS = 0x04c2d010f88e8c238882fbbcbce5c81fdc1dc8ece85e8dbf3f602b4d81ec0351; -global CANONICAL_AUTH_REGISTRY_ADDRESS = 0x27ffa4fb3da8a80b6365315f9798c887474854c71c0720e1c5236861288ce147; -global DEPLOYER_CONTRACT_ADDRESS = 0x2b231c13768709b1ba51c1f86275b47e38dfac16e3d7f242cb578d92a4e2d934; -global REGISTERER_CONTRACT_ADDRESS = 0x1da1c95bfa44d2d94cda61564e0b28a3515f0b2ad4bd8d30d86572f02e2fba00; -global GAS_TOKEN_ADDRESS = 0x06fc7badd50bb8ee32439b52e8874b5a16ddd2aa1d5647ec46b2a0f51356f889; +global CANONICAL_KEY_REGISTRY_ADDRESS = 0x2e6f0d98a8c8b8fde4864c78c1e16c377b60309f69419509288312af7d1ad7b5; +global CANONICAL_AUTH_REGISTRY_ADDRESS = 0x29a9700eee0862817ce14b542ca9dfbf46cd41fa0a974852a1f193576d82abb0; +global DEPLOYER_CONTRACT_ADDRESS = 0x06cdf1d3435d2b934c9213fafe585722ba2e78a78bee88abfbf6ec014efc29bc; +global REGISTERER_CONTRACT_ADDRESS = 0x14221e1cb17ae7c8d9b7a77d2ec65eba85f7ad8f4a9b51f5b1adfbfbeac48b7e; +global GAS_TOKEN_ADDRESS = 0x27e9239c122133f5695dd8a0d2754fa182d976bd0144a9b477849a70de4ad659; // LENGTH OF STRUCTS SERIALIZED TO FIELDS global AZTEC_ADDRESS_LENGTH = 1; @@ -343,9 +338,8 @@ global GENERATOR_INDEX__OVSK_M = 50; global GENERATOR_INDEX__TSK_M = 51; global GENERATOR_INDEX__PUBLIC_KEYS_HASH = 52; global GENERATOR_INDEX__NOTE_NULLIFIER = 53; -global GENERATOR_INDEX__INNER_NOTE_HASH = 54; -global GENERATOR_INDEX__NOTE_CONTENT_HASH = 55; -global GENERATOR_INDEX__SYMMETRIC_KEY: u8 = 56; +global GENERATOR_INDEX__NOTE_CONTENT_HASH: u32 = 54; +global GENERATOR_INDEX__SYMMETRIC_KEY: u8 = 55; global SENDER_SELECTOR: u32 = 0; // "address" actually does not exist in PublicCircuitPublicInputs, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr index 1ca110a1b710..5098d074bd27 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_read.nr @@ -4,7 +4,7 @@ use crate::{ }; struct StorageRead { - storage_slot: Field, + storage_index: Field, current_value: Field, // TODO(dbanks12): use side effects properly in kernel checks counter: u32, @@ -12,14 +12,14 @@ struct StorageRead { impl Eq for StorageRead { fn eq(self, other: Self) -> bool { - (self.storage_slot == other.storage_slot) & (self.current_value == other.current_value) & (self.current_value == other.current_value) + (self.storage_index == other.storage_index) & (self.current_value == other.current_value) & (self.current_value == other.current_value) } } impl Empty for StorageRead { fn empty() -> Self { Self { - storage_slot: 0, + storage_index: 0, current_value: 0, counter: 0, } @@ -34,14 +34,14 @@ impl Hash for StorageRead { impl Serialize for StorageRead { fn serialize(self) -> [Field; CONTRACT_STORAGE_READ_LENGTH] { - [self.storage_slot, self.current_value, self.counter as Field] + [self.storage_index, self.current_value, self.counter as Field] } } impl Deserialize for StorageRead { fn deserialize(serialized: [Field; CONTRACT_STORAGE_READ_LENGTH]) -> Self { Self { - storage_slot: serialized[0], + storage_index: serialized[0], current_value: serialized[1], counter: serialized[2] as u32, } @@ -50,7 +50,7 @@ impl Deserialize for StorageRead { impl StorageRead { pub fn is_empty(self) -> bool { - (self.storage_slot == 0) & (self.current_value == 0) & (self.counter == 0) + (self.storage_index == 0) & (self.current_value == 0) & (self.counter == 0) } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/data/hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/data/hash.nr index c10db9c20098..6e091edf5a2f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/data/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/data/hash.nr @@ -1,10 +1,11 @@ use crate::{address::AztecAddress, constants::GENERATOR_INDEX__PUBLIC_LEAF_INDEX}; -pub fn compute_public_data_tree_index(contract_address: AztecAddress, storage_slot: Field) -> Field { +// TODO(#7551): rename to `compute_storage_index` if we decide upon the naming changes +pub fn compute_public_data_tree_index(contract_address: AztecAddress, storage_index: Field) -> Field { std::hash::pedersen_hash_with_separator( [ contract_address.to_field(), - storage_slot + storage_index ], GENERATOR_INDEX__PUBLIC_LEAF_INDEX ) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/generators.nr b/noir-projects/noir-protocol-circuits/crates/types/src/generators.nr new file mode 100644 index 000000000000..849a24a13cae --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/generators.nr @@ -0,0 +1,30 @@ +// TODO(#7551): This file is a bit of leak from aztec-nr - I placed it in protocol circuits because some of +// the functionality is used by the base rollup. Move the stuff relevant only to aztec-nr to the aztec-nr crate. +use crate::{point::Point, scalar::Scalar}; +use dep::std::embedded_curve_ops::multi_scalar_mul; + +// A set of generators generated with `derive_generators(...)` function from noir::std +global Ga1 = Point { x: 0x30426e64aee30e998c13c8ceecda3a77807dbead52bc2f3bf0eae851b4b710c1, y: 0x113156a068f603023240c96b4da5474667db3b8711c521c748212a15bc034ea6, is_infinite: false }; +global Ga2 = Point { x: 0x2825c79cc6a5cbbeef7d6a8f1b6a12b312aa338440aefeb4396148c89147c049, y: 0x129bfd1da54b7062d6b544e7e36b90736350f6fba01228c41c72099509f5701e, is_infinite: false }; +global Ga3 = Point { x: 0x0edb1e293c3ce91bfc04e3ceaa50d2c541fa9d091c72eb403efb1cfa2cb3357f, y: 0x1341d675fa030ece3113ad53ca34fd13b19b6e9762046734f414824c4d6ade35, is_infinite: false }; +// The following is a generator used to derive base slots. Base slots are slots which were derived using +// the `integer * G_base_slot` formula. These slots are typically assigned by aztec macros to state variables in +// `Storage` struct when compiling a contract. +global G_base_slot = Point { x: 0x041223147b680850dc82e8a55a952d4df20256fe0593d949a9541ca00f0abf15, y: 0x0a8c72e60d0e60f5d804549d48f3044d06140b98ed717a9b532af630c1530791, is_infinite: false }; +// TODO(#7551): dynamically derive and handle nested maps slots +global G_map_slot_layer_1 = Point { x: 0x0e0dad2250583f2a9f0acb04ededf1701b85b0393cae753fe7e14b88af81cb52, y: 0x0973b02c5caac339ee4ad5dab51329920f7bf1b6a07e1dabe5df67040b300962, is_infinite: false }; + +mod test { + use crate::{generators::{Ga1, Ga2, Ga3, G_base_slot, G_map_slot_layer_1}, point::Point}; + use std::hash::derive_generators; + + #[test] +fn test_generators() { + let generators: [Point; 5] = derive_generators("aztec_nr_generators".as_bytes(), 0); + assert_eq(generators[0], Ga1); + assert_eq(generators[1], Ga2); + assert_eq(generators[2], Ga3); + assert_eq(generators[3], G_base_slot); + assert_eq(generators[4], G_map_slot_layer_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 5e98e6f57768..87be98831cd4 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr @@ -11,7 +11,7 @@ use crate::{ MAX_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX }, merkle_tree::root::root_from_sibling_path, messaging::l2_to_l1_message::ScopedL2ToL1Message, - recursion::verification_key::VerificationKey, traits::is_empty, + point::Point, recursion::verification_key::VerificationKey, traits::is_empty, utils::field::field_from_bytes_32_trunc }; use std::hash::{pedersen_hash_with_separator, sha256}; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr b/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr index e70b3103d77a..6465a8f59100 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr @@ -33,4 +33,6 @@ use abis::kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKerne mod recursion; mod data; -mod storage; + +mod generators; +mod slots; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/slots.nr b/noir-projects/noir-protocol-circuits/crates/types/src/slots.nr new file mode 100644 index 000000000000..2f1093acc26a --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/slots.nr @@ -0,0 +1,74 @@ +// TODO(#7551): This file is a bit of leak from aztec-nr - I placed it in protocol circuits because some of +// the functionality is used by the base rollup. Move the stuff relevant only to aztec-nr to the aztec-nr crate. +use crate::{generators::{G_base_slot, G_map_slot_layer_1}, point::Point, scalar::Scalar, traits::ToField}; +use dep::std::{embedded_curve_ops::multi_scalar_mul, hash::from_field_unsafe}; + +global S1 = Point { x: 0x041223147b680850dc82e8a55a952d4df20256fe0593d949a9541ca00f0abf15, y: 0x0a8c72e60d0e60f5d804549d48f3044d06140b98ed717a9b532af630c1530791, is_infinite: false }; +global S2 = Point { x: 0x047df3ac2425b3359b582a5fd340168c9dca8a0c0ef63af0ba97d2d3b3adda52, y: 0x23c4574a895a4ef693c02df3411bca997185cd677ae4af6f6b4a313a14c943a3, is_infinite: false }; +global S3 = Point { x: 0x224de6d3e4c26e053b190add588b5800bc5063bd702caa096602af74fac60f04, y: 0x0d64bbdb6d5e279cb6a056d24b5c2fc0cec91c11ce7034406f5181af13149245, is_infinite: false }; + +// The following is used in public state variables to compute an initialization slot whose value is used to +// determine whether a given variable has been initialized (by asserting that the value in the slot is 0). +// The initialization slot is computed by adding the constant below to the variable's storage slot. This constant has +// to be large enough so that it's ensured that it doesn't collide with storage slots of other variables. +global INITIALIZATION_CONTRACT_STORAGE_INDEX_SEPARATOR: Field = 1000_000_000; + +// The following is a function used to derive base slots. Base slots are slots which were derived using +// the `integer * G_base_slot` formula. These slots are typically assigned by aztec macros to state variables in +// `Storage` struct when compiling a contract. +// TODO(#7551): review all the usage of this function and ensure that the correct generator is used! +pub fn derive_base_slot(slot_preimage: Field) -> Point { + // We check that the slot preimage fits into 128 bits --> this is ok for base slots + slot_preimage.assert_max_bit_size(128); + multi_scalar_mul([G_base_slot], [Scalar { lo: slot_preimage, hi: 0 }]) +} + +pub fn derive_storage_slot_in_map(map_storage_slot: Point, key: K) -> Point where K: ToField { + // We use the unsafe version because the multi_scalar_mul will constraint the scalars. + let key_scalar = from_field_unsafe(key.to_field()); + + // TODO(#7551): we need to handle map nesting here as this could result in collisions! + multi_scalar_mul([G_map_slot_layer_1], [key_scalar]) + map_storage_slot +} + +// TODO(#7551): nuke this func - is only temporarily used in note_interface.rs before we get some AVM compatible +// hash func hashing to a point +pub fn field_to_point(slot_preimage: Field) -> Point { + // We use the unsafe version because the multi_scalar_mul will constraint the scalars. + let slot_preimage_scalar = from_field_unsafe(slot_preimage); + multi_scalar_mul([G_base_slot], [slot_preimage_scalar]) +} + +mod test { + use crate::{address::AztecAddress, point::Point, slots::{derive_base_slot, derive_storage_slot_in_map, S1, S2, S3}}; + + #[test] + fn test_derive_base_slot() { + let derived_s1 = derive_base_slot(1); + let derived_s2 = derive_base_slot(2); + let derived_s3 = derive_base_slot(3); + assert_eq(derived_s1, S1); + assert_eq(derived_s2, S2); + assert_eq(derived_s3, S3); + } + + #[test] + fn test_derive_storage_slot_in_map_matches_typescript() { + let map_slot = Point { + x: 0x132258fb6962c4387ba659d9556521102d227549a386d39f0b22d1890d59c2b5, + y: 0x0a9fa1154d046737fdc9562245b2db9eb87d065301c70995c55ea20693e27b44, + is_infinite: false + }; + let key = AztecAddress::from_field(0x302dbc2f9b50a73283d5fb2f35bc01eae8935615817a0b4219a057b2ba8a5a3f); + + let slot = derive_storage_slot_in_map(map_slot, key); + + // The following 2 values were generated by `map_slot.test.ts` + let slot_x_from_typescript = 0x2e6865f314bd97a5d93eb47180214b9bc61ef070e21f091afd7d441f6bca9565; + let slot_y_from_typescript = 0x0eabe741b8b291f9fefb958de389bc61aa2638eafb3c63f75bd50efd8cc9a0a9; + + let slot_from_typescript = Point { x: slot_x_from_typescript, y: slot_y_from_typescript, is_infinite: false }; + + assert_eq(slot, slot_from_typescript); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/storage/map.nr b/noir-projects/noir-protocol-circuits/crates/types/src/storage/map.nr deleted file mode 100644 index 797cc666bac3..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/storage/map.nr +++ /dev/null @@ -1,5 +0,0 @@ -use crate::{hash::pedersen_hash, traits::ToField}; - -pub fn derive_storage_slot_in_map(storage_slot: Field, key: K) -> Field where K: ToField { - pedersen_hash([storage_slot, key.to_field()], 0) -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/storage/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/storage/mod.nr deleted file mode 100644 index 4dc2dfd9153e..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/storage/mod.nr +++ /dev/null @@ -1 +0,0 @@ -mod map; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr index 55de13e3a3c7..2401b3e7e0d6 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr @@ -102,7 +102,7 @@ impl PublicCallDataBuilder { if i < num_reads { let read_request = StorageRead { // The default storage slot is its index + 1. - storage_slot: (value_offset + i + 1) as Field, + storage_index: (value_offset + i + 1) as Field, // The default value is its index + 999. current_value: (value_offset + i + 999) as Field, counter: i as u32 diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index f6011b705e54..871d26faec80 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -446,6 +446,7 @@ name = "aztec_macros" version = "0.32.0" dependencies = [ "acvm", + "bn254_blackbox_solver", "convert_case 0.6.0", "iter-extended", "noirc_errors", diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index a99a654aeed9..44900884b572 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true [dependencies] acvm.workspace = true +bn254_blackbox_solver.workspace = true noirc_frontend.workspace = true noirc_errors.workspace = true iter-extended.workspace = true diff --git a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs index 40fde39a06f9..e371b6530f3d 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs @@ -29,7 +29,10 @@ fn check_for_compute_note_hash_and_optionally_a_nullifier_definition( _ => false }) && func_data.parameters.0.get(1).is_some_and(|(_, typ, _)| typ.is_field()) - && func_data.parameters.0.get(2).is_some_and(|(_, typ, _)| typ.is_field()) + && func_data.parameters.0.get(2).is_some_and(| (_, typ, _) | match typ { + Type::Struct(struct_typ, _) => struct_typ.borrow().name.0.contents == "Point", + _ => false + }) && func_data.parameters.0.get(3).is_some_and(|(_, typ, _)| typ.is_field()) && func_data.parameters.0.get(4).is_some_and(|(_, typ, _)| typ.is_bool()) // checks if the 6th parameter is an array and contains only fields @@ -178,7 +181,7 @@ fn generate_compute_note_hash_and_optionally_a_nullifier_source( unconstrained fn compute_note_hash_and_optionally_a_nullifier( contract_address: aztec::protocol_types::address::AztecAddress, nonce: Field, - storage_slot: Field, + storage_slot: aztec::protocol_types::point::Point, note_type_id: Field, compute_nullifier: bool, serialized_note: [Field; {}], @@ -210,7 +213,7 @@ fn generate_compute_note_hash_and_optionally_a_nullifier_source( unconstrained fn compute_note_hash_and_optionally_a_nullifier( contract_address: aztec::protocol_types::address::AztecAddress, nonce: Field, - storage_slot: Field, + storage_slot: aztec::protocol_types::point::Point, note_type_id: Field, compute_nullifier: bool, serialized_note: [Field; {}], 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 3233e12ab73a..d38f64b391a2 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -225,13 +225,13 @@ fn generate_note_to_be_bytes( ) -> Result { let function_source = format!( " - fn to_be_bytes(self: {1}, storage_slot: Field) -> [u8; {0}] {{ + fn to_be_bytes(self: {1}, storage_slot: aztec::protocol_types::point::Point) -> [u8; {0}] {{ assert({0} == {2} * 32 + 64, \"Note byte length must be equal to (serialized_length * 32) + 64 bytes\"); let serialized_note = self.serialize_content(); let mut buffer: [u8; {0}] = [0; {0}]; - let storage_slot_bytes = storage_slot.to_be_bytes(32); + let storage_slot_bytes = aztec::utils::point::point_to_bytes(storage_slot); let note_type_id_bytes = {1}::get_note_type_id().to_be_bytes(32); for i in 0..32 {{ @@ -495,18 +495,22 @@ fn generate_note_properties_fn( } // Automatically generate the method to compute the note's content hash as: -// fn compute_note_content_hash(self: NoteType) -> Field { -// aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) +// fn compute_note_content_hash(self: NoteType) -> Point { +// aztec::hash::pedersen_commitment(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) // } // fn generate_compute_note_content_hash( note_type: &String, impl_span: Option, ) -> Result { + // TODO(#7551): Replace the pedersen hash with something that returns a point directly to avoid + // the superfluous call to field_to_point(...). I was trying to use pedersen_commitment for that + // but that is currently not supported by the AVM (but might be soon). let function_source = format!( " - fn compute_note_content_hash(self: {}) -> Field {{ - aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) + fn compute_note_content_hash(self: {}) -> aztec::protocol_types::point::Point {{ + let h = aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH); + aztec::protocol_types::slots::field_to_point(h) }} ", note_type diff --git a/noir/noir-repo/aztec_macros/src/transforms/storage.rs b/noir/noir-repo/aztec_macros/src/transforms/storage.rs index 1c6ef6340708..bb92f1229b7f 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/storage.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/storage.rs @@ -1,8 +1,9 @@ use acvm::acir::AcirField; +use bn254_blackbox_solver::multi_scalar_mul; use noirc_errors::Span; use noirc_frontend::ast::{ BlockExpression, Expression, ExpressionKind, FunctionDefinition, Ident, Literal, NoirFunction, - NoirStruct, Pattern, StatementKind, TypeImpl, UnresolvedType, UnresolvedTypeData, + NoirStruct, PathKind, Pattern, StatementKind, TypeImpl, UnresolvedType, UnresolvedTypeData, }; use noirc_frontend::{ graph::CrateId, @@ -17,7 +18,7 @@ use noirc_frontend::{ }; use crate::{ - chained_path, + chained_dep, chained_path, utils::{ ast_utils::{ call, expression, ident, ident_path, is_custom_attribute, lambda, make_statement, @@ -173,23 +174,23 @@ pub fn generate_storage_field_constructor( /// /// To: /// -/// impl Storage { +/// impl Storage { /// fn init(context: Context) -> Self { /// Storage { -/// a_map: Map::new(context, 0, |context, slot| { +/// a_map: Map::new(context, Point::empty(), |context, slot| { /// SomeStoragePrimitive::new(context, slot) /// }), -/// a_nested_map: Map::new(context, 0, |context, slot| { +/// a_nested_map: Map::new(context, Point::empty(), |context, slot| { /// Map::new(context, slot, |context, slot| { /// SomeStoragePrimitive::new(context, slot) /// }) /// }), -/// a_field: SomeStoragePrimitive::new(context, 0), +/// a_field: SomeStoragePrimitive::new(context, Point::empty()), /// } /// } /// } /// -/// Storage slots are generated as 0 and will be populated using the information from the HIR +/// Storage slots are generated as empty points and will be populated using the information from the HIR /// at a later stage. pub fn generate_storage_implementation( module: &mut SortedModule, @@ -201,11 +202,20 @@ pub fn generate_storage_implementation( .find(|r#struct| r#struct.name.0.contents == *storage_struct_name) .unwrap(); - let slot_zero = expression(ExpressionKind::Literal(Literal::Integer( + let zero_field_expression = expression(ExpressionKind::Literal(Literal::Integer( FieldElement::from(i128::from(0)), false, ))); + let slot_zero = expression(ExpressionKind::constructor(( + chained_dep!("aztec", "protocol_types", "point", "Point"), + vec![ + (ident("x"), zero_field_expression.clone()), + (ident("y"), zero_field_expression.clone()), + (ident("is_infinite"), expression(ExpressionKind::Literal(Literal::Bool(false)))), + ], + ))); + let field_constructors = definition .fields .iter() @@ -397,7 +407,19 @@ pub fn assign_storage_slots( )), }?; - let mut storage_slot: u32 = 1; + // The following is denoted as G_slot in aztec-nr + let base_slot_generator = [ + FieldElement::from_hex( + "0x041223147b680850dc82e8a55a952d4df20256fe0593d949a9541ca00f0abf15", + ) + .unwrap(), + FieldElement::from_hex( + "0x0a8c72e60d0e60f5d804549d48f3044d06140b98ed717a9b532af630c1530791", + ) + .unwrap(), + FieldElement::zero(), + ]; + let mut storage_slot_preimage: u32 = 1; for (index, (_, expr_id)) in storage_constructor_expression.fields.iter().enumerate() { let fields = storage_struct .borrow() @@ -420,11 +442,11 @@ pub fn assign_storage_slots( context.def_interner.expression(&new_call_expression.arguments[1]); let current_storage_slot = match slot_arg_expression { - HirExpression::Literal(HirLiteral::Integer(slot, _)) => Ok(slot.to_u128()), + HirExpression::Constructor(constructor) => Ok(constructor), _ => Err(( AztecMacroError::CouldNotAssignStorageSlots { secondary_message: Some( - "Storage slot argument expression must be a literal integer" + "Storage slot argument expression must be a constructor" .to_string(), ), }, @@ -465,31 +487,111 @@ pub fn assign_storage_slots( file_id, ))?; - let new_storage_slot = if current_storage_slot == 0 { - u128::from(storage_slot) - } else { - current_storage_slot + let coords = current_storage_slot.fields[0..2] + .iter() + .fold(vec![], |mut acc, (_, expr_id)| { + let coord = match context.def_interner.expression(expr_id) { + HirExpression::Literal(HirLiteral::Integer(value, _)) => { + Ok((*expr_id, value)) + } + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some(format!( + "Storage layout field ({}) not found or has an incorrect type", + field_name + )), + }, + file_id, + )), + }; + acc.push(coord); + acc + }) + .into_iter() + .collect::, _>>()?; + let [(x_expr_id, mut x), (y_expr_id, mut y)] = coords[0..2] else { + return Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage slot field does not have the correct number of coordinates" + .to_string(), + ), + }, + file_id, + )); }; + if x == FieldElement::zero() && y == FieldElement::zero() { + // Storage slot point is not populated so we compute it as `storage_slot_preimage * G_slot` + let storage_slot_point = multi_scalar_mul( + &base_slot_generator, + &[FieldElement::from(storage_slot_preimage as u128)], + &[FieldElement::zero()], + ) + .map_err(|_| { + ( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Cannot compute storage slot point".to_string(), + ), + }, + file_id, + ) + })?; + x = storage_slot_point.0; + y = storage_slot_point.1; + } let type_serialized_len = get_storage_serialized_length(&traits, field_type, &context.def_interner) .map_err(|err| (err, file_id))?; - context.def_interner.update_expression(new_call_expression.arguments[1], |expr| { - *expr = HirExpression::Literal(HirLiteral::Integer( - FieldElement::from(new_storage_slot), - false, - )) + context.def_interner.update_expression(x_expr_id, |expr| { + *expr = HirExpression::Literal(HirLiteral::Integer(x, false)) + }); + + context.def_interner.update_expression(y_expr_id, |expr| { + *expr = HirExpression::Literal(HirLiteral::Integer(y, false)) + }); + + let storage_layout_expr = + context.def_interner.expression(&storage_layout_slot_expr_id); + let [x_expr_id, y_expr_id] = match storage_layout_expr { + HirExpression::Constructor(constructor) => { + let coords: Vec<_> = + constructor.fields[0..2].iter().map(|(_, expr_id)| *expr_id).collect(); + if coords.len() >= 2 { + Ok([coords[0], coords[1]]) + } else { + Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage layout storage_slot field does not have the correct number of coordinates" + .to_string(), + ), + }, + file_id, + )) + } + } + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Cannot implement storage layout for contract".to_string(), + ), + }, + file_id, + )), + }?; + + context.def_interner.update_expression(x_expr_id, |expr| { + *expr = HirExpression::Literal(HirLiteral::Integer(x, false)) }); - context.def_interner.update_expression(storage_layout_slot_expr_id, |expr| { - *expr = HirExpression::Literal(HirLiteral::Integer( - FieldElement::from(new_storage_slot), - false, - )) + context.def_interner.update_expression(y_expr_id, |expr| { + *expr = HirExpression::Literal(HirLiteral::Integer(y, false)) }); - storage_slot += type_serialized_len; + storage_slot_preimage += type_serialized_len; } } } @@ -514,7 +616,7 @@ pub fn generate_storage_layout( definition.fields.iter().for_each(|(field_ident, _)| { storable_fields.push(format!("{}: dep::aztec::prelude::Storable", field_ident)); storable_fields_impl - .push(format!("{}: dep::aztec::prelude::Storable {{ slot: 0 }}", field_ident,)); + .push(format!("{}: dep::aztec::prelude::Storable {{ slot: dep::aztec::protocol_types::point::Point {{ x: 0, y: 0, is_infinite: false }} }}", field_ident,)); }); let storage_fields_source = format!( diff --git a/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts b/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts index ea7489409bc8..ead580f80fab 100644 --- a/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts +++ b/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts @@ -18,7 +18,7 @@ import { FunctionSelector, Header } from '@aztec/circuits.js'; import { NoteSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, Point } from '@aztec/foundation/fields'; import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; /** @@ -42,6 +42,7 @@ export function createAztecNodeRpcServer(node: AztecNode) { TxHash, PublicDataWitness, SiblingPath, + Point, }, { EncryptedNoteL2BlockL2Logs, diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index bf5e7016f0ee..6026269df916 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -669,13 +669,17 @@ export class AztecNodeService implements AztecNode { * Aztec's version of `eth_getStorageAt`. * * @param contract - Address of the contract to query. - * @param slot - Slot to query. + * @param contractStorageIndex - The contract storage index to query. * @param blockNumber - The block number at which to get the data or 'latest'. * @returns Storage value at the given contract slot. */ - public async getPublicStorageAt(contract: AztecAddress, slot: Fr, blockNumber: L2BlockNumber): Promise { + public async getPublicStorageAt( + contract: AztecAddress, + contractStorageIndex: Fr, + blockNumber: L2BlockNumber, + ): Promise { const committedDb = await this.#getWorldState(blockNumber); - const leafSlot = computePublicDataTreeLeafSlot(contract, slot); + const leafSlot = computePublicDataTreeLeafSlot(contract, contractStorageIndex); const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); if (!lowLeafResult || !lowLeafResult.alreadyPresent) { diff --git a/yarn-project/aztec.js/src/utils/cheat_codes.ts b/yarn-project/aztec.js/src/utils/cheat_codes.ts index d10bbcef91d7..59b10a748702 100644 --- a/yarn-project/aztec.js/src/utils/cheat_codes.ts +++ b/yarn-project/aztec.js/src/utils/cheat_codes.ts @@ -1,7 +1,7 @@ import { type Note, type PXE } from '@aztec/circuit-types'; -import { type AztecAddress, type EthAddress, Fr } from '@aztec/circuits.js'; +import { type AztecAddress, type EthAddress, Fr, type Point, deriveStorageSlotInMap } from '@aztec/circuits.js'; import { toBigIntBE, toHex } from '@aztec/foundation/bigint-buffer'; -import { keccak256, pedersenHash } from '@aztec/foundation/crypto'; +import { keccak256 } from '@aztec/foundation/crypto'; import { createDebugLogger } from '@aztec/foundation/log'; import fs from 'fs'; @@ -243,10 +243,14 @@ export class AztecCheatCodes { * @param key - The key to lookup in the map * @returns The storage slot of the value in the map */ - public computeSlotInMap(baseSlot: Fr | bigint, key: Fr | bigint | AztecAddress): Fr { - // Based on `at` function in - // aztec3-packages/aztec-nr/aztec/src/state_vars/map.nr - return pedersenHash([new Fr(baseSlot), new Fr(key)]); + public computeSlotInMap( + baseSlot: Point, + key: { + /** Serialize to a field. */ + toField: () => Fr; + }, + ): Point { + return deriveStorageSlotInMap(baseSlot, key); } /** @@ -285,16 +289,16 @@ export class AztecCheatCodes { /** * Loads the value stored at the given slot in the private storage of the given contract. - * @param contract - The address of the contract + * @param contractAddress - The address of the contract * @param owner - The owner for whom the notes are encrypted - * @param slot - The storage slot to lookup + * @param storageSlot - The storage slot to lookup * @returns The notes stored at the given slot */ - public async loadPrivate(owner: AztecAddress, contract: AztecAddress, slot: Fr | bigint): Promise { + public async loadPrivate(owner: AztecAddress, contractAddress: AztecAddress, storageSlot: Point): Promise { const extendedNotes = await this.pxe.getIncomingNotes({ owner, - contractAddress: contract, - storageSlot: new Fr(slot), + contractAddress, + storageSlot, }); return extendedNotes.map(extendedNote => extendedNote.note); } diff --git a/yarn-project/aztec.js/src/wallet/base_wallet.ts b/yarn-project/aztec.js/src/wallet/base_wallet.ts index 11c5b7fc3bbb..fc79788d79ac 100644 --- a/yarn-project/aztec.js/src/wallet/base_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/base_wallet.ts @@ -126,8 +126,8 @@ export abstract class BaseWallet implements Wallet { getNoteNonces(note: ExtendedNote): Promise { return this.pxe.getNoteNonces(note); } - getPublicStorageAt(contract: AztecAddress, storageSlot: Fr): Promise { - return this.pxe.getPublicStorageAt(contract, storageSlot); + getPublicStorageAt(contract: AztecAddress, contractStorageIndex: Fr): Promise { + return this.pxe.getPublicStorageAt(contract, contractStorageIndex); } addNote(note: ExtendedNote): Promise { return this.pxe.addNote(note); diff --git a/yarn-project/builder/src/contract-interface-gen/typescript.ts b/yarn-project/builder/src/contract-interface-gen/typescript.ts index d092f4b06889..5186042bf900 100644 --- a/yarn-project/builder/src/contract-interface-gen/typescript.ts +++ b/yarn-project/builder/src/contract-interface-gen/typescript.ts @@ -196,7 +196,7 @@ function generateStorageLayoutGetter(input: ContractArtifact) { .map( ([name, { slot }]) => `${name}: { - slot: new Fr(${slot.toBigInt()}n), + slot: new Point(new Fr(${slot.x.toBigInt()}n), new Fr(${slot.y.toBigInt()}n), false), }`, ) .join(',\n'); diff --git a/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts b/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts index 6bd10b74e74d..cfd7e1d534da 100644 --- a/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts +++ b/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts @@ -2,7 +2,7 @@ import { FunctionSelector, Header } from '@aztec/circuits.js'; import { EventSelector, NoteSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, Point } from '@aztec/foundation/fields'; import { createJsonRpcClient, defaultFetch } from '@aztec/foundation/json-rpc/client'; import { type AztecNode } from '../../interfaces/aztec-node.js'; @@ -42,6 +42,7 @@ export function createAztecNodeClient(url: string, fetch = defaultFetch): AztecN TxHash, PublicDataWitness, SiblingPath, + Point, }, { EncryptedNoteL2BlockL2Logs, diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.ts index 4973715724f7..e92bab0aa88c 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.ts @@ -297,11 +297,11 @@ export interface AztecNode { * Aztec's version of `eth_getStorageAt`. * * @param contract - Address of the contract to query. - * @param slot - Slot to query. + * @param contractStorageIndex - The index of the storage slot to query. * @param blockNumber - The block number at which to get the data or 'latest'. * @returns Storage value at the given contract slot. */ - getPublicStorageAt(contract: AztecAddress, slot: Fr, blockNumber: L2BlockNumber): Promise; + getPublicStorageAt(contract: AztecAddress, contractStorageIndex: Fr, blockNumber: L2BlockNumber): Promise; /** * Returns the currently committed block header. diff --git a/yarn-project/circuit-types/src/interfaces/pxe.ts b/yarn-project/circuit-types/src/interfaces/pxe.ts index 78f9285bf689..5e7a24432891 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.ts @@ -211,17 +211,18 @@ export interface PXE { getTxEffect(txHash: TxHash): Promise; /** - * Gets the storage value at the given contract storage slot. + * Gets the storage value at the given contract storage index. * * @remarks The storage slot here refers to the slot as it is defined in Noir not the index in the merkle tree. * Aztec's version of `eth_getStorageAt`. * * @param contract - Address of the contract to query. - * @param slot - Slot to query. - * @returns Storage value at the given contract slot. + * @param contractStorageIndex - The index of the storage slot to query. + * @dev Contract storage index along with contract address is a preimage to the leaf index in the public data tree. + * @returns Storage value at the given contract storage index. * @throws If the contract is not deployed. */ - getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise; + getPublicStorageAt(contract: AztecAddress, contractStorageIndex: Fr): Promise; /** * Gets incoming notes of accounts registered in this PXE based on the provided filter. diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_event_log_incoming_body.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_event_log_incoming_body.test.ts index 6e431995d265..fe3e6b752b02 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_event_log_incoming_body.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_event_log_incoming_body.test.ts @@ -55,7 +55,7 @@ describe('encrypt log incoming body', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr', - 'expected_event_body_ciphertext', + 'event_body_ciphertext_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts index 18613e08e6ed..b8995b4e8966 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts @@ -1,4 +1,4 @@ -import { Fr, GrumpkinScalar } from '@aztec/circuits.js'; +import { Fr, GrumpkinScalar, Point } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { NoteSelector } from '@aztec/foundation/abi'; import { updateInlineTestData } from '@aztec/foundation/testing'; @@ -21,7 +21,7 @@ describe('encrypt log incoming body', () => { const viewingPubKey = grumpkin.mul(Grumpkin.generator, viewingSecretKey); const note = Note.random(); - const storageSlot = Fr.random(); + const storageSlot = Point.random(); const noteTypeId = NoteSelector.random(); const body = new EncryptedNoteLogIncomingBody(storageSlot, noteTypeId, note); @@ -34,7 +34,7 @@ describe('encrypt log incoming body', () => { }); it('encrypt a note log incoming body, generate input for noir test', () => { - // The following 2 are arbitrary fixed values - fixed in order to test a match with Noir + // All the values in this test were arbitrarily set and copied over to `incoming_body.nr` const ephSecretKey = new GrumpkinScalar(0x23b3127c127b1f29a7adff5cccf8fb06649e7ca01d9de27b21624098b897babdn); const viewingSecretKey = new GrumpkinScalar(0x1fdd0dd8c99b21af8e00d2d130bdc263b36dadcbea84ac5ec9293a0660deca01n); @@ -42,7 +42,11 @@ describe('encrypt log incoming body', () => { const note = new Note([new Fr(1), new Fr(2), new Fr(3)]); const noteTypeId = new NoteSelector(1); - const storageSlot = new Fr(2); + const storageSlot = new Point( + new Fr(0x1d83b1af3f569775af9e3fdae19b84590245098f46d4a407b5963f313000ce37n), + new Fr(0x1537c632779932ccbc415d91dd70801f88ad410fff48179886d3dce035582d76n), + false, + ); const body = new EncryptedNoteLogIncomingBody(storageSlot, noteTypeId, note); @@ -56,7 +60,7 @@ describe('encrypt log incoming body', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr', - 'expected_note_body_ciphertext', + 'note_body_ciphertext_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.ts index 344b84105ee5..068996029263 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.ts @@ -1,4 +1,4 @@ -import { Fr, type GrumpkinScalar, type PublicKey } from '@aztec/circuits.js'; +import { Fr, type GrumpkinScalar, Point, type PublicKey } from '@aztec/circuits.js'; import { NoteSelector } from '@aztec/foundation/abi'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -6,7 +6,7 @@ import { Note } from '../payload.js'; import { EncryptedLogIncomingBody } from './encrypted_log_incoming_body.js'; export class EncryptedNoteLogIncomingBody extends EncryptedLogIncomingBody { - constructor(public storageSlot: Fr, public noteTypeId: NoteSelector, public note: Note) { + constructor(public storageSlot: Point, public noteTypeId: NoteSelector, public note: Note) { super(); } @@ -18,7 +18,7 @@ export class EncryptedNoteLogIncomingBody extends EncryptedLogIncomingBody { public toBuffer(): Buffer { const noteBufferWithoutLength = this.note.toBuffer().subarray(4); // Note: We serialize note type to field first because that's how it's done in Noir - return serializeToBuffer(this.storageSlot, this.noteTypeId.toField(), noteBufferWithoutLength); + return serializeToBuffer(this.storageSlot.toCompressedBuffer(), this.noteTypeId.toField(), noteBufferWithoutLength); } /** @@ -29,7 +29,7 @@ export class EncryptedNoteLogIncomingBody extends EncryptedLogIncomingBody { */ public static fromBuffer(buf: Buffer): EncryptedNoteLogIncomingBody { const reader = BufferReader.asReader(buf); - const storageSlot = Fr.fromBuffer(reader); + const storageSlot = Point.fromCompressedBuffer(reader.readBytes(Point.COMPRESSED_SIZE_IN_BYTES)); const noteTypeId = NoteSelector.fromField(Fr.fromBuffer(reader)); // 2 Fields (storage slot and note type id) are not included in the note buffer diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.test.ts index e4de515d00b8..35b89c559e03 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.test.ts @@ -56,7 +56,7 @@ describe('encrypt log outgoing body', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr', - 'expected_outgoing_body_ciphertext', + 'outgoing_body_ciphertext_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts b/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts index 1f53c1e00c18..25fe47a6aa20 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts @@ -1,6 +1,6 @@ import { AztecAddress, type GrumpkinScalar, type KeyValidationRequest, type PublicKey } from '@aztec/circuits.js'; import { NoteSelector } from '@aztec/foundation/abi'; -import { Fr } from '@aztec/foundation/fields'; +import { Point } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { EncryptedNoteLogIncomingBody } from './encrypted_log_incoming_body/index.js'; @@ -25,7 +25,7 @@ export class L1NotePayload extends L1Payload { /** * Storage slot of the underlying note. */ - public storageSlot: Fr, + public storageSlot: Point, /** * Type identifier for the underlying note, required to determine how to compute its hash and nullifier. */ @@ -44,7 +44,7 @@ export class L1NotePayload extends L1Payload { return new L1NotePayload( reader.readObject(Note), reader.readObject(AztecAddress), - Fr.fromBuffer(reader), + Point.fromCompressedBuffer(reader.readBytes(Point.COMPRESSED_SIZE_IN_BYTES)), reader.readObject(NoteSelector), ); } @@ -54,7 +54,7 @@ export class L1NotePayload extends L1Payload { * @returns Buffer representation of the L1NotePayload object. */ toBuffer() { - return serializeToBuffer([this.note, this.contractAddress, this.storageSlot, this.noteTypeId]); + return serializeToBuffer([this.note, this.contractAddress, this.storageSlot.toCompressedBuffer(), this.noteTypeId]); } /** @@ -63,7 +63,7 @@ export class L1NotePayload extends L1Payload { * @returns A random L1NotePayload object. */ static random(contract = AztecAddress.random()) { - return new L1NotePayload(Note.random(), contract, Fr.random(), NoteSelector.random()); + return new L1NotePayload(Note.random(), contract, Point.random(), NoteSelector.random()); } public encrypt(ephSk: GrumpkinScalar, recipient: AztecAddress, ivpk: PublicKey, ovKeys: KeyValidationRequest) { diff --git a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts index af8397ff49da..92b9818b6f26 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts @@ -1,7 +1,7 @@ import { AztecAddress, KeyValidationRequest, computeOvskApp, derivePublicKeyFromSecretKey } from '@aztec/circuits.js'; import { EventSelector, NoteSelector } from '@aztec/foundation/abi'; import { pedersenHash } from '@aztec/foundation/crypto'; -import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; +import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; import { updateInlineTestData } from '@aztec/foundation/testing'; import { EncryptedL2Log } from '../encrypted_l2_log.js'; @@ -57,8 +57,14 @@ describe('L1 Note Payload', () => { }); it('encrypted tagged log matches Noir', () => { + // All the values in this test were arbitrarily set and copied over to `payload.nr` const contract = AztecAddress.fromString('0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04'); - const storageSlot = new Fr(0x0fe46be583b71f4ab5b70c2657ff1d05cccf1d292a9369628d1a194f944e6599n); + // Storage slot point (as well as all the other values) was randomly generated + const storageSlot = new Point( + new Fr(0x1d83b1af3f569775af9e3fdae19b84590245098f46d4a407b5963f313000ce37n), + new Fr(0x1537c632779932ccbc415d91dd70801f88ad410fff48179886d3dce035582d76n), + false, + ); const noteValue = new Fr(0x301640ceea758391b2e161c92c0513f129020f4125256afdae2646ce31099f5cn); const noteTypeId = new NoteSelector(0); @@ -82,7 +88,7 @@ describe('L1 Note Payload', () => { const encrypted = taggedLog.encrypt(ephSk, recipientAddress, ivpk, ovKeys).toString('hex'); expect(encrypted).toMatchInlineSnapshot( - `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa79f357275ed3983136f963253ad9beae147bb8d4ff52b6f53db957c440cf4fdd8003e6ce87650578cd7b96f3080ec6e5c2ecd07e28342cd006753d95a3c8a06acf6815cac45494d419312e71423d9b4fd48f220392d0b02eb1860f4e0213d97e188adb228027de514dc521cbf938589012df3e58c73a5969a601678dfedd5b6fcc008842b1538f37490b64b101edede3ccd93d635293e3510937548a9dc7dd0d22d41e92857588cedc8a109565280bf3304c3f36466f03681b0748b491b62de01f3c748eed5425b9fb78f24675e053e320dd9a14f1ee729e46d8bf377a63625fac106431d94b9993a40d2a4dba550234b6db10ea8886915eb3e9f473df5c1eaa964a508de9def29dddf43503dfc361b64016802793e2917840f7c7815c67197ac2aa140f0a6cd50a93abf6f82373a8d1a617672d845cfd4e3fac7154890552b4cd51c848610dd697052ee723d2490b3b244c6a2d4556474ba83e821e565fb05fb"`, + `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa77e4f33ba2f47fdac6370f13bc5f16bbae857bbe6ab3ee4ea2a339192eef22a47ce0df4426fc314cb6294ccf291b79c1d8d362cdcc223e51020ccd3318e7052ca74f1fe922ad914bd46e4b6abcd681b63ab1c5bf4151e82f00548ae7c61c59df8cccb8cabb59882a9b32934ad2d2bc60198489ef90b2909a0304e7b84cb8cd70d16c958ff8b9a4c143f867d6cefd03f3b21758be1b80040991583cc6f29541790def5c80cea0b300add14fc267a28f942f8c5c6d14f143b42c5d7101291e4ef7c51436731c43ae4c340c7f3b870ad1dc4d74dd9555295714cc95d5f94255fdee9d29601b61c843b949c8124e637c7952458328fcc9920681d89131fb289759d140880e6fa4b2c368002f1d09907d6fc39984b4539f8b39c915ca50d7435212ab7e74dcbaa7d14793088c25e8bcf94daf881e781bf2def6c3c225275bab4c74da9d5ee2f2500280c6ed8af173b5175a41850f1287eb3e64e914b66cc0c1e92f0de"`, ); const byteArrayString = `[${encrypted.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))}]`; @@ -90,7 +96,7 @@ describe('L1 Note Payload', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr', - 'expected_encrypted_note_log', + 'encrypted_note_log_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index f14f28d93b49..bc7547ca9da9 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -25,7 +25,7 @@ import { type ContractArtifact, NoteSelector } from '@aztec/foundation/abi'; import { makeTuple } from '@aztec/foundation/array'; import { times } from '@aztec/foundation/collection'; import { randomBytes } from '@aztec/foundation/crypto'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, Point } from '@aztec/foundation/fields'; import { type ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts'; import { EncryptedNoteTxL2Logs, EncryptedTxL2Logs, Note, UnencryptedTxL2Logs } from './logs/index.js'; @@ -230,7 +230,7 @@ export const randomExtendedNote = ({ owner = AztecAddress.random(), contractAddress = AztecAddress.random(), txHash = randomTxHash(), - storageSlot = Fr.random(), + storageSlot = Point.random(), noteTypeId = NoteSelector.random(), }: Partial = {}) => { return new ExtendedNote(note, owner, contractAddress, storageSlot, noteTypeId, txHash); diff --git a/yarn-project/circuit-types/src/notes/extended_note.ts b/yarn-project/circuit-types/src/notes/extended_note.ts index bf91e2dc49dd..9ad89042824d 100644 --- a/yarn-project/circuit-types/src/notes/extended_note.ts +++ b/yarn-project/circuit-types/src/notes/extended_note.ts @@ -1,4 +1,4 @@ -import { AztecAddress, Fr } from '@aztec/circuits.js'; +import { AztecAddress, Point } from '@aztec/circuits.js'; import { NoteSelector } from '@aztec/foundation/abi'; import { BufferReader } from '@aztec/foundation/serialize'; @@ -17,7 +17,7 @@ export class ExtendedNote { /** The contract address this note is created in. */ public contractAddress: AztecAddress, /** The specific storage location of the note on the contract. */ - public storageSlot: Fr, + public storageSlot: Point, /** The type identifier of the note on the contract. */ public noteTypeId: NoteSelector, /** The hash of the tx the note was created in. */ @@ -41,7 +41,7 @@ export class ExtendedNote { const note = Note.fromBuffer(reader); const owner = AztecAddress.fromBuffer(reader); const contractAddress = AztecAddress.fromBuffer(reader); - const storageSlot = Fr.fromBuffer(reader); + const storageSlot = reader.readObject(Point); const noteTypeId = reader.readObject(NoteSelector); const txHash = new TxHash(reader.readBytes(TxHash.SIZE)); diff --git a/yarn-project/circuit-types/src/notes/incoming_notes_filter.ts b/yarn-project/circuit-types/src/notes/incoming_notes_filter.ts index 27ff8dcfb41c..5bc28ead25ec 100644 --- a/yarn-project/circuit-types/src/notes/incoming_notes_filter.ts +++ b/yarn-project/circuit-types/src/notes/incoming_notes_filter.ts @@ -1,4 +1,4 @@ -import { type AztecAddress, type Fr } from '@aztec/circuits.js'; +import { type AztecAddress, type Fr, type Point } from '@aztec/circuits.js'; import { type TxHash } from '../tx/tx_hash.js'; import { type NoteStatus } from './note_status.js'; @@ -13,7 +13,7 @@ export type IncomingNotesFilter = { /** The contract address the note belongs to. */ contractAddress?: AztecAddress; /** The specific storage location of the note on the contract. */ - storageSlot?: Fr; + storageSlot?: Point; /** The owner of the note (whose public key was used to encrypt the note). */ owner?: AztecAddress; /** The status of the note. Defaults to 'ACTIVE'. */ diff --git a/yarn-project/circuit-types/src/notes/outgoing_notes_filter.ts b/yarn-project/circuit-types/src/notes/outgoing_notes_filter.ts index 888c3fc6ef11..8a085326f74e 100644 --- a/yarn-project/circuit-types/src/notes/outgoing_notes_filter.ts +++ b/yarn-project/circuit-types/src/notes/outgoing_notes_filter.ts @@ -1,4 +1,4 @@ -import { type AztecAddress, type Fr } from '@aztec/circuits.js'; +import { type AztecAddress, type Point } from '@aztec/circuits.js'; import { type TxHash } from '../tx/tx_hash.js'; @@ -12,7 +12,7 @@ export type OutgoingNotesFilter = { /** The contract address the note belongs to. */ contractAddress?: AztecAddress; /** The specific storage location of the note on the contract. */ - storageSlot?: Fr; + storageSlot?: Point; /** The owner of the note (whose public key was used to encrypt the note). */ owner?: AztecAddress; }; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index c739e764449b..f9e3d96dc629 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -81,7 +81,6 @@ export const FUNCTION_SELECTOR_NUM_BYTES = 4; export const ARGS_HASH_CHUNK_LENGTH = 16; export const ARGS_HASH_CHUNK_COUNT = 16; export const MAX_ARGS_LENGTH = 256; -export const INITIALIZATION_SLOT_SEPARATOR = 1000000000; export const INITIAL_L2_BLOCK_NUM = 1; export const BLOB_SIZE_IN_BYTES = 126976; export const MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 20000; @@ -110,13 +109,13 @@ 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 = - 2153455745675440165069577621832684870696142028027528497509357256345838682961n; + 21002604897410093708477100201638914385100863925743623823186725421598769862581n; export const CANONICAL_AUTH_REGISTRY_ADDRESS = - 18091885756106795278141309801070173692350235742979924147720536894670507925831n; -export const DEPLOYER_CONTRACT_ADDRESS = 19511485909966796736993840362353440247573331327062358513665772226446629198132n; + 18844197343959337477226781811009542647774288930267535903153059057819715808176n; +export const DEPLOYER_CONTRACT_ADDRESS = 3077749756291741958160703262435693846649629333047756518291243914166007507388n; export const REGISTERER_CONTRACT_ADDRESS = - 13402924717071282069537366635406026232165444473509746327951838324587448220160n; -export const GAS_TOKEN_ADDRESS = 3159976153131520272419617514531889581796079438158800470341967144801191524489n; + 9106537597827539209583757446796074130561720707558015678434966723134574857086n; +export const GAS_TOKEN_ADDRESS = 18052122229623850597530611627473354790534583502914175510126040829737657095769n; export const AZTEC_ADDRESS_LENGTH = 1; export const GAS_FEES_LENGTH = 2; export const GAS_LENGTH = 2; @@ -266,7 +265,6 @@ export enum GeneratorIndex { TSK_M = 51, PUBLIC_KEYS_HASH = 52, NOTE_NULLIFIER = 53, - INNER_NOTE_HASH = 54, - NOTE_CONTENT_HASH = 55, - SYMMETRIC_KEY = 56, + NOTE_CONTENT_HASH = 54, + SYMMETRIC_KEY = 55, } diff --git a/yarn-project/circuits.js/src/hash/hash.ts b/yarn-project/circuits.js/src/hash/hash.ts index 3d78b7790716..0b5b00f6fdbd 100644 --- a/yarn-project/circuits.js/src/hash/hash.ts +++ b/yarn-project/circuits.js/src/hash/hash.ts @@ -1,12 +1,14 @@ import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { padArrayEnd } from '@aztec/foundation/collection'; import { pedersenHash, pedersenHashBuffer } from '@aztec/foundation/crypto'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, type Point } from '@aztec/foundation/fields'; import { numToUInt8, numToUInt16BE, numToUInt32BE } from '@aztec/foundation/serialize'; import chunk from 'lodash.chunk'; +import { Grumpkin } from '../barretenberg/index.js'; import { ARGS_HASH_CHUNK_COUNT, ARGS_HASH_CHUNK_LENGTH, GeneratorIndex, MAX_ARGS_LENGTH } from '../constants.gen.js'; +import { deriveBaseSlot } from '../storage_slots/index.js'; import { VerificationKey } from '../structs/index.js'; /** @@ -52,22 +54,26 @@ export function siloNoteHash(contract: AztecAddress, uniqueNoteHash: Fr): Fr { } /** - * Computes a note content hash. + * Computes a note content hash as is done by the default implementation injected by macros. * @param noteContent - The note content (e.g. note.items). * @returns A note content hash. + * TODO(#7551): Not sure about having this function here given that different notes have different ways + * of computing the note content hash. */ -export function computeNoteContentHash(noteContent: Fr[]): Fr { - return pedersenHash(noteContent, GeneratorIndex.NOTE_CONTENT_HASH); +export function computeNoteContentHash(noteContent: Fr[]): Point { + const h = pedersenHash(noteContent, GeneratorIndex.NOTE_CONTENT_HASH); + return deriveBaseSlot(h); } /** * Computes an inner note hash, given a storage slot and a note hash. * @param storageSlot - The storage slot. - * @param noteHash - The note hash. + * @param noteContentHash - The hash of note content. * @returns An inner note hash. */ -export function computeInnerNoteHash(storageSlot: Fr, noteHash: Fr): Fr { - return pedersenHash([storageSlot, noteHash], GeneratorIndex.INNER_NOTE_HASH); +export function computeInnerNoteHash(storageSlot: Point, noteContentHash: Point): Point { + const grumpkin = new Grumpkin(); + return grumpkin.add(storageSlot, noteContentHash); } /** @@ -105,12 +111,12 @@ export function computePublicDataTreeValue(value: Fr): Fr { /** * Computes a public data tree index from contract address and storage slot. * @param contractAddress - Contract where insertion is occurring. - * @param storageSlot - Storage slot where insertion is occurring. + * @param contractStorageIndex - Contract storage index where insertion is occurring. * @returns Public data tree index computed from contract address and storage slot. */ -export function computePublicDataTreeLeafSlot(contractAddress: AztecAddress, storageSlot: Fr): Fr { - return pedersenHash([contractAddress, storageSlot], GeneratorIndex.PUBLIC_LEAF_INDEX); +export function computePublicDataTreeLeafSlot(contractAddress: AztecAddress, contractStorageIndex: Fr): Fr { + return pedersenHash([contractAddress, contractStorageIndex], GeneratorIndex.PUBLIC_LEAF_INDEX); } /** diff --git a/yarn-project/circuits.js/src/index.ts b/yarn-project/circuits.js/src/index.ts index 3b7dba0dfefb..73c209774b80 100644 --- a/yarn-project/circuits.js/src/index.ts +++ b/yarn-project/circuits.js/src/index.ts @@ -4,6 +4,7 @@ export * from './hints/index.js'; export * from './interfaces/index.js'; export * from './keys/index.js'; export * from './structs/index.js'; +export * from './storage_slots/index.js'; export * from './types/index.js'; export * from './utils/index.js'; export * from './merkle/index.js'; diff --git a/yarn-project/circuits.js/src/storage_slots/generators.ts b/yarn-project/circuits.js/src/storage_slots/generators.ts new file mode 100644 index 000000000000..d547cbb66829 --- /dev/null +++ b/yarn-project/circuits.js/src/storage_slots/generators.ts @@ -0,0 +1,24 @@ +import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; + +import { Grumpkin } from '../barretenberg/index.js'; + +// TODO(#7551): this is copied over from noir-projects/aztec-nr/aztec/src/generators.nr --> test that we get a match with noir + +// The following generator is used to compute an inner note hash - used to silo note content hash with with storage slot +export const G_BASE_SLOT = new Point( + new Fr(0x041223147b680850dc82e8a55a952d4df20256fe0593d949a9541ca00f0abf15n), + new Fr(0x0a8c72e60d0e60f5d804549d48f3044d06140b98ed717a9b532af630c1530791n), + false, +); +export const G_MAP_SLOT_LAYER_1 = new Point( + new Fr(0x0e0dad2250583f2a9f0acb04ededf1701b85b0393cae753fe7e14b88af81cb52n), + new Fr(0x0973b02c5caac339ee4ad5dab51329920f7bf1b6a07e1dabe5df67040b300962n), + false, +); + +// Derive a base slot (using the base generator) from a slot preimage +export function deriveBaseSlot(slotPreimage: number | bigint | Fr): Point { + const grumpkin = new Grumpkin(); + const slotPreimageScalar = new GrumpkinScalar(new Fr(slotPreimage).toBigInt()); + return grumpkin.mul(G_BASE_SLOT, new GrumpkinScalar(slotPreimageScalar)); +} diff --git a/yarn-project/circuits.js/src/storage_slots/index.ts b/yarn-project/circuits.js/src/storage_slots/index.ts new file mode 100644 index 000000000000..9e7db44adc90 --- /dev/null +++ b/yarn-project/circuits.js/src/storage_slots/index.ts @@ -0,0 +1,2 @@ +export * from './generators.js'; +export * from './map_slot.js'; diff --git a/yarn-project/circuits.js/src/storage_slots/map_slot.test.ts b/yarn-project/circuits.js/src/storage_slots/map_slot.test.ts new file mode 100644 index 000000000000..6263022a6177 --- /dev/null +++ b/yarn-project/circuits.js/src/storage_slots/map_slot.test.ts @@ -0,0 +1,35 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr, Point } from '@aztec/foundation/fields'; +import { updateInlineTestData } from '@aztec/foundation/testing'; + +import { deriveStorageSlotInMap } from './map_slot.js'; + +describe('Map slot', () => { + it('derived map slot matches Noir', () => { + const mapSlot = new Point( + new Fr(0x132258fb6962c4387ba659d9556521102d227549a386d39f0b22d1890d59c2b5n), + new Fr(0x0a9fa1154d046737fdc9562245b2db9eb87d065301c70995c55ea20693e27b44n), + false, + ); + + const key = AztecAddress.fromString('0x302dbc2f9b50a73283d5fb2f35bc01eae8935615817a0b4219a057b2ba8a5a3f'); + + const slot = deriveStorageSlotInMap(mapSlot, key); + + expect(slot.toString()).toMatchInlineSnapshot( + `"0x2e6865f314bd97a5d93eb47180214b9bc61ef070e21f091afd7d441f6bca95650eabe741b8b291f9fefb958de389bc61aa2638eafb3c63f75bd50efd8cc9a0a9"`, + ); + + // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data + updateInlineTestData( + 'noir-projects/noir-protocol-circuits/crates/types/src/slots.nr', + 'slot_x_from_typescript', + slot.x.toString(), + ); + updateInlineTestData( + 'noir-projects/noir-protocol-circuits/crates/types/src/slots.nr', + 'slot_y_from_typescript', + slot.y.toString(), + ); + }); +}); diff --git a/yarn-project/circuits.js/src/storage_slots/map_slot.ts b/yarn-project/circuits.js/src/storage_slots/map_slot.ts new file mode 100644 index 000000000000..2fb1cde09a5f --- /dev/null +++ b/yarn-project/circuits.js/src/storage_slots/map_slot.ts @@ -0,0 +1,22 @@ +import { G_MAP_SLOT_LAYER_1 } from '@aztec/circuits.js'; +import { Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { type Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields'; + +/** + * Computes the resulting storage slot for an entry in a map. + * @param mapSlot - The slot of the map within state. + * @param key - The key of the map. + * @returns The slot in the contract storage where the value is stored. + * TODO(#7551): Test that it matches Noir. + */ +export function deriveStorageSlotInMap( + mapSlot: Point, + key: { + /** Serialize to a field. */ + toField: () => Fr; + }, +): Point { + const grumpkin = new Grumpkin(); + const scalar = new GrumpkinScalar(key.toField().toBigInt()); + return grumpkin.add(grumpkin.mul(G_MAP_SLOT_LAYER_1, scalar), mapSlot); +} diff --git a/yarn-project/circuits.js/src/structs/contract_storage_read.ts b/yarn-project/circuits.js/src/structs/contract_storage_read.ts index 56f0f95aa1dd..8ec4c374d040 100644 --- a/yarn-project/circuits.js/src/structs/contract_storage_read.ts +++ b/yarn-project/circuits.js/src/structs/contract_storage_read.ts @@ -14,6 +14,7 @@ export class ContractStorageRead { constructor( /** * Storage slot we are reading from. + * TODO(#7551): rename to contract storage index once we decide upon the naming changes. */ public readonly storageSlot: Fr, /** diff --git a/yarn-project/circuits.js/src/structs/public_data_read_request.ts b/yarn-project/circuits.js/src/structs/public_data_read_request.ts index 4244f93ff9ea..4c407012a71b 100644 --- a/yarn-project/circuits.js/src/structs/public_data_read_request.ts +++ b/yarn-project/circuits.js/src/structs/public_data_read_request.ts @@ -9,6 +9,7 @@ export class PublicDataRead { constructor( /** * Index of the leaf in the public data tree. + * TODO(#7551): Rename to something like public data tree index. */ public readonly leafSlot: Fr, /** diff --git a/yarn-project/cli/src/cmds/pxe/add_note.ts b/yarn-project/cli/src/cmds/pxe/add_note.ts index 990027573dc8..cdf5a0647f32 100644 --- a/yarn-project/cli/src/cmds/pxe/add_note.ts +++ b/yarn-project/cli/src/cmds/pxe/add_note.ts @@ -1,4 +1,4 @@ -import { type AztecAddress, type Fr, type NoteSelector } from '@aztec/aztec.js'; +import { type AztecAddress, type NoteSelector, type Point } from '@aztec/aztec.js'; import { ExtendedNote, Note, type TxHash } from '@aztec/circuit-types'; import { type DebugLogger } from '@aztec/foundation/log'; @@ -8,7 +8,7 @@ import { parseFields } from '../../utils/commands.js'; export async function addNote( address: AztecAddress, contractAddress: AztecAddress, - storageSlot: Fr, + storageSlot: Point, noteTypeId: NoteSelector, txHash: TxHash, noteFields: string[], diff --git a/yarn-project/cli/src/cmds/pxe/index.ts b/yarn-project/cli/src/cmds/pxe/index.ts index b9a69eca05c8..6ab26f102370 100644 --- a/yarn-project/cli/src/cmds/pxe/index.ts +++ b/yarn-project/cli/src/cmds/pxe/index.ts @@ -1,4 +1,4 @@ -import { Fr, PublicKeys } from '@aztec/circuits.js'; +import { Fr, Point, PublicKeys } from '@aztec/circuits.js'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { type Command } from 'commander'; @@ -325,12 +325,14 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL .description('Adds a note to the database in the PXE.') .argument('
', 'The Aztec address of the note owner.', parseAztecAddress) .argument('', 'Aztec address of the contract.', parseAztecAddress) - .argument('', 'The storage slot of the note.', parseField) + .argument('', 'The storage slot of the note.', parseField) + .argument('', 'The storage slot of the note.', parseField) .argument('', 'The type ID of the note.', parseField) .argument('', 'The tx hash of the tx containing the note.', parseTxHash) .requiredOption('-n, --note [note...]', 'The members of a Note serialized as hex strings.', []) .addOption(pxeOption) - .action(async (address, contractAddress, storageSlot, noteTypeId, txHash, options) => { + .action(async (address, contractAddress, storageSlotX, storageSlotY, noteTypeId, txHash, options) => { + const storageSlot = new Point(storageSlotX, storageSlotY, false); const { addNote } = await import('./add_note.js'); await addNote( address, diff --git a/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts b/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts index 1d465504b8bc..6bbd6215923f 100644 --- a/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts @@ -24,13 +24,17 @@ const pageLogger = createDebugLogger('aztec:e2e_aztec_browser.js:web:page'); * 1) Build the whole repository, * 2) go to `yarn-project/end-to-end` and build the web packed package with `yarn build:web`, * 3) start anvil: `anvil`, - * 4) if you intend to use a remotely running environment then export the URL of your PXE e.g. `export PXE_URL='http://localhost:8080'` + * 4) if you intend to use a remotely running environment then export the URL of your PXE e.g. + * `export PXE_URL='http://localhost:8080'` * 5) go to `yarn-project/end-to-end` and run the test: `yarn test aztec_js_browser` - * 6) If you get dependency error run `apt install libssn3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libxdamage1 libxkbcommon0 libpango-1.0-0 libcairo2`. + * 6) If you get dependency error run `apt install libnss3 libatk1.0-0t64 libatk-bridge2.0-0t64 libcups2t64 libxdamage1 libxkbcommon0 libpango-1.0-0 libcairo2 libasound2t64`. * * NOTE 1: If you see the logs spammed with unexpected logs there is probably a chrome process with a webpage - * unexpectedly running in the background. Kill it with `killall chrome` + * unexpectedly running in the background. Kill it with `killall chrome` * NOTE 2: Don't forget to run `yarn build:web` once you make changes! + * NOTE 3: The test serializes token contract artifact to and from buffer. If you introduce a new type in the artifact + * you have to register it on `TypeRegistry` class, implement fromJSON method just like TypeRegistry requires + * and add a case in `contractArtifactFromBuffer(...)` function. */ const setupApp = async () => { diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index 9fd48ad46079..1ed825a5aa8f 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -13,6 +13,7 @@ import { retryUntil, sleep, } from '@aztec/aztec.js'; +import { deriveBaseSlot } from '@aztec/circuits.js'; import { ChildContract, TestContract, TokenContract } from '@aztec/noir-contracts.js'; import { expect, jest } from '@jest/globals'; @@ -180,8 +181,10 @@ describe('e2e_2_pxes', () => { await retryUntil(isServerSynchronized, 'server sync', 10); }; - const getChildStoredValue = (child: { address: AztecAddress }, pxe: PXE) => - pxe.getPublicStorageAt(child.address, new Fr(1)); + const getChildStoredValue = (child: { address: AztecAddress }, pxe: PXE) => { + const contractStorageIndex = deriveBaseSlot(1).x; + return pxe.getPublicStorageAt(child.address, contractStorageIndex); + }; it('user calls a public function on a contract deployed by a different user using a different PXE', async () => { const childCompleteAddress = await deployChildContractViaServerA(); @@ -347,7 +350,7 @@ describe('e2e_2_pxes', () => { const testContract = await TestContract.deploy(walletA).send().deployed(); // 2. Create a note - const noteStorageSlot = 10; + const noteStorageSlot = deriveBaseSlot(10).toNoirStruct(); const noteValue = 5; let note: ExtendedNote; { diff --git a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts index b1492648f86a..c94ec8369f8a 100644 --- a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts @@ -12,6 +12,7 @@ import { type PXE, type Wallet, } from '@aztec/aztec.js'; +import { deriveBaseSlot } from '@aztec/circuits.js'; import { deriveSigningKey } from '@aztec/circuits.js/keys'; import { randomBytes } from '@aztec/foundation/crypto'; import { ChildContract } from '@aztec/noir-contracts.js/Child'; @@ -32,6 +33,10 @@ function itShouldBehaveLikeAnAccountContract( let logger: DebugLogger; let teardown: () => Promise; + // s1 is the first base slot - base slot is a slot which was derived using the same procedure as is done by aztec + // macros - the procedure is `integer * G_slot` where G_slot is a generator used for base slots + const s1 = deriveBaseSlot(1); + beforeEach(async () => { ({ logger, pxe, teardown } = await setup(0)); secretKey = Fr.random(); @@ -51,7 +56,7 @@ function itShouldBehaveLikeAnAccountContract( it('calls a public function', async () => { logger.info('Calling public function...'); await child.methods.pub_inc_value(42).send().wait({ interval: 0.1 }); - const storedValue = await pxe.getPublicStorageAt(child.address, new Fr(1)); + const storedValue = await pxe.getPublicStorageAt(child.address, s1.x); expect(storedValue).toEqual(new Fr(42n)); }); 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 206e19e00b4d..b1773d718565 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 @@ -236,7 +236,7 @@ describe('e2e_block_building', () => { }); describe('logs in nested calls are ordered as expected', () => { - // This test was originally writted for e2e_nested, but it was refactored + // This test was originally written for e2e_nested, but it was refactored // to not use TestContract. let testContract: TestContract; diff --git a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts index 562a7fef0da6..7dd53b740989 100644 --- a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts +++ b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts @@ -9,6 +9,7 @@ import { type Wallet, computeSecretHash, } from '@aztec/aztec.js'; +import { deriveBaseSlot } from '@aztec/circuits.js'; import { RollupAbi } from '@aztec/l1-artifacts'; import { TestContract, TokenContract } from '@aztec/noir-contracts.js'; @@ -198,7 +199,7 @@ describe('e2e_cheat_codes', () => { }); it('load public', async () => { - expect(await cc.aztec.loadPublic(token.address, 1n)).toEqual(admin.address.toField()); + expect(await cc.aztec.loadPublic(token.address, deriveBaseSlot(1n).x)).toEqual(admin.address.toField()); }); it('load public returns 0 for non existent value', async () => { @@ -207,7 +208,7 @@ describe('e2e_cheat_codes', () => { }); it('load private works as expected for no notes', async () => { - const notes = await cc.aztec.loadPrivate(admin.address, token.address, 5n); + const notes = await cc.aztec.loadPrivate(admin.address, token.address, deriveBaseSlot(5n)); const values = notes.map(note => note.items[0]); const balance = values.reduce((sum, current) => sum + current.toBigInt(), 0n); expect(balance).toEqual(0n); diff --git a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index 0de4fac6276f..4fb9cc412dea 100644 --- a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts +++ b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts @@ -192,7 +192,7 @@ describe('e2e_crowdfunding_and_claim', () => { // eslint-disable-next-line camelcase contract_address: extendedNote.contractAddress, // eslint-disable-next-line camelcase - storage_slot: extendedNote.storageSlot, + storage_slot: extendedNote.storageSlot.toNoirStruct(), // TODO(#6337): make call to toNoirStruct unnecessary // eslint-disable-next-line camelcase note_hash_counter: 0, // set as 0 as note is not transient nonce: noteNonces[0], diff --git a/yarn-project/end-to-end/src/e2e_encryption.test.ts b/yarn-project/end-to-end/src/e2e_encryption.test.ts index afa5751e0d63..40372208393d 100644 --- a/yarn-project/end-to-end/src/e2e_encryption.test.ts +++ b/yarn-project/end-to-end/src/e2e_encryption.test.ts @@ -8,6 +8,7 @@ import { Note, type Wallet, } from '@aztec/aztec.js'; +import { deriveBaseSlot } from '@aztec/circuits.js'; import { Aes128, Grumpkin } from '@aztec/circuits.js/barretenberg'; import { TestContract } from '@aztec/noir-contracts.js'; @@ -91,7 +92,7 @@ describe('e2e_encryption', () => { const ephPubKey = grumpkin.mul(Grumpkin.generator, ephSecretKey); const viewingPubKey = grumpkin.mul(Grumpkin.generator, viewingSecretKey); - const storageSlot = new Fr(1); + const storageSlot = deriveBaseSlot(1); const noteTypeId = TestContract.artifact.notes['TestNote'].id; const value = Fr.random(); const note = new Note([value]); @@ -99,7 +100,12 @@ describe('e2e_encryption', () => { const body = new EncryptedNoteLogIncomingBody(storageSlot, noteTypeId, note); const encrypted = await contract.methods - .compute_incoming_log_body_ciphertext(ephSecretKey, viewingPubKey.toNoirStruct(), storageSlot, value) + .compute_incoming_log_body_ciphertext( + ephSecretKey, + viewingPubKey.toNoirStruct(), + storageSlot.toNoirStruct(), + value, + ) .simulate(); expect(Buffer.from(encrypted.map((x: bigint) => Number(x)))).toEqual( diff --git a/yarn-project/end-to-end/src/e2e_key_registry.test.ts b/yarn-project/end-to-end/src/e2e_key_registry.test.ts index 251ba141aeec..c5da8857fcf4 100644 --- a/yarn-project/end-to-end/src/e2e_key_registry.test.ts +++ b/yarn-project/end-to-end/src/e2e_key_registry.test.ts @@ -1,5 +1,5 @@ import { type AccountWallet, AztecAddress, Fr, type PXE } from '@aztec/aztec.js'; -import { CompleteAddress, Point, PublicKeys } from '@aztec/circuits.js'; +import { CompleteAddress, Point, PublicKeys, deriveBaseSlot } from '@aztec/circuits.js'; import { KeyRegistryContract, TestContract } from '@aztec/noir-contracts.js'; import { getCanonicalKeyRegistryAddress } from '@aztec/protocol-contracts/key-registry'; @@ -23,6 +23,7 @@ describe('Key Registry', () => { let teardown: () => Promise; const account = CompleteAddress.random(); + const sharedMutableStorageSlot = deriveBaseSlot(1).toNoirStruct(); beforeAll(async () => { ({ teardown, pxe, wallets } = await setup(2)); @@ -136,7 +137,7 @@ describe('Key Registry', () => { // We check if our registered nullifier key is equal to the key obtained from the getter by // reading our registry contract from the test contract. We expect this to fail because the change has not been applied yet const emptyNullifierPublicKeyX = await testContract.methods - .test_shared_mutable_private_getter_for_registry_contract(1, account) + .test_shared_mutable_private_getter_for_registry_contract(sharedMutableStorageSlot, account) .simulate(); expect(new Fr(emptyNullifierPublicKeyX)).toEqual(Fr.ZERO); @@ -145,7 +146,7 @@ describe('Key Registry', () => { await crossDelay(); const nullifierPublicKeyX = await testContract.methods - .test_shared_mutable_private_getter_for_registry_contract(1, account) + .test_shared_mutable_private_getter_for_registry_contract(sharedMutableStorageSlot, account) .simulate(); expect(new Fr(nullifierPublicKeyX)).toEqual(account.publicKeys.masterNullifierPublicKey.x); @@ -177,7 +178,7 @@ describe('Key Registry', () => { // We check if our rotated nullifier key is equal to the key obtained from the getter by reading our registry // contract from the test contract. We expect this to fail because the change has not been applied yet const emptyNullifierPublicKeyX = await testContract.methods - .test_shared_mutable_private_getter_for_registry_contract(1, wallets[0].getAddress()) + .test_shared_mutable_private_getter_for_registry_contract(sharedMutableStorageSlot, wallets[0].getAddress()) .simulate(); expect(new Fr(emptyNullifierPublicKeyX)).toEqual(Fr.ZERO); @@ -186,7 +187,7 @@ describe('Key Registry', () => { await crossDelay(); const nullifierPublicKeyX = await testContract.methods - .test_shared_mutable_private_getter_for_registry_contract(1, wallets[0].getAddress()) + .test_shared_mutable_private_getter_for_registry_contract(sharedMutableStorageSlot, wallets[0].getAddress()) .simulate(); expect(new Fr(nullifierPublicKeyX)).toEqual(firstNewMasterNullifierPublicKey.x); @@ -206,7 +207,7 @@ describe('Key Registry', () => { // We get the old nullifier key as the change has not been applied yet const oldNullifierPublicKeyX = await testContract.methods - .test_shared_mutable_private_getter_for_registry_contract(1, wallets[0].getAddress()) + .test_shared_mutable_private_getter_for_registry_contract(sharedMutableStorageSlot, wallets[0].getAddress()) .simulate(); expect(new Fr(oldNullifierPublicKeyX)).toEqual(firstNewMasterNullifierPublicKey.x); @@ -215,7 +216,7 @@ describe('Key Registry', () => { // We get the new nullifier key as the change has been applied const newNullifierPublicKeyX = await testContract.methods - .test_shared_mutable_private_getter_for_registry_contract(1, wallets[0].getAddress()) + .test_shared_mutable_private_getter_for_registry_contract(sharedMutableStorageSlot, wallets[0].getAddress()) .simulate(); expect(new Fr(newNullifierPublicKeyX)).toEqual(secondNewMasterNullifierPublicKey.x); diff --git a/yarn-project/end-to-end/src/e2e_keys.test.ts b/yarn-project/end-to-end/src/e2e_keys.test.ts index e485431c8ab3..41040ede5c00 100644 --- a/yarn-project/end-to-end/src/e2e_keys.test.ts +++ b/yarn-project/end-to-end/src/e2e_keys.test.ts @@ -13,6 +13,7 @@ import { INITIAL_L2_BLOCK_NUM, computeAppNullifierSecretKey, computeAppSecretKey, + deriveBaseSlot, deriveMasterNullifierSecretKey, deriveMasterOutgoingViewingSecretKey, derivePublicKeyFromSecretKey, @@ -73,13 +74,16 @@ describe('Key Registry', () => { const noteValue = 5; const noteOwner = account.getAddress(); const outgoingViewer = noteOwner; // Setting the outgoing viewer to owner to not have to bother with setting up another account. - const noteStorageSlot = 12; + const noteStorageSlot = deriveBaseSlot(12); - await testContract.methods.call_create_note(noteValue, noteOwner, outgoingViewer, noteStorageSlot).send().wait(); + await testContract.methods + .call_create_note(noteValue, noteOwner, outgoingViewer, noteStorageSlot.toNoirStruct()) + .send() + .wait(); expect(await getNumNullifiedNotes(nskApp, testContract.address)).toEqual(0); - await testContract.methods.call_destroy_note(noteStorageSlot).send().wait(); + await testContract.methods.call_destroy_note(noteStorageSlot.toNoirStruct()).send().wait(); expect(await getNumNullifiedNotes(nskApp, testContract.address)).toEqual(1); }); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts index a0ba5c148c0e..2bb1f22ef5cf 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts @@ -1,4 +1,5 @@ import { type AztecAddress, Fr } from '@aztec/aztec.js'; +import { deriveBaseSlot } from '@aztec/circuits.js'; import { ChildContract, ParentContract } from '@aztec/noir-contracts.js'; import { NestedContractTest } from './nested_contract_test.js'; @@ -7,7 +8,8 @@ describe('e2e_nested_contract manual_enqueue', () => { const t = new NestedContractTest('manual_enqueue'); let { wallets, pxe, parentContract, childContract } = t; - const getChildStoredValue = (child: { address: AztecAddress }) => pxe.getPublicStorageAt(child.address, new Fr(1)); + const getChildStoredValue = (child: { address: AztecAddress }) => + pxe.getPublicStorageAt(child.address, deriveBaseSlot(1).x); beforeAll(async () => { await t.applyBaseSnapshots(); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts index 0db8df6f5588..da71eaf9dd19 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts @@ -1,4 +1,5 @@ import { type AztecAddress, BatchCall, Fr, type Wallet, toBigIntBE } from '@aztec/aztec.js'; +import { deriveBaseSlot } from '@aztec/circuits.js'; import { NestedContractTest } from './nested_contract_test.js'; @@ -7,7 +8,8 @@ describe('e2e_nested_contract manual', () => { let wallet: Wallet; let { wallets, pxe, parentContract, childContract } = t; - const getChildStoredValue = (child: { address: AztecAddress }) => pxe.getPublicStorageAt(child.address, new Fr(1)); + const getChildStoredValue = (child: { address: AztecAddress }) => + pxe.getPublicStorageAt(child.address, deriveBaseSlot(1).x); beforeAll(async () => { await t.applyBaseSnapshots(); diff --git a/yarn-project/end-to-end/src/e2e_note_getter.test.ts b/yarn-project/end-to-end/src/e2e_note_getter.test.ts index be87db5bd487..7364324dd6c5 100644 --- a/yarn-project/end-to-end/src/e2e_note_getter.test.ts +++ b/yarn-project/end-to-end/src/e2e_note_getter.test.ts @@ -1,4 +1,4 @@ -import { type AztecAddress, Comparator, Fr, type Wallet } from '@aztec/aztec.js'; +import { type AztecAddress, Comparator, Fr, Point, type Wallet } from '@aztec/aztec.js'; import { DocsExampleContract, TestContract } from '@aztec/noir-contracts.js'; import { setup } from './fixtures/utils.js'; @@ -163,40 +163,46 @@ describe('e2e_note_getter', () => { const VALUE = 5; // To prevent tests from interacting with one another, we'll have each use a different storage slot. - let storageSlot = TestContract.storage.example_set.slot.toNumber(); + let storageSlot = TestContract.storage.example_set.slot; beforeEach(() => { - storageSlot += 1; + storageSlot = Point.random(); }); - async function assertNoteIsReturned(storageSlot: number, expectedValue: number, activeOrNullified: boolean) { - const viewNotesResult = await contract.methods.call_view_notes(storageSlot, activeOrNullified).simulate(); - const getNotesResult = await contract.methods.call_get_notes(storageSlot, activeOrNullified).simulate(); + async function assertNoteIsReturned(storageSlot: Point, expectedValue: number, activeOrNullified: boolean) { + const viewNotesResult = await contract.methods + .call_view_notes(storageSlot.toNoirStruct(), activeOrNullified) + .simulate(); + const getNotesResult = await contract.methods + .call_get_notes(storageSlot.toNoirStruct(), activeOrNullified) + .simulate(); expect(viewNotesResult).toEqual(getNotesResult); expect(viewNotesResult).toEqual(BigInt(expectedValue)); } - async function assertNoReturnValue(storageSlot: number, activeOrNullified: boolean) { - await expect(contract.methods.call_view_notes(storageSlot, activeOrNullified).simulate()).rejects.toThrow( + async function assertNoReturnValue(storageSlot: Point, activeOrNullified: boolean) { + await expect( + contract.methods.call_view_notes(storageSlot.toNoirStruct(), activeOrNullified).simulate(), + ).rejects.toThrow( 'index < self.len', // from BoundedVec::get ); - await expect(contract.methods.call_get_notes(storageSlot, activeOrNullified).prove()).rejects.toThrow( - `Assertion failed: Cannot return zero notes`, - ); + await expect( + contract.methods.call_get_notes(storageSlot.toNoirStruct(), activeOrNullified).prove(), + ).rejects.toThrow(`Assertion failed: Cannot return zero notes`); } describe('active note only', () => { const activeOrNullified = false; it('returns active notes', async () => { - await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot).send().wait(); + await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot.toNoirStruct()).send().wait(); await assertNoteIsReturned(storageSlot, VALUE, activeOrNullified); }); it('does not return nullified notes', async () => { - await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot).send().wait(); - await contract.methods.call_destroy_note(storageSlot).send().wait(); + await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot.toNoirStruct()).send().wait(); + await contract.methods.call_destroy_note(storageSlot.toNoirStruct()).send().wait(); await assertNoReturnValue(storageSlot, activeOrNullified); }); @@ -206,13 +212,13 @@ describe('e2e_note_getter', () => { const activeOrNullified = true; it('returns active notes', async () => { - await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot).send().wait(); + await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot.toNoirStruct()).send().wait(); await assertNoteIsReturned(storageSlot, VALUE, activeOrNullified); }); it('returns nullified notes', async () => { - await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot).send().wait(); - await contract.methods.call_destroy_note(storageSlot).send().wait(); + await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot.toNoirStruct()).send().wait(); + await contract.methods.call_destroy_note(storageSlot.toNoirStruct()).send().wait(); await assertNoteIsReturned(storageSlot, VALUE, activeOrNullified); }); @@ -220,19 +226,19 @@ describe('e2e_note_getter', () => { it('returns both active and nullified notes', async () => { // We store two notes with two different values in the same storage slot, and then delete one of them. Note that // we can't be sure which one was deleted since we're just deleting based on the storage slot. - await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot).send().wait(); + await contract.methods.call_create_note(VALUE, owner, outgoingViewer, storageSlot.toNoirStruct()).send().wait(); await contract.methods - .call_create_note(VALUE + 1, owner, outgoingViewer, storageSlot) + .call_create_note(VALUE + 1, owner, outgoingViewer, storageSlot.toNoirStruct()) .send() .wait(); - await contract.methods.call_destroy_note(storageSlot).send().wait(); + await contract.methods.call_destroy_note(storageSlot.toNoirStruct()).send().wait(); // We now fetch multiple notes, and get both the active and the nullified one. const viewNotesManyResult = await contract.methods - .call_view_notes_many(storageSlot, activeOrNullified) + .call_view_notes_many(storageSlot.toNoirStruct(), activeOrNullified) .simulate(); const getNotesManyResult = await contract.methods - .call_get_notes_many(storageSlot, activeOrNullified) + .call_get_notes_many(storageSlot.toNoirStruct(), activeOrNullified) .simulate(); // We can't be sure in which order the notes will be returned, so we simply sort them to test equality. Note diff --git a/yarn-project/end-to-end/src/e2e_ordering.test.ts b/yarn-project/end-to-end/src/e2e_ordering.test.ts index 949f64d8481b..1ad47c01b81a 100644 --- a/yarn-project/end-to-end/src/e2e_ordering.test.ts +++ b/yarn-project/end-to-end/src/e2e_ordering.test.ts @@ -1,5 +1,6 @@ // Test suite for testing proper ordering of side effects -import { Fr, type FunctionSelector, type PXE, type Wallet, toBigIntBE } from '@aztec/aztec.js'; +import { type FunctionSelector, type PXE, type Wallet, toBigIntBE } from '@aztec/aztec.js'; +import { deriveBaseSlot } from '@aztec/circuits.js'; import { ChildContract } from '@aztec/noir-contracts.js/Child'; import { ParentContract } from '@aztec/noir-contracts.js/Parent'; @@ -79,7 +80,7 @@ describe('e2e_ordering', () => { await expectLogsFromLastBlockToBe(expectedOrder); // The final value of the child is the last one set - const value = await pxe.getPublicStorageAt(child.address, new Fr(1)); + const value = await pxe.getPublicStorageAt(child.address, deriveBaseSlot(1).x); expect(value.value).toBe(expectedOrder[1]); // final state should match last value set }, ); @@ -104,7 +105,7 @@ describe('e2e_ordering', () => { await child.methods[method]().send().wait(); - const value = await pxe.getPublicStorageAt(child.address, new Fr(1)); + const value = await pxe.getPublicStorageAt(child.address, deriveBaseSlot(1).x); expect(value.value).toBe(expectedOrder[expectedOrder.length - 1]); // final state should match last value set }); diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index 400a25e9f361..44bf9e74a348 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -1,4 +1,5 @@ import { AztecAddress, BatchCall, Fr, type PXE, type Wallet } from '@aztec/aztec.js'; +import { deriveBaseSlot } from '@aztec/circuits.js'; import { AuthContract, DocsExampleContract, TestContract } from '@aztec/noir-contracts.js'; import { jest } from '@jest/globals'; @@ -239,6 +240,7 @@ describe('e2e_state_vars', () => { describe('SharedMutablePrivateGetter', () => { let authContract: AuthContract; let testContract: TestContract; + const sharedMutableStorageSlot = deriveBaseSlot(2); const delay = async (blocks: number) => { for (let i = 0; i < blocks; i++) { @@ -262,7 +264,7 @@ describe('e2e_state_vars', () => { .wait(); const authorized = await testContract.methods - .test_shared_mutable_private_getter(authContract.address, 2) + .test_shared_mutable_private_getter(authContract.address, sharedMutableStorageSlot.toNoirStruct()) .simulate(); // We expect the value to not have been applied yet @@ -273,7 +275,7 @@ describe('e2e_state_vars', () => { // We check after the delay, expecting to find the value we set. const newAuthorized = await testContract.methods - .test_shared_mutable_private_getter(authContract.address, 2) + .test_shared_mutable_private_getter(authContract.address, sharedMutableStorageSlot.toNoirStruct()) .simulate(); expect(AztecAddress.fromBigInt(newAuthorized)).toEqual(AztecAddress.fromBigInt(6969696969n)); @@ -282,7 +284,9 @@ describe('e2e_state_vars', () => { it('checks authorized in AuthContract from TestContract and finds the correctly set max block number', async () => { const lastBlockNumber = await pxe.getBlockNumber(); - const tx = await testContract.methods.test_shared_mutable_private_getter(authContract.address, 2).prove(); + const tx = await testContract.methods + .test_shared_mutable_private_getter(authContract.address, sharedMutableStorageSlot.toNoirStruct()) + .prove(); const expectedMaxBlockNumber = lastBlockNumber + 5; @@ -295,7 +299,9 @@ describe('e2e_state_vars', () => { const expectedInitialMaxBlockNumber = (await pxe.getBlockNumber()) + 5; // Our SharedMutablePrivateGetter here reads from the SharedMutable authorized storage property in AuthContract - const tx = await testContract.methods.test_shared_mutable_private_getter(authContract.address, 2).prove(); + const tx = await testContract.methods + .test_shared_mutable_private_getter(authContract.address, sharedMutableStorageSlot.toNoirStruct()) + .prove(); // The validity of our SharedMutable read request is limited to 5 blocks, which is our initial delay. expect(tx.data.forRollup!.rollupValidationRequests.maxBlockNumber.isSome).toEqual(true); @@ -323,7 +329,9 @@ describe('e2e_state_vars', () => { ); // We check for parity between accessing our SharedMutable directly and from our SharedMutablePrivateGetter. The validity assumptions should remain the same. - const tx3 = await testContract.methods.test_shared_mutable_private_getter(authContract.address, 2).prove(); + const tx3 = await testContract.methods + .test_shared_mutable_private_getter(authContract.address, sharedMutableStorageSlot.toNoirStruct()) + .prove(); expect(tx3.data.forRollup!.rollupValidationRequests.maxBlockNumber.isSome).toEqual(true); expect(tx3.data.forRollup!.rollupValidationRequests.maxBlockNumber.value).toEqual( @@ -346,7 +354,9 @@ describe('e2e_state_vars', () => { ); // We check for parity between accessing our SharedMutable directly and from our SharedMutablePrivateGetter. The validity assumptions should remain the same. - const tx5 = await testContract.methods.test_shared_mutable_private_getter(authContract.address, 2).prove(); + const tx5 = await testContract.methods + .test_shared_mutable_private_getter(authContract.address, sharedMutableStorageSlot.toNoirStruct()) + .prove(); expect(tx5.data.forRollup!.rollupValidationRequests.maxBlockNumber.isSome).toEqual(true); expect(tx5.data.forRollup!.rollupValidationRequests.maxBlockNumber.value).toEqual( diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index 81f12ef8a8cc..062f57a8e09c 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -12,6 +12,7 @@ import { createPXEClient, waitForPXE, } from '@aztec/aztec.js'; +import { type Point, deriveBaseSlot } from '@aztec/circuits.js'; // docs:end:imports // docs:start:import_contract import { TestContract } from '@aztec/noir-contracts.js/Test'; @@ -141,7 +142,7 @@ describe('guides/dapp/testing', () => { let testContract: TestContract; let token: TokenContract; let cheats: CheatCodes; - let ownerSlot: Fr; + let ownerSlot: Point; beforeAll(async () => { pxe = createPXEClient(PXE_URL); @@ -194,9 +195,10 @@ describe('guides/dapp/testing', () => { it('checks public storage', async () => { // docs:start:public-storage await token.methods.mint_public(owner.getAddress(), 100n).send().wait(); - const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(6n, owner.getAddress()); - const balance = await pxe.getPublicStorageAt(token.address, ownerPublicBalanceSlot); - expect(balance.value).toEqual(100n); + const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(deriveBaseSlot(6n), owner.getAddress()); + const ownerPublicBalanceContractStorageIndex = ownerPublicBalanceSlot.x; + const balance = await pxe.getPublicStorageAt(token.address, ownerPublicBalanceContractStorageIndex); + expect(balance.toBigInt()).toEqual(100n); // docs:end:public-storage }); @@ -252,8 +254,9 @@ describe('guides/dapp/testing', () => { const call = token.methods.transfer_public(owner.getAddress(), recipient.getAddress(), 1000n, 0); const receipt = await call.send({ skipPublicSimulation: true }).wait({ dontThrowOnRevert: true }); expect(receipt.status).toEqual(TxStatus.APP_LOGIC_REVERTED); - const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(6n, owner.getAddress()); - const balance = await pxe.getPublicStorageAt(token.address, ownerPublicBalanceSlot); + const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(deriveBaseSlot(6n), owner.getAddress()); + const ownerPublicContractStorageIndex = ownerPublicBalanceSlot.x; + const balance = await pxe.getPublicStorageAt(token.address, ownerPublicContractStorageIndex); expect(balance.value).toEqual(100n); // docs:end:pub-reverted }); diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index ee5352cf4d10..df5cad49d712 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -1,6 +1,6 @@ import { inflate } from 'pako'; -import { type Fr } from '../fields/fields.js'; +import { type Point } from '../fields/point.js'; import { type FunctionSelector } from './function_selector.js'; import { type NoteSelector } from './note_selector.js'; @@ -289,7 +289,7 @@ export type FieldLayout = { /** * Slot in which the field is stored. */ - slot: Fr; + slot: Point; }; /** diff --git a/yarn-project/foundation/src/fields/point.ts b/yarn-project/foundation/src/fields/point.ts index eb443cbf6c00..053f58cb01de 100644 --- a/yarn-project/foundation/src/fields/point.ts +++ b/yarn-project/foundation/src/fields/point.ts @@ -1,6 +1,6 @@ import { toBigIntBE } from '../bigint-buffer/index.js'; import { poseidon2Hash, randomBoolean } from '../crypto/index.js'; -import { BufferReader, FieldReader, serializeToBuffer } from '../serialize/index.js'; +import { BufferReader, FieldReader, TypeRegistry, serializeToBuffer } from '../serialize/index.js'; import { Fr } from './fields.js'; /** @@ -265,19 +265,13 @@ export class Point { const rhs = this.x.square().mul(this.x).sub(A); return lhs.equals(rhs); } -} -/** - * Does this object look like a point? - * @param obj - Object to test if it is a point. - * @returns Whether it looks like a point. - */ -export function isPoint(obj: object): obj is Point { - if (!obj) { - return false; + toJSON() { + return { + type: 'Point', + value: this.toString(), + }; } - const point = obj as Point; - return point.kind === 'point' && point.x !== undefined && point.y !== undefined; } export class NotOnCurveError extends Error { @@ -286,3 +280,6 @@ export class NotOnCurveError extends Error { this.name = 'NotOnCurveError'; } } + +// For deserializing JSON. +TypeRegistry.register('Point', Point); diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index 7bc509a2562b..12a0ddefdf3d 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -1794,7 +1794,7 @@ export function mapGlobalVariablesToNoir(globalVariables: GlobalVariables): Glob */ export function mapStorageReadToNoir(storageRead: ContractStorageRead): StorageReadNoir { return { - storage_slot: mapFieldToNoir(storageRead.storageSlot), + storage_index: mapFieldToNoir(storageRead.storageSlot), current_value: mapFieldToNoir(storageRead.currentValue), counter: mapNumberToNoir(storageRead.counter), }; diff --git a/yarn-project/pxe/src/database/deferred_note_dao.test.ts b/yarn-project/pxe/src/database/deferred_note_dao.test.ts index 542774a11650..7d9cf5788552 100644 --- a/yarn-project/pxe/src/database/deferred_note_dao.test.ts +++ b/yarn-project/pxe/src/database/deferred_note_dao.test.ts @@ -10,7 +10,7 @@ export const randomDeferredNoteDao = ({ note = Note.random(), contractAddress = AztecAddress.random(), txHash = randomTxHash(), - storageSlot = Fr.random(), + storageSlot = Point.random(), noteTypeId = NoteSelector.random(), noteHashes = [Fr.random(), Fr.random()], dataStartIndexForTx = randomInt(100), diff --git a/yarn-project/pxe/src/database/deferred_note_dao.ts b/yarn-project/pxe/src/database/deferred_note_dao.ts index bc5cfd455ba2..0b5532f833cd 100644 --- a/yarn-project/pxe/src/database/deferred_note_dao.ts +++ b/yarn-project/pxe/src/database/deferred_note_dao.ts @@ -17,7 +17,7 @@ export class DeferredNoteDao { /** The contract address this note is created in. */ public contractAddress: AztecAddress, /** The specific storage location of the note on the contract. */ - public storageSlot: Fr, + public storageSlot: Point, /** The type ID of the note on the contract. */ public noteTypeId: NoteSelector, /** The hash of the tx the note was created in. Equal to the first nullifier */ @@ -46,7 +46,7 @@ export class DeferredNoteDao { reader.readObject(Point), reader.readObject(Note), reader.readObject(AztecAddress), - reader.readObject(Fr), + reader.readObject(Point), reader.readObject(NoteSelector), reader.readObject(TxHash), reader.readVector(Fr), diff --git a/yarn-project/pxe/src/database/incoming_note_dao.test.ts b/yarn-project/pxe/src/database/incoming_note_dao.test.ts index f20e957fc1b8..75c14bea1832 100644 --- a/yarn-project/pxe/src/database/incoming_note_dao.test.ts +++ b/yarn-project/pxe/src/database/incoming_note_dao.test.ts @@ -8,10 +8,10 @@ export const randomIncomingNoteDao = ({ note = Note.random(), contractAddress = AztecAddress.random(), txHash = randomTxHash(), - storageSlot = Fr.random(), + storageSlot = Point.random(), noteTypeId = NoteSelector.random(), nonce = Fr.random(), - innerNoteHash = Fr.random(), + innerNoteHashX: innerNoteHash = Fr.random(), siloedNullifier = Fr.random(), index = Fr.random().toBigInt(), ivpkM = Point.random(), diff --git a/yarn-project/pxe/src/database/incoming_note_dao.ts b/yarn-project/pxe/src/database/incoming_note_dao.ts index 0a128a742599..ff1d616b7897 100644 --- a/yarn-project/pxe/src/database/incoming_note_dao.ts +++ b/yarn-project/pxe/src/database/incoming_note_dao.ts @@ -15,7 +15,7 @@ export class IncomingNoteDao implements NoteData { /** The contract address this note is created in. */ public contractAddress: AztecAddress, /** The specific storage location of the note on the contract. */ - public storageSlot: Fr, + public storageSlot: Point, /** The note type identifier for the contract. */ public noteTypeId: NoteSelector, /** The hash of the tx the note was created in. */ @@ -26,7 +26,7 @@ export class IncomingNoteDao implements NoteData { * Inner note hash of the note. This is customizable by the app circuit. * We can use this value to compute siloedNoteHash and uniqueSiloedNoteHash. */ - public innerNoteHash: Fr, + public innerNoteHashX: Fr, /** * The nullifier of the note (siloed by contract address). * Note: Might be set as 0 if the note was added to PXE as nullified. @@ -46,7 +46,7 @@ export class IncomingNoteDao implements NoteData { this.noteTypeId, this.txHash.buffer, this.nonce, - this.innerNoteHash, + this.innerNoteHashX, this.siloedNullifier, this.index, this.ivpkM, @@ -57,7 +57,7 @@ export class IncomingNoteDao implements NoteData { const note = Note.fromBuffer(reader); const contractAddress = AztecAddress.fromBuffer(reader); - const storageSlot = Fr.fromBuffer(reader); + const storageSlot = reader.readObject(Point); const noteTypeId = reader.readObject(NoteSelector); const txHash = reader.readObject(TxHash); const nonce = Fr.fromBuffer(reader); diff --git a/yarn-project/pxe/src/database/outgoing_note_dao.test.ts b/yarn-project/pxe/src/database/outgoing_note_dao.test.ts index 9e7241760ffa..8b70bf149fb7 100644 --- a/yarn-project/pxe/src/database/outgoing_note_dao.test.ts +++ b/yarn-project/pxe/src/database/outgoing_note_dao.test.ts @@ -8,7 +8,7 @@ export const randomOutgoingNoteDao = ({ note = Note.random(), contractAddress = AztecAddress.random(), txHash = randomTxHash(), - storageSlot = Fr.random(), + storageSlot = Point.random(), noteTypeId = NoteSelector.random(), nonce = Fr.random(), innerNoteHash = Fr.random(), diff --git a/yarn-project/pxe/src/database/outgoing_note_dao.ts b/yarn-project/pxe/src/database/outgoing_note_dao.ts index 03075f9f7df4..1286d4a58fab 100644 --- a/yarn-project/pxe/src/database/outgoing_note_dao.ts +++ b/yarn-project/pxe/src/database/outgoing_note_dao.ts @@ -14,7 +14,7 @@ export class OutgoingNoteDao { /** The contract address this note is created in. */ public contractAddress: AztecAddress, /** The specific storage location of the note on the contract. */ - public storageSlot: Fr, + public storageSlot: Point, /** The note type identifier for the contract. */ public noteTypeId: NoteSelector, /** The hash of the tx the note was created in. */ @@ -50,7 +50,7 @@ export class OutgoingNoteDao { const note = Note.fromBuffer(reader); const contractAddress = AztecAddress.fromBuffer(reader); - const storageSlot = Fr.fromBuffer(reader); + const storageSlot = reader.readObject(Point); const noteTypeId = reader.readObject(NoteSelector); const txHash = new TxHash(reader.readBytes(TxHash.SIZE)); const nonce = Fr.fromBuffer(reader); diff --git a/yarn-project/pxe/src/database/pxe_database_test_suite.ts b/yarn-project/pxe/src/database/pxe_database_test_suite.ts index 92a2dc1d2e57..592a1bf9f977 100644 --- a/yarn-project/pxe/src/database/pxe_database_test_suite.ts +++ b/yarn-project/pxe/src/database/pxe_database_test_suite.ts @@ -73,7 +73,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { describe('incoming notes', () => { let owners: CompleteAddress[]; let contractAddresses: AztecAddress[]; - let storageSlots: Fr[]; + let storageSlots: Point[]; let notes: IncomingNoteDao[]; const filteringTests: [() => IncomingNotesFilter, () => IncomingNoteDao[]][] = [ @@ -89,7 +89,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { () => ({ storageSlot: storageSlots[0] }), () => notes.filter(note => note.storageSlot.equals(storageSlots[0])), ], - [() => ({ storageSlot: Fr.random() }), () => []], + [() => ({ storageSlot: Point.random() }), () => []], [() => ({ txHash: notes[0].txHash }), () => [notes[0]]], [() => ({ txHash: randomTxHash() }), () => []], @@ -112,7 +112,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { beforeEach(async () => { owners = Array.from({ length: 2 }).map(() => CompleteAddress.random()); contractAddresses = Array.from({ length: 2 }).map(() => AztecAddress.random()); - storageSlots = Array.from({ length: 2 }).map(() => Fr.random()); + storageSlots = Array.from({ length: 2 }).map(() => Point.random()); notes = Array.from({ length: 10 }).map((_, i) => randomIncomingNoteDao({ @@ -201,7 +201,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { describe('outgoing notes', () => { let owners: CompleteAddress[]; let contractAddresses: AztecAddress[]; - let storageSlots: Fr[]; + let storageSlots: Point[]; let notes: OutgoingNoteDao[]; const filteringTests: [() => OutgoingNotesFilter, () => OutgoingNoteDao[]][] = [ @@ -217,7 +217,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { () => ({ storageSlot: storageSlots[0] }), () => notes.filter(note => note.storageSlot.equals(storageSlots[0])), ], - [() => ({ storageSlot: Fr.random() }), () => []], + [() => ({ storageSlot: Point.random() }), () => []], [() => ({ txHash: notes[0].txHash }), () => [notes[0]]], [() => ({ txHash: randomTxHash() }), () => []], @@ -240,7 +240,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { beforeEach(async () => { owners = Array.from({ length: 2 }).map(() => CompleteAddress.random()); contractAddresses = Array.from({ length: 2 }).map(() => AztecAddress.random()); - storageSlots = Array.from({ length: 2 }).map(() => Fr.random()); + storageSlots = Array.from({ length: 2 }).map(() => Point.random()); notes = Array.from({ length: 10 }).map((_, i) => randomOutgoingNoteDao({ diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts index a3a0bf9d24f8..3b1cdc165acc 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -21,7 +21,7 @@ import { makeTxRequest } from '@aztec/circuits.js/testing'; import { NoteSelector } from '@aztec/foundation/abi'; import { makeTuple } from '@aztec/foundation/array'; import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, Point } from '@aztec/foundation/fields'; import { type ExecutionResult, type NoteAndSlot } from '@aztec/simulator'; import { mock } from 'jest-mock-extended'; @@ -42,7 +42,7 @@ describe('Kernel Prover', () => { .fill(null) .map(() => ({ note: new Note([Fr.random(), Fr.random(), Fr.random()]), - storageSlot: Fr.random(), + storageSlot: Point.random(), noteTypeId: NoteSelector.random(), owner: { x: Fr.random(), y: Fr.random() }, })); diff --git a/yarn-project/pxe/src/note_processor/note_processor.test.ts b/yarn-project/pxe/src/note_processor/note_processor.test.ts index 8fb954ea9866..3da9e8625b56 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -166,7 +166,7 @@ describe('Note Processor', () => { simulator.computeNoteHashAndOptionallyANullifier.mockImplementation((...args) => Promise.resolve({ - innerNoteHash: Fr.random(), + innerNoteHashX: Fr.random(), uniqueNoteHash: Fr.random(), siloedNoteHash: pedersenHash(args[5].items), // args[5] is note innerNullifier: Fr.random(), diff --git a/yarn-project/pxe/src/note_processor/produce_note_dao.ts b/yarn-project/pxe/src/note_processor/produce_note_dao.ts index 46d64c7125f1..986c90772cda 100644 --- a/yarn-project/pxe/src/note_processor/produce_note_dao.ts +++ b/yarn-project/pxe/src/note_processor/produce_note_dao.ts @@ -51,7 +51,7 @@ export async function produceNoteDaos( try { if (ivpkM) { - const { noteHashIndex, nonce, innerNoteHash, siloedNullifier } = await findNoteIndexAndNullifier( + const { noteHashIndex, nonce, innerNoteHashX, siloedNullifier } = await findNoteIndexAndNullifier( simulator, noteHashes, txHash, @@ -69,7 +69,7 @@ export async function produceNoteDaos( payload.noteTypeId, txHash, nonce, - innerNoteHash, + innerNoteHashX, siloedNullifier, index, ivpkM, @@ -108,12 +108,12 @@ export async function produceNoteDaos( payload.noteTypeId, txHash, incomingNote.nonce, - incomingNote.innerNoteHash, + incomingNote.innerNoteHashX, incomingNote.index, ovpkM, ); } else { - const { noteHashIndex, nonce, innerNoteHash } = await findNoteIndexAndNullifier( + const { noteHashIndex, nonce, innerNoteHashX } = await findNoteIndexAndNullifier( simulator, noteHashes, txHash, @@ -130,7 +130,7 @@ export async function produceNoteDaos( payload.noteTypeId, txHash, nonce, - innerNoteHash, + innerNoteHashX, index, ovpkM, ); @@ -189,7 +189,7 @@ async function findNoteIndexAndNullifier( ) { let noteHashIndex = 0; let nonce: Fr | undefined; - let innerNoteHash: Fr | undefined; + let innerNoteHashX: Fr | undefined; let siloedNoteHash: Fr | undefined; let innerNullifier: Fr | undefined; const firstNullifier = Fr.fromBuffer(txHash.toBuffer()); @@ -205,7 +205,7 @@ async function findNoteIndexAndNullifier( } const expectedNonce = computeNoteHashNonce(firstNullifier, noteHashIndex); - ({ innerNoteHash, siloedNoteHash, innerNullifier } = await simulator.computeNoteHashAndOptionallyANullifier( + ({ innerNoteHashX, siloedNoteHash, innerNullifier } = await simulator.computeNoteHashAndOptionallyANullifier( contractAddress, expectedNonce, storageSlot, @@ -229,7 +229,7 @@ async function findNoteIndexAndNullifier( return { noteHashIndex, nonce, - innerNoteHash: innerNoteHash!, + innerNoteHashX: innerNoteHashX!, siloedNullifier: siloNullifier(contractAddress, innerNullifier!), }; } diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 9c2c0fc90657..a6b0f54e6005 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -289,11 +289,11 @@ export class PXEService implements PXE { return this.db.getContractsAddresses(); } - public async getPublicStorageAt(contract: AztecAddress, slot: Fr) { + public async getPublicStorageAt(contract: AztecAddress, contractStorageIndex: Fr) { if (!(await this.getContractInstance(contract))) { throw new Error(`Contract ${contract.toString()} is not deployed`); } - return await this.node.getPublicStorageAt(contract, slot, 'latest'); + return await this.node.getPublicStorageAt(contract, contractStorageIndex, 'latest'); } public async getIncomingNotes(filter: IncomingNotesFilter): Promise { @@ -350,7 +350,7 @@ export class PXEService implements PXE { } for (const nonce of nonces) { - const { innerNoteHash, siloedNoteHash, innerNullifier } = + const { innerNoteHashX, siloedNoteHash, innerNullifier } = await this.simulator.computeNoteHashAndOptionallyANullifier( note.contractAddress, nonce, @@ -379,7 +379,7 @@ export class PXEService implements PXE { note.noteTypeId, note.txHash, nonce, - innerNoteHash, + innerNoteHashX, siloedNullifier, index, owner.publicKeys.masterIncomingViewingPublicKey, @@ -400,7 +400,7 @@ export class PXEService implements PXE { } for (const nonce of nonces) { - const { innerNoteHash, siloedNoteHash, innerNullifier } = + const { innerNoteHashX, siloedNoteHash, innerNullifier } = await this.simulator.computeNoteHashAndOptionallyANullifier( note.contractAddress, nonce, @@ -427,7 +427,7 @@ export class PXEService implements PXE { note.noteTypeId, note.txHash, nonce, - innerNoteHash, + innerNoteHashX, Fr.ZERO, // We are not able to derive index, owner.publicKeys.masterIncomingViewingPublicKey, diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 02f6aa0859d7..00ee578a6ba5 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -15,6 +15,7 @@ import { type Header, type KeyValidationRequest, type L1_TO_L2_MSG_TREE_HEIGHT, + type Point, } from '@aztec/circuits.js'; import { computeL1ToL2MessageNullifier } from '@aztec/circuits.js/hash'; import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi'; @@ -77,18 +78,18 @@ export class SimulatorOracle implements DBOracle { return capsule; } - async getNotes(contractAddress: AztecAddress, storageSlot: Fr, status: NoteStatus) { + async getNotes(contractAddress: AztecAddress, storageSlot: Point, status: NoteStatus) { const noteDaos = await this.db.getIncomingNotes({ contractAddress, storageSlot, status, }); - return noteDaos.map(({ contractAddress, storageSlot, nonce, note, innerNoteHash, siloedNullifier, index }) => ({ + return noteDaos.map(({ contractAddress, storageSlot, nonce, note, innerNoteHashX, siloedNullifier, index }) => ({ contractAddress, storageSlot, nonce, note, - innerNoteHash, + innerNoteHashX, siloedNullifier, // PXE can use this index to get full MembershipWitness index, diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts index 5cc1219637c1..c5d308ec0cfa 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts @@ -1,7 +1,5 @@ import { type Tx, mockTx } from '@aztec/circuit-types'; import { AztecAddress, Fr, FunctionSelector, GasSettings } from '@aztec/circuits.js'; -import { pedersenHash } from '@aztec/foundation/crypto'; -import { GasTokenContract } from '@aztec/noir-contracts.js'; import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token'; import { type MockProxy, mock, mockFn } from 'jest-mock-extended'; @@ -37,7 +35,9 @@ describe('GasTxValidator', () => { inclusionFee: new Fr(TX_FEE), }); payer = tx.data.feePayer; - expectedBalanceSlot = pedersenHash([GasTokenContract.storage.balances.slot, payer]); + // expectedBalanceSlot = pedersenHash([GasTokenContract.storage.balances.slot, payer]); + // TODO(#7604): nuke the following and uncomment above + expectedBalanceSlot = payer.toField(); expect(tx.data.constants.txContext.gasSettings.getFeeLimit()).toEqual(new Fr(TX_FEE)); }); diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts index e1b47f095398..2330609d06f2 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts @@ -2,7 +2,7 @@ import { PublicKernelType, type Tx, type TxValidator } from '@aztec/circuit-type import { type AztecAddress, type Fr } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { GasTokenArtifact } from '@aztec/protocol-contracts/gas-token'; -import { AbstractPhaseManager, computeFeePayerBalanceStorageSlot } from '@aztec/simulator'; +import { AbstractPhaseManager, computeFeePayerBalanceContractStorageIndex } from '@aztec/simulator'; /** Provides a view into public contract state */ export interface PublicStateSource { @@ -51,7 +51,7 @@ export class GasTxValidator implements TxValidator { // Read current balance of the feePayer const initialBalance = await this.#publicDataSource.storageRead( this.#gasTokenAddress, - computeFeePayerBalanceStorageSlot(feePayer), + computeFeePayerBalanceContractStorageIndex(feePayer), ); // If there is a claim in this tx that increases the fee payer balance in gas token, add it to balance diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index bc7fedc2f141..f45177017e29 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -182,7 +182,9 @@ export class Oracle { } async getNotes( - [storageSlot]: ACVMField[], + [storageSlotX]: ACVMField[], + [storageSlotY]: ACVMField[], + [storageSlotIsInfinite]: ACVMField[], [numSelects]: ACVMField[], selectByIndexes: ACVMField[], selectByOffsets: ACVMField[], @@ -198,8 +200,13 @@ export class Oracle { [status]: ACVMField[], [returnSize]: ACVMField[], ): Promise { + const storageSlot = new Point( + fromACVMField(storageSlotX), + fromACVMField(storageSlotY), + !fromACVMField(storageSlotIsInfinite).isZero(), + ); const noteDatas = await this.typedOracle.getNotes( - fromACVMField(storageSlot), + storageSlot, +numSelects, selectByIndexes.map(s => +s), selectByOffsets.map(s => +s), @@ -244,17 +251,24 @@ export class Oracle { } notifyCreatedNote( - [storageSlot]: ACVMField[], + [storageSlotX]: ACVMField[], + [storageSlotY]: ACVMField[], + [storageSlotIsInfinite]: ACVMField[], [noteTypeId]: ACVMField[], note: ACVMField[], - [innerNoteHash]: ACVMField[], + [innerNoteHashX]: ACVMField[], [counter]: ACVMField[], ): ACVMField { + const storageSlot = new Point( + fromACVMField(storageSlotX), + fromACVMField(storageSlotY), + !fromACVMField(storageSlotIsInfinite).isZero(), + ); this.typedOracle.notifyCreatedNote( - fromACVMField(storageSlot), + storageSlot, NoteSelector.fromField(fromACVMField(noteTypeId)), note.map(fromACVMField), - fromACVMField(innerNoteHash), + fromACVMField(innerNoteHashX), +counter, ); return toACVMField(0); @@ -364,7 +378,9 @@ export class Oracle { computeEncryptedNoteLog( [contractAddress]: ACVMField[], - [storageSlot]: ACVMField[], + [storageSlotX]: ACVMField[], + [storageSlotY]: ACVMField[], + [storageSlotIsInfinite]: ACVMField[], [noteTypeId]: ACVMField[], [ovskApp]: ACVMField[], [ovpkMX]: ACVMField[], @@ -376,12 +392,17 @@ export class Oracle { [recipient]: ACVMField[], preimage: ACVMField[], ): ACVMField[] { + const storageSlot = new Point( + fromACVMField(storageSlotX), + fromACVMField(storageSlotY), + !fromACVMField(storageSlotIsInfinite).isZero(), + ); const ovpkM = new Point(fromACVMField(ovpkMX), fromACVMField(ovpkMY), !fromACVMField(ovpkMIsInfinite).isZero()); const ovKeys = new KeyValidationRequest(ovpkM, Fr.fromString(ovskApp)); const ivpkM = new Point(fromACVMField(ivpkMX), fromACVMField(ivpkMY), !fromACVMField(ivpkMIsInfinite).isZero()); const encLog = this.typedOracle.computeEncryptedNoteLog( AztecAddress.fromString(contractAddress), - Fr.fromString(storageSlot), + storageSlot, NoteSelector.fromField(Fr.fromString(noteTypeId)), ovKeys, ivpkM, diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 256e7bd30835..e417e710b2f4 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -18,7 +18,7 @@ import { } from '@aztec/circuits.js'; import { type FunctionSelector, type NoteSelector } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, type Point } from '@aztec/foundation/fields'; import { type ContractInstance } from '@aztec/types/contracts'; /** @@ -30,11 +30,11 @@ export interface NoteData { /** The contract address of the note. */ contractAddress: AztecAddress; /** The storage slot of the note. */ - storageSlot: Fr; + storageSlot: Point; /** The nonce of the note. */ nonce: Fr; /** The inner note hash of the note. */ - innerNoteHash: Fr; + innerNoteHashX: Fr; /** The corresponding nullifier of the note. Undefined for pending notes. */ siloedNullifier?: Fr; /** The note's leaf index in the note hash tree. Undefined for pending notes. */ @@ -146,7 +146,7 @@ export abstract class TypedOracle { } getNotes( - _storageSlot: Fr, + _storageSlot: Point, _numSelects: number, _selectByIndexes: number[], _selectByOffsets: number[], @@ -165,10 +165,10 @@ export abstract class TypedOracle { } notifyCreatedNote( - _storageSlot: Fr, + _storageSlot: Point, _noteTypeId: NoteSelector, _note: Fr[], - _innerNoteHash: Fr, + _innerNoteHashX: Fr, _counter: number, ): void { throw new OracleMethodNotAvailableError('notifyCreatedNote'); @@ -199,6 +199,7 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('storageRead'); } + // TODO(#7551): the _startStorageSlot naming is inconsistent with Noir now and should be fixed storageWrite(_startStorageSlot: Fr, _values: Fr[]): Promise { throw new OracleMethodNotAvailableError('storageWrite'); } @@ -230,7 +231,7 @@ export abstract class TypedOracle { computeEncryptedNoteLog( _contractAddress: AztecAddress, - _storageSlot: Fr, + _storageSlot: Point, _noteTypeId: NoteSelector, _ovKeys: KeyValidationRequest, _ivpkM: PublicKey, diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index 83d4f95939be..f28cef878176 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -1,4 +1,4 @@ -import { GasFees } from '@aztec/circuits.js'; +import { GasFees, deriveBaseSlot } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { computeVarArgsHash } from '@aztec/circuits.js/hash'; import { FunctionSelector } from '@aztec/foundation/abi'; @@ -338,11 +338,11 @@ describe('AVM simulator: transpiled Noir contracts', () => { const sender = new Fr(42); const leafIndex = new Fr(7); const slotNumber = 1; // must update Noir contract if changing this - const slot = new Fr(slotNumber); + const contractStorageIndex = deriveBaseSlot(slotNumber).x; const listSlotNumber0 = 2; // must update Noir contract if changing this - const listSlotNumber1 = listSlotNumber0 + 1; - const listSlot0 = new Fr(listSlotNumber0); - const listSlot1 = new Fr(listSlotNumber1); + const listContractStorageIndex0 = deriveBaseSlot(listSlotNumber0).x; + // When writing to storage multiple values the contract indices of the following values are incremented by 1 + const listContractStorageIndex1 = new Fr(listContractStorageIndex0.toBigInt() + 1n); const value0 = new Fr(420); const value1 = new Fr(69); @@ -570,10 +570,10 @@ describe('AVM simulator: transpiled Noir contracts', () => { const results = await new AvmSimulator(context).executeBytecode(bytecode); expect(results.reverted).toBe(false); - expect(await context.persistableState.peekStorage(storageAddress, slot)).toEqual(value0); + expect(await context.persistableState.peekStorage(storageAddress, contractStorageIndex)).toEqual(value0); expect(trace.tracePublicStorageWrite).toHaveBeenCalledTimes(1); - expect(trace.tracePublicStorageWrite).toHaveBeenCalledWith(storageAddress, slot, value0); + expect(trace.tracePublicStorageWrite).toHaveBeenCalledWith(storageAddress, contractStorageIndex, value0); }); it('Should read value in storage (single)', async () => { @@ -589,7 +589,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect(trace.tracePublicStorageRead).toHaveBeenCalledTimes(1); expect(trace.tracePublicStorageRead).toHaveBeenCalledWith( storageAddress, - slot, + contractStorageIndex, value0, /*exists=*/ true, /*cached=*/ false, @@ -607,11 +607,11 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect(results.output).toEqual([value0]); expect(trace.tracePublicStorageWrite).toHaveBeenCalledTimes(1); - expect(trace.tracePublicStorageWrite).toHaveBeenCalledWith(storageAddress, slot, value0); + expect(trace.tracePublicStorageWrite).toHaveBeenCalledWith(storageAddress, contractStorageIndex, value0); expect(trace.tracePublicStorageRead).toHaveBeenCalledTimes(1); expect(trace.tracePublicStorageRead).toHaveBeenCalledWith( storageAddress, - slot, + contractStorageIndex, value0, /*exists=*/ true, /*cached=*/ true, @@ -627,19 +627,23 @@ describe('AVM simulator: transpiled Noir contracts', () => { const results = await new AvmSimulator(context).executeBytecode(bytecode); expect(results.reverted).toBe(false); - expect(await context.persistableState.peekStorage(storageAddress, listSlot0)).toEqual(calldata[0]); - expect(await context.persistableState.peekStorage(storageAddress, listSlot1)).toEqual(calldata[1]); + expect(await context.persistableState.peekStorage(storageAddress, listContractStorageIndex0)).toEqual( + calldata[0], + ); + expect(await context.persistableState.peekStorage(storageAddress, listContractStorageIndex1)).toEqual( + calldata[1], + ); expect(trace.tracePublicStorageWrite).toHaveBeenCalledTimes(2); - expect(trace.tracePublicStorageWrite).toHaveBeenCalledWith(storageAddress, listSlot0, value0); - expect(trace.tracePublicStorageWrite).toHaveBeenCalledWith(storageAddress, listSlot1, value1); + expect(trace.tracePublicStorageWrite).toHaveBeenCalledWith(storageAddress, listContractStorageIndex0, value0); + expect(trace.tracePublicStorageWrite).toHaveBeenCalledWith(storageAddress, listContractStorageIndex1, value1); }); it('Should read a value in storage (list)', async () => { const context = createContext(); const mockedStorage = new Map([ - [listSlot0.toBigInt(), value0], - [listSlot1.toBigInt(), value1], + [listContractStorageIndex0.toBigInt(), value0], + [listContractStorageIndex1.toBigInt(), value1], ]); mockStorageReadWithMap(hostStorage, mockedStorage); @@ -651,14 +655,14 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect(trace.tracePublicStorageRead).toHaveBeenCalledWith( storageAddress, - listSlot0, + listContractStorageIndex0, value0, /*exists=*/ true, /*cached=*/ false, ); expect(trace.tracePublicStorageRead).toHaveBeenCalledWith( storageAddress, - listSlot1, + listContractStorageIndex1, value1, /*exists=*/ true, /*cached=*/ false, diff --git a/yarn-project/simulator/src/avm/journal/journal.ts b/yarn-project/simulator/src/avm/journal/journal.ts index f34a2832edd7..21fdf87d54fd 100644 --- a/yarn-project/simulator/src/avm/journal/journal.ts +++ b/yarn-project/simulator/src/avm/journal/journal.ts @@ -101,13 +101,13 @@ export class AvmPersistableStateManager { * Read from public storage, don't trace the read. * * @param storageAddress - the address of the contract whose storage is being read from - * @param slot - the slot in the contract's storage being read from + * @param contractStorageIndex - Storage index to read in the contract storage. * @returns the latest value written to slot, or 0 if never written to before */ - public async peekStorage(storageAddress: Fr, slot: Fr): Promise { - const { value, exists, cached } = await this.publicStorage.read(storageAddress, slot); + public async peekStorage(storageAddress: Fr, contractStorageIndex: Fr): Promise { + const { value, exists, cached } = await this.publicStorage.read(storageAddress, contractStorageIndex); this.log.debug( - `Storage peek (address=${storageAddress}, slot=${slot}): value=${value}, exists=${exists}, cached=${cached}`, + `Storage peek (address=${storageAddress}, contractStorageIndex=${contractStorageIndex}): value=${value}, exists=${exists}, cached=${cached}`, ); return Promise.resolve(value); } diff --git a/yarn-project/simulator/src/avm/journal/public_storage.ts b/yarn-project/simulator/src/avm/journal/public_storage.ts index 443717963db4..1468e023b6da 100644 --- a/yarn-project/simulator/src/avm/journal/public_storage.ts +++ b/yarn-project/simulator/src/avm/journal/public_storage.ts @@ -68,16 +68,16 @@ export class PublicStorage { * 4. Not found! Value has never been written to before. Flag it as non-existent and return value zero. * * @param storageAddress - the address of the contract whose storage is being read from - * @param slot - the slot in the contract's storage being read from + * @param contractStorageIndex - Storage index to read in the contract storage. * @returns exists: whether the slot has EVER been written to before, value: the latest value written to slot, or 0 if never written to before */ - public async read(storageAddress: Fr, slot: Fr): Promise { + public async read(storageAddress: Fr, contractStorageIndex: Fr): Promise { let cached = false; // Check this cache and parent's (recursively) - let value = this.readHereOrParent(storageAddress, slot); + let value = this.readHereOrParent(storageAddress, contractStorageIndex); // Finally try the host's Aztec state (a trip to the database) if (!value) { - value = await this.hostPublicStorage.storageRead(storageAddress, slot); + value = await this.hostPublicStorage.storageRead(storageAddress, contractStorageIndex); // TODO(dbanks12): if value retrieved from host storage, we can cache it here // any future reads to the same slot can read from cache instead of more expensive // DB access @@ -94,11 +94,11 @@ export class PublicStorage { * Stage a storage write. * * @param storageAddress - the address of the contract whose storage is being written to - * @param slot - the slot in the contract's storage being written to + * @param contractStorageIndex - Storage index to write in the contract storage. * @param value - the value being written to the slot */ - public write(storageAddress: Fr, slot: Fr, value: Fr) { - this.cache.write(storageAddress, slot, value); + public write(storageAddress: Fr, contractStorageIndex: Fr, value: Fr) { + this.cache.write(storageAddress, contractStorageIndex, value); } /** diff --git a/yarn-project/simulator/src/avm/test_utils.ts b/yarn-project/simulator/src/avm/test_utils.ts index ce65116d5b87..3bd834ec8907 100644 --- a/yarn-project/simulator/src/avm/test_utils.ts +++ b/yarn-project/simulator/src/avm/test_utils.ts @@ -23,8 +23,8 @@ export function mockStorageRead(hs: HostStorage, value: Fr) { } export function mockStorageReadWithMap(hs: HostStorage, mockedStorage: Map) { - (hs.publicStateDb as jest.Mocked).storageRead.mockImplementation((_address, slot) => - Promise.resolve(mockedStorage.get(slot.toBigInt()) ?? Fr.ZERO), + (hs.publicStateDb as jest.Mocked).storageRead.mockImplementation((_address, contractStorageIndex) => + Promise.resolve(mockedStorage.get(contractStorageIndex.toBigInt()) ?? Fr.ZERO), ); } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 304bc11528d3..353387dd42c9 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -226,7 +226,7 @@ export class ClientExecutionContext extends ViewDataOracle { * @returns Array of note data. */ public override async getNotes( - storageSlot: Fr, + storageSlot: Point, numSelects: number, selectByIndexes: number[], selectByOffsets: number[], @@ -272,7 +272,7 @@ export class ClientExecutionContext extends ViewDataOracle { if (n.index !== undefined) { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386) // Should always call computeUniqueNoteHash when publicly created notes include nonces. - const uniqueNoteHash = n.nonce.isZero() ? n.innerNoteHash : computeUniqueNoteHash(n.nonce, n.innerNoteHash); + const uniqueNoteHash = n.nonce.isZero() ? n.innerNoteHashX : computeUniqueNoteHash(n.nonce, n.innerNoteHashX); const siloedNoteHash = siloNoteHash(n.contractAddress, uniqueNoteHash); const noteHashForReadRequest = siloedNoteHash; this.noteHashLeafIndexMap.set(noteHashForReadRequest.toBigInt(), n.index); @@ -289,14 +289,14 @@ export class ClientExecutionContext extends ViewDataOracle { * @param storageSlot - The storage slot. * @param noteTypeId - The type ID of the note. * @param noteItems - The items to be included in a Note. - * @param innerNoteHash - The inner note hash of the new note. + * @param innerNoteHashX - The x coordinate of inner note hash of the new note. * @returns */ public override notifyCreatedNote( - storageSlot: Fr, + storageSlot: Point, noteTypeId: NoteSelector, noteItems: Fr[], - innerNoteHash: Fr, + innerNoteHashX: Fr, counter: number, ) { const note = new Note(noteItems); @@ -307,7 +307,7 @@ export class ClientExecutionContext extends ViewDataOracle { nonce: Fr.ZERO, // Nonce cannot be known during private execution. note, siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note. - innerNoteHash, + innerNoteHashX, }, counter, ); @@ -410,7 +410,7 @@ export class ClientExecutionContext extends ViewDataOracle { */ public override computeEncryptedNoteLog( contractAddress: AztecAddress, - storageSlot: Fr, + storageSlot: Point, noteTypeId: NoteSelector, ovKeys: KeyValidationRequest, ivpkM: Point, diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index 57d0d185ac6b..a5e2a1f39fab 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -8,7 +8,7 @@ import { import { type CompleteAddress, type Header, type KeyValidationRequest } from '@aztec/circuits.js'; import { type FunctionArtifact, type FunctionSelector } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; -import { type Fr } from '@aztec/foundation/fields'; +import { type Fr, type Point } from '@aztec/foundation/fields'; import { type ContractInstance } from '@aztec/types/contracts'; import { type NoteData } from '../acvm/index.js'; @@ -78,12 +78,12 @@ export interface DBOracle extends CommitmentsDB { * The query result is paginated using 'limit' and 'offset' values. * Returns an object containing an array of note data. * - * @param contractAddress - The AztecAddress instance representing the contract address. - * @param storageSlot - The Fr instance representing the storage slot of the notes. + * @param contractAddress - The contract address of the notes. + * @param storageSlot - The storage slot of the notes. * @param status - The status of notes to fetch. * @returns A Promise that resolves to an array of note data. */ - getNotes(contractAddress: AztecAddress, storageSlot: Fr, status: NoteStatus): Promise; + getNotes(contractAddress: AztecAddress, storageSlot: Point, status: NoteStatus): Promise; /** * Retrieve the artifact information of a specific function within a contract. diff --git a/yarn-project/simulator/src/client/execution_note_cache.ts b/yarn-project/simulator/src/client/execution_note_cache.ts index 3615b012c99a..23d7b974a199 100644 --- a/yarn-project/simulator/src/client/execution_note_cache.ts +++ b/yarn-project/simulator/src/client/execution_note_cache.ts @@ -1,6 +1,6 @@ import { siloNullifier } from '@aztec/circuits.js/hash'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, type Point } from '@aztec/foundation/fields'; import { type NoteData } from '../acvm/index.js'; @@ -55,7 +55,7 @@ export class ExecutionNoteCache { // Find and remove the matching new note and log(s) if the emitted innerNoteHash is not empty. if (!innerNoteHash.equals(Fr.ZERO)) { const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; - const noteIndexToRemove = notes.findIndex(n => n.note.innerNoteHash.equals(innerNoteHash)); + const noteIndexToRemove = notes.findIndex(n => n.note.innerNoteHashX.equals(innerNoteHash)); if (noteIndexToRemove === -1) { throw new Error('Attempt to remove a pending note that does not exist.'); } @@ -72,7 +72,7 @@ export class ExecutionNoteCache { * @param contractAddress - Contract address of the notes. * @param storageSlot - Storage slot of the notes. **/ - public getNotes(contractAddress: AztecAddress, storageSlot: Fr) { + public getNotes(contractAddress: AztecAddress, storageSlot: Point) { const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; return notes.filter(n => n.note.storageSlot.equals(storageSlot)).map(n => n.note); } @@ -85,7 +85,7 @@ export class ExecutionNoteCache { **/ public checkNoteExists(contractAddress: AztecAddress, innerNoteHash: Fr) { const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; - return notes.some(n => n.note.innerNoteHash.equals(innerNoteHash)); + return notes.some(n => n.note.innerNoteHashX.equals(innerNoteHash)); } /** diff --git a/yarn-project/simulator/src/client/execution_result.ts b/yarn-project/simulator/src/client/execution_result.ts index d804e08b3865..fe23503664da 100644 --- a/yarn-project/simulator/src/client/execution_result.ts +++ b/yarn-project/simulator/src/client/execution_result.ts @@ -9,7 +9,7 @@ import { } from '@aztec/circuit-types'; import { type IsEmpty, type PrivateCallStackItem, PublicCallRequest, sortByCounter } from '@aztec/circuits.js'; import { type NoteSelector } from '@aztec/foundation/abi'; -import { type Fr } from '@aztec/foundation/fields'; +import { type Fr, type Point } from '@aztec/foundation/fields'; import { type ACVMField } from '../acvm/index.js'; @@ -20,7 +20,7 @@ export interface NoteAndSlot { /** The note. */ note: Note; /** The storage slot of the note. */ - storageSlot: Fr; + storageSlot: Point; /** The note type identifier. */ noteTypeId: NoteSelector; } diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 4c94b92b441f..9ec6fa7a0d24 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -28,11 +28,19 @@ import { TxContext, computeAppNullifierSecretKey, computeOvskApp, + deriveBaseSlot, deriveKeys, + deriveStorageSlotInMap, getContractInstanceFromDeployParams, getNonEmptyItems, } from '@aztec/circuits.js'; -import { computeNoteHashNonce, computeSecretHash, computeVarArgsHash } from '@aztec/circuits.js/hash'; +import { + computeInnerNoteHash, + computeNoteContentHash, + computeNoteHashNonce, + computeSecretHash, + computeVarArgsHash, +} from '@aztec/circuits.js/hash'; import { makeHeader } from '@aztec/circuits.js/testing'; import { type FunctionArtifact, @@ -46,7 +54,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { times } from '@aztec/foundation/collection'; import { pedersenHash, poseidon2Hash, randomInt } from '@aztec/foundation/crypto'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, type Point } from '@aztec/foundation/fields'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { type FieldsOf } from '@aztec/foundation/types'; import { openTmpStore } from '@aztec/kv-store/utils'; @@ -66,7 +74,6 @@ import { toFunctionSelector } from 'viem'; import { MessageLoadOracleInputs } from '../acvm/index.js'; import { buildL1ToL2Message } from '../test/utils.js'; -import { computeSlotForMapping } from '../utils.js'; import { type DBOracle } from './db_oracle.js'; import { type ExecutionResult, collectSortedEncryptedLogs } from './execution_result.js'; import { AcirSimulator } from './simulator.js'; @@ -304,7 +311,7 @@ describe('Private Execution test suite', () => { const mockFirstNullifier = new Fr(1111); let currentNoteIndex = 0n; - const buildNote = (amount: bigint, ownerNpkMHash: Fr, storageSlot: Fr, noteTypeId: NoteSelector) => { + const buildNote = (amount: bigint, ownerNpkMHash: Fr, storageSlot: Point, noteTypeId: NoteSelector) => { // WARNING: this is not actually how nonces are computed! // For the purpose of this test we use a mocked firstNullifier and and a random number // to compute the nonce. Proper nonces are only enforced later by the kernel/later circuits @@ -316,14 +323,14 @@ describe('Private Execution test suite', () => { const noteHashIndex = randomInt(1); // mock index in TX's final noteHashes array const nonce = computeNoteHashNonce(mockFirstNullifier, noteHashIndex); const note = new Note([new Fr(amount), ownerNpkMHash, Fr.random()]); - const innerNoteHash = pedersenHash(note.items); + const innerNoteHashX = computeInnerNoteHash(storageSlot, computeNoteContentHash(note.items)).x; return { contractAddress, storageSlot, noteTypeId, nonce, note, - innerNoteHash, + innerNoteHashX, siloedNullifier: new Fr(0), index: currentNoteIndex++, }; @@ -349,13 +356,13 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(1); const newNote = result.newNotes[0]; - expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(newNote.storageSlot).toEqual(deriveStorageSlotInMap(deriveBaseSlot(new Fr(1n)), owner)); expect(newNote.noteTypeId).toEqual(valueNoteTypeId); // ValueNote const noteHashes = getNonEmptyItems(result.callStackItem.publicInputs.noteHashes); expect(noteHashes).toHaveLength(1); expect(noteHashes[0].value).toEqual( - await acirSimulator.computeInnerNoteHash( + await acirSimulator.computeInnerNoteHashX( contractAddress, newNote.storageSlot, newNote.noteTypeId, @@ -379,13 +386,13 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(1); const newNote = result.newNotes[0]; - expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(newNote.storageSlot).toEqual(deriveStorageSlotInMap(deriveBaseSlot(new Fr(1n)), owner)); expect(newNote.noteTypeId).toEqual(valueNoteTypeId); // ValueNote const noteHashes = getNonEmptyItems(result.callStackItem.publicInputs.noteHashes); expect(noteHashes).toHaveLength(1); expect(noteHashes[0].value).toEqual( - await acirSimulator.computeInnerNoteHash( + await acirSimulator.computeInnerNoteHashX( contractAddress, newNote.storageSlot, newNote.noteTypeId, @@ -406,8 +413,8 @@ describe('Private Execution test suite', () => { const amountToTransfer = 100n; const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); - const storageSlot = computeSlotForMapping(StatefulTestContractArtifact.storageLayout['notes'].slot, owner); - const recipientStorageSlot = computeSlotForMapping( + const storageSlot = deriveStorageSlotInMap(StatefulTestContractArtifact.storageLayout['notes'].slot, owner); + const recipientStorageSlot = deriveStorageSlotInMap( StatefulTestContractArtifact.storageLayout['notes'].slot, recipient, ); @@ -447,13 +454,13 @@ describe('Private Execution test suite', () => { expect(noteHashes).toHaveLength(2); const [changeNoteHash, recipientNoteHash] = noteHashes; const [recipientInnerNoteHash, changeInnerNoteHash] = [ - await acirSimulator.computeInnerNoteHash( + await acirSimulator.computeInnerNoteHashX( contractAddress, recipientStorageSlot, valueNoteTypeId, recipientNote.note, ), - await acirSimulator.computeInnerNoteHash(contractAddress, storageSlot, valueNoteTypeId, changeNote.note), + await acirSimulator.computeInnerNoteHashX(contractAddress, storageSlot, valueNoteTypeId, changeNote.note), ]; expect(recipientNoteHash.value).toEqual(recipientInnerNoteHash); expect(changeNoteHash.value).toEqual(changeInnerNoteHash); @@ -483,7 +490,7 @@ describe('Private Execution test suite', () => { const balance = 160n; const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); - const storageSlot = computeSlotForMapping(new Fr(1n), owner); + const storageSlot = deriveStorageSlotInMap(deriveBaseSlot(new Fr(1n)), owner); const notes = [ buildNote( @@ -835,7 +842,7 @@ describe('Private Execution test suite', () => { storageSlot, nonce: Fr.ZERO, note, - innerNoteHash: Fr.ZERO, + innerNoteHashX: Fr.ZERO, siloedNullifier: Fr.random(), index: 1n, }, @@ -970,7 +977,7 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(1); const noteAndSlot = result.newNotes[0]; - expect(noteAndSlot.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(noteAndSlot.storageSlot).toEqual(deriveStorageSlotInMap(deriveBaseSlot(new Fr(1n)), owner)); expect(noteAndSlot.note.items[0]).toEqual(new Fr(amountToTransfer)); @@ -978,12 +985,12 @@ describe('Private Execution test suite', () => { expect(noteHashes).toHaveLength(1); const noteHash = noteHashes[0].value; - const storageSlot = computeSlotForMapping( + const storageSlot = deriveStorageSlotInMap( PendingNoteHashesContractArtifact.storageLayout['balances'].slot, owner, ); - const innerNoteHash = await acirSimulator.computeInnerNoteHash( + const innerNoteHash = await acirSimulator.computeInnerNoteHashX( contractAddress, storageSlot, valueNoteTypeId, @@ -1051,7 +1058,7 @@ describe('Private Execution test suite', () => { const execInsert = result.nestedExecutions[0]; const execGetThenNullify = result.nestedExecutions[1]; - const storageSlot = computeSlotForMapping( + const storageSlot = deriveStorageSlotInMap( PendingNoteHashesContractArtifact.storageLayout['balances'].slot, owner, ); @@ -1067,7 +1074,7 @@ describe('Private Execution test suite', () => { expect(noteHashes).toHaveLength(1); const noteHash = noteHashes[0].value; - const innerNoteHash = await acirSimulator.computeInnerNoteHash( + const innerNoteHash = await acirSimulator.computeInnerNoteHashX( contractAddress, noteAndSlot.storageSlot, noteAndSlot.noteTypeId, @@ -1138,7 +1145,7 @@ describe('Private Execution test suite', () => { it('fails if returning no notes', async () => { const artifact = getFunctionArtifact(TestContractArtifact, 'call_get_notes'); - const args = [2n, true]; + const args = [deriveBaseSlot(2n).toNoirStruct(), true]; oracle.getNotes.mockResolvedValue([]); await expect(() => runSimulator({ artifact, args })).rejects.toThrow( diff --git a/yarn-project/simulator/src/client/simulator.test.ts b/yarn-project/simulator/src/client/simulator.test.ts index a761bb4df8f3..14dd58e88fcf 100644 --- a/yarn-project/simulator/src/client/simulator.test.ts +++ b/yarn-project/simulator/src/client/simulator.test.ts @@ -62,9 +62,9 @@ describe('Simulator', () => { oracle.getFunctionArtifactByName.mockResolvedValue(artifact); const note = createNote(); - const tokenNoteHash = computeNoteContentHash(note.items); - const innerNoteHash = computeInnerNoteHash(storageSlot, tokenNoteHash); - const uniqueNoteHash = computeUniqueNoteHash(nonce, innerNoteHash); + const noteContentHash = computeNoteContentHash(note.items); + const innerNoteHashX = computeInnerNoteHash(storageSlot, noteContentHash).x; + const uniqueNoteHash = computeUniqueNoteHash(nonce, innerNoteHashX); const siloedNoteHash = siloNoteHash(contractAddress, uniqueNoteHash); const innerNullifier = poseidon2Hash([siloedNoteHash, appNullifierSecretKey, GeneratorIndex.NOTE_NULLIFIER]); @@ -78,7 +78,7 @@ describe('Simulator', () => { ); expect(result).toEqual({ - innerNoteHash, + innerNoteHashX, uniqueNoteHash, siloedNoteHash, innerNullifier, diff --git a/yarn-project/simulator/src/client/simulator.ts b/yarn-project/simulator/src/client/simulator.ts index 0f31da457801..498988a7dd38 100644 --- a/yarn-project/simulator/src/client/simulator.ts +++ b/yarn-project/simulator/src/client/simulator.ts @@ -9,7 +9,7 @@ import { encodeArguments, } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, type Point } from '@aztec/foundation/fields'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { createSimulationError } from '../common/errors.js'; @@ -139,7 +139,7 @@ export class AcirSimulator { public async computeNoteHashAndOptionallyANullifier( contractAddress: AztecAddress, nonce: Fr, - storageSlot: Fr, + storageSlot: Point, noteTypeId: NoteSelector, computeNullifier: boolean, note: Note, @@ -180,7 +180,7 @@ export class AcirSimulator { args: encodeArguments(artifact, [ contractAddress, nonce, - storageSlot, + storageSlot.toNoirStruct(), noteTypeId, computeNullifier, extendedNoteItems, @@ -188,14 +188,14 @@ export class AcirSimulator { returnTypes: artifact.returnTypes, }; - const [innerNoteHash, uniqueNoteHash, siloedNoteHash, innerNullifier] = (await this.runUnconstrained( + const [innerNoteHashX, uniqueNoteHash, siloedNoteHash, innerNullifier] = (await this.runUnconstrained( execRequest, artifact, contractAddress, )) as bigint[]; return { - innerNoteHash: new Fr(innerNoteHash), + innerNoteHashX: new Fr(innerNoteHashX), uniqueNoteHash: new Fr(uniqueNoteHash), siloedNoteHash: new Fr(siloedNoteHash), innerNullifier: new Fr(innerNullifier), @@ -210,13 +210,13 @@ export class AcirSimulator { * @param note - The note. * @returns The note hash. */ - public async computeInnerNoteHash( + public async computeInnerNoteHashX( contractAddress: AztecAddress, - storageSlot: Fr, + storageSlot: Point, noteTypeId: NoteSelector, note: Note, ) { - const { innerNoteHash } = await this.computeNoteHashAndOptionallyANullifier( + const { innerNoteHashX } = await this.computeNoteHashAndOptionallyANullifier( contractAddress, Fr.ZERO, storageSlot, @@ -224,6 +224,6 @@ export class AcirSimulator { false, note, ); - return innerNoteHash; + return innerNoteHashX; } } diff --git a/yarn-project/simulator/src/client/unconstrained_execution.test.ts b/yarn-project/simulator/src/client/unconstrained_execution.test.ts index fbca7486e34e..64d0a5882839 100644 --- a/yarn-project/simulator/src/client/unconstrained_execution.test.ts +++ b/yarn-project/simulator/src/client/unconstrained_execution.test.ts @@ -2,7 +2,7 @@ import { type AztecNode, type FunctionCall, Note } from '@aztec/circuit-types'; import { CompleteAddress, Header } from '@aztec/circuits.js'; import { FunctionSelector, FunctionType, encodeArguments } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, Point } from '@aztec/foundation/fields'; import { StatefulTestContractArtifact } from '@aztec/noir-contracts.js/StatefulTest'; import { mock } from 'jest-mock-extended'; @@ -57,11 +57,11 @@ describe('Unconstrained Execution test suite', () => { oracle.getNotes.mockResolvedValue( notes.map((note, index) => ({ contractAddress, - storageSlot: Fr.random(), + storageSlot: Point.random(), nonce: Fr.random(), isSome: new Fr(1), note, - innerNoteHash: Fr.random(), + innerNoteHashX: Fr.random(), siloedNullifier: Fr.random(), index: BigInt(index), })), diff --git a/yarn-project/simulator/src/client/view_data_oracle.ts b/yarn-project/simulator/src/client/view_data_oracle.ts index 75e7f1802338..791504ff6838 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -10,7 +10,7 @@ import { import { type Header, type KeyValidationRequest } from '@aztec/circuits.js'; import { siloNullifier } from '@aztec/circuits.js/hash'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, type Point } from '@aztec/foundation/fields'; import { applyStringFormatting, createDebugLogger } from '@aztec/foundation/log'; import { type ContractInstance } from '@aztec/types/contracts'; @@ -204,7 +204,7 @@ export class ViewDataOracle extends TypedOracle { * @returns Array of note data. */ public override async getNotes( - storageSlot: Fr, + storageSlot: Point, numSelects: number, selectByIndexes: number[], selectByOffsets: number[], diff --git a/yarn-project/simulator/src/index.ts b/yarn-project/simulator/src/index.ts index 444584e171a5..fc5a4653413f 100644 --- a/yarn-project/simulator/src/index.ts +++ b/yarn-project/simulator/src/index.ts @@ -6,4 +6,3 @@ export * from './public/index.js'; export * from './providers/index.js'; export * from './mocks/index.js'; export * from './stats/index.js'; -export * from './utils.js'; diff --git a/yarn-project/simulator/src/public/db_interfaces.ts b/yarn-project/simulator/src/public/db_interfaces.ts index ca44044de82d..e71a13fc7288 100644 --- a/yarn-project/simulator/src/public/db_interfaces.ts +++ b/yarn-project/simulator/src/public/db_interfaces.ts @@ -13,19 +13,19 @@ export interface PublicStateDB { /** * Reads a value from public storage, returning zero if none. * @param contract - Owner of the storage. - * @param slot - Slot to read in the contract storage. + * @param contractStorageIndex - Storage index to read in the contract storage. * @returns The current value in the storage slot. */ - storageRead(contract: AztecAddress, slot: Fr): Promise; + storageRead(contract: AztecAddress, contractStorageIndex: Fr): Promise; /** * Records a write to public storage. * @param contract - Owner of the storage. - * @param slot - Slot to read in the contract storage. + * @param contractStorageIndex - Storage index to write in the contract storage. * @param newValue - The new value to store. * @returns The slot of the written leaf in the public data tree. */ - storageWrite(contract: AztecAddress, slot: Fr, newValue: Fr): Promise; + storageWrite(contract: AztecAddress, contractStorageIndex: Fr, newValue: Fr): Promise; /** * Mark the uncommitted changes in this TX as a checkpoint. diff --git a/yarn-project/simulator/src/public/fee_payment.ts b/yarn-project/simulator/src/public/fee_payment.ts index 721f9055d4e7..2190100930e3 100644 --- a/yarn-project/simulator/src/public/fee_payment.ts +++ b/yarn-project/simulator/src/public/fee_payment.ts @@ -2,25 +2,25 @@ import { GAS_TOKEN_ADDRESS } from '@aztec/circuits.js'; import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; -import { GasTokenArtifact } from '@aztec/protocol-contracts/gas-token'; - -import { computeSlotForMapping } from '../utils.js'; /** * Computes the storage slot within the gas token contract for the balance of the fee payer. */ -export function computeFeePayerBalanceStorageSlot(feePayer: AztecAddress) { - return computeSlotForMapping(GasTokenArtifact.storageLayout.balances.slot, feePayer); +export function computeFeePayerBalanceContractStorageIndex(feePayer: AztecAddress): Fr { + // return deriveStorageSlotInMap(GasTokenArtifact.storageLayout.balances.slot, feePayer).x; + // TODO(#7604): nuke the following and uncomment above + return feePayer.toField(); } /** * Computes the leaf slot in the public data tree for the balance of the fee payer in the gas token. + * TODO(#7551): rename leaf slot to storage_index once we decide upon the naming changes. */ export function computeFeePayerBalanceLeafSlot(feePayer: AztecAddress): Fr { if (feePayer.isZero()) { return Fr.ZERO; } const gasToken = AztecAddress.fromBigInt(GAS_TOKEN_ADDRESS); - const balanceSlot = computeFeePayerBalanceStorageSlot(feePayer); - return computePublicDataTreeLeafSlot(gasToken, balanceSlot); + const feePayerBalanceContractStorageIndex = computeFeePayerBalanceContractStorageIndex(feePayer); + return computePublicDataTreeLeafSlot(gasToken, feePayerBalanceContractStorageIndex); } diff --git a/yarn-project/simulator/src/public/public_db_sources.test.ts b/yarn-project/simulator/src/public/public_db_sources.test.ts index d3a66358d1da..ffc36c84fcc6 100644 --- a/yarn-project/simulator/src/public/public_db_sources.test.ts +++ b/yarn-project/simulator/src/public/public_db_sources.test.ts @@ -14,17 +14,17 @@ describe('world_state_public_db', () => { let db: MockProxy; let dbStorage: Map>; let addresses: AztecAddress[]; - let slots: Fr[]; + let contractStorageIndices: Fr[]; let dbValues: Fr[]; beforeEach(() => { addresses = Array(DB_VALUES_SIZE).fill(0).map(AztecAddress.random); - slots = Array(DB_VALUES_SIZE).fill(0).map(Fr.random); + contractStorageIndices = Array(DB_VALUES_SIZE).fill(0).map(Fr.random); dbValues = Array(DB_VALUES_SIZE).fill(0).map(Fr.random); const publicDataEntries = Array(DB_VALUES_SIZE) .fill(0) .map((_, idx: number) => { - const leafSlot = computePublicDataTreeLeafSlot(addresses[idx], slots[idx]); + const leafSlot = computePublicDataTreeLeafSlot(addresses[idx], contractStorageIndices[idx]); return new PublicDataTreeLeafPreimage(leafSlot, dbValues[idx], Fr.ZERO, 0n); }); dbStorage = new Map>([ @@ -72,149 +72,149 @@ describe('world_state_public_db', () => { it('reads unwritten value from merkle tree db', async function () { const publicStateDb = new WorldStatePublicDB(db); - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(dbValues[0]); - expect(await publicStateDb.storageRead(addresses[1], slots[1])).toEqual(dbValues[1]); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(dbValues[0]); + expect(await publicStateDb.storageRead(addresses[1], contractStorageIndices[1])).toEqual(dbValues[1]); }); it('reads uncommitted value back', async function () { const publicStateDb = new WorldStatePublicDB(db); - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(dbValues[0]); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(dbValues[0]); const newValue = new Fr(dbValues[0].toBigInt() + 1n); // write a new value to our first value - await publicStateDb.storageWrite(addresses[0], slots[0], newValue); + await publicStateDb.storageWrite(addresses[0], contractStorageIndices[0], newValue); // should read back the uncommitted value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue); // other slots should be unchanged - expect(await publicStateDb.storageRead(addresses[1], slots[1])).toEqual(dbValues[1]); + expect(await publicStateDb.storageRead(addresses[1], contractStorageIndices[1])).toEqual(dbValues[1]); }); it('reads committed value back', async function () { const publicStateDb = new WorldStatePublicDB(db); - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(dbValues[0]); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(dbValues[0]); const newValue = new Fr(dbValues[0].toBigInt() + 1n); // write a new value to our first value - await publicStateDb.storageWrite(addresses[0], slots[0], newValue); + await publicStateDb.storageWrite(addresses[0], contractStorageIndices[0], newValue); // commit the data await publicStateDb.commit(); // should read back the committed value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue); // other slots should be unchanged - expect(await publicStateDb.storageRead(addresses[1], slots[1])).toEqual(dbValues[1]); + expect(await publicStateDb.storageRead(addresses[1], contractStorageIndices[1])).toEqual(dbValues[1]); }); it('will not rollback a committed value', async function () { const publicStateDb = new WorldStatePublicDB(db); - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(dbValues[0]); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(dbValues[0]); const newValue = new Fr(dbValues[0].toBigInt() + 1n); // write a new value to our first value - await publicStateDb.storageWrite(addresses[0], slots[0], newValue); + await publicStateDb.storageWrite(addresses[0], contractStorageIndices[0], newValue); // commit the data await publicStateDb.commit(); // should read back the committed value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue); await publicStateDb.rollbackToCommit(); // should still read back the committed value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue); }); it('reads original value if rolled back uncommitted value', async function () { const publicStateDb = new WorldStatePublicDB(db); - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(dbValues[0]); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(dbValues[0]); const newValue = new Fr(dbValues[0].toBigInt() + 1n); // write a new value to our first value - await publicStateDb.storageWrite(addresses[0], slots[0], newValue); + await publicStateDb.storageWrite(addresses[0], contractStorageIndices[0], newValue); // should read back the uncommitted value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue); // now rollback await publicStateDb.rollbackToCommit(); // should now read the original value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(dbValues[0]); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(dbValues[0]); }); it('reads newly uncommitted value back', async function () { const publicStateDb = new WorldStatePublicDB(db); - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(dbValues[0]); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(dbValues[0]); const newValue = new Fr(dbValues[0].toBigInt() + 1n); // write a new value to our first value - await publicStateDb.storageWrite(addresses[0], slots[0], newValue); + await publicStateDb.storageWrite(addresses[0], contractStorageIndices[0], newValue); // commit the data await publicStateDb.commit(); // should read back the committed value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue); // other slots should be unchanged - expect(await publicStateDb.storageRead(addresses[1], slots[1])).toEqual(dbValues[1]); + expect(await publicStateDb.storageRead(addresses[1], contractStorageIndices[1])).toEqual(dbValues[1]); // now update the slot again const newValue2 = new Fr(dbValues[0].toBigInt() + 2n); // write a new value to our first value - await publicStateDb.storageWrite(addresses[0], slots[0], newValue2); + await publicStateDb.storageWrite(addresses[0], contractStorageIndices[0], newValue2); // should read back the uncommitted value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue2); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue2); }); it('rolls back to previously committed value', async function () { const publicStateDb = new WorldStatePublicDB(db); - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(dbValues[0]); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(dbValues[0]); const newValue = new Fr(dbValues[0].toBigInt() + 1n); // write a new value to our first value - await publicStateDb.storageWrite(addresses[0], slots[0], newValue); + await publicStateDb.storageWrite(addresses[0], contractStorageIndices[0], newValue); // commit the data await publicStateDb.commit(); // should read back the committed value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue); // other slots should be unchanged - expect(await publicStateDb.storageRead(addresses[1], slots[1])).toEqual(dbValues[1]); + expect(await publicStateDb.storageRead(addresses[1], contractStorageIndices[1])).toEqual(dbValues[1]); // now update the slot again const newValue2 = new Fr(dbValues[0].toBigInt() + 2n); // write a new value to our first value - await publicStateDb.storageWrite(addresses[0], slots[0], newValue2); + await publicStateDb.storageWrite(addresses[0], contractStorageIndices[0], newValue2); // should read back the uncommitted value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue2); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue2); // rollback await publicStateDb.rollbackToCommit(); // should read back the previously committed value - expect(await publicStateDb.storageRead(addresses[0], slots[0])).toEqual(newValue); + expect(await publicStateDb.storageRead(addresses[0], contractStorageIndices[0])).toEqual(newValue); }); it('can use checkpoints', async function () { const publicStateDb = new WorldStatePublicDB(db); - const read = () => publicStateDb.storageRead(addresses[0], slots[0]); - const write = (value: Fr) => publicStateDb.storageWrite(addresses[0], slots[0], value); + const read = () => publicStateDb.storageRead(addresses[0], contractStorageIndices[0]); + const write = (value: Fr) => publicStateDb.storageWrite(addresses[0], contractStorageIndices[0], value); const newValue = new Fr(dbValues[0].toBigInt() + 1n); const newValue2 = new Fr(dbValues[0].toBigInt() + 2n); diff --git a/yarn-project/simulator/src/public/public_db_sources.ts b/yarn-project/simulator/src/public/public_db_sources.ts index da99bb02d4ec..15a9192159dc 100644 --- a/yarn-project/simulator/src/public/public_db_sources.ts +++ b/yarn-project/simulator/src/public/public_db_sources.ts @@ -127,11 +127,11 @@ export class WorldStatePublicDB implements PublicStateDB { /** * Reads a value from public storage, returning zero if none. * @param contract - Owner of the storage. - * @param slot - Slot to read in the contract storage. + * @param contractStorageIndex - Storage index to read in the contract storage. * @returns The current value in the storage slot. */ - public async storageRead(contract: AztecAddress, slot: Fr): Promise { - const leafSlot = computePublicDataTreeLeafSlot(contract, slot).value; + public async storageRead(contract: AztecAddress, contractStorageIndex: Fr): Promise { + const leafSlot = computePublicDataTreeLeafSlot(contract, contractStorageIndex).toBigInt(); const uncommitted = this.uncommittedWriteCache.get(leafSlot); if (uncommitted !== undefined) { return uncommitted; @@ -161,12 +161,12 @@ export class WorldStatePublicDB implements PublicStateDB { /** * Records a write to public storage. * @param contract - Owner of the storage. - * @param slot - Slot to read in the contract storage. + * @param contractStorageIndex - Storage index to write in the contract storage. * @param newValue - The new value to store. * @returns The slot of the written leaf in the public data tree. */ - public storageWrite(contract: AztecAddress, slot: Fr, newValue: Fr): Promise { - const index = computePublicDataTreeLeafSlot(contract, slot).value; + public storageWrite(contract: AztecAddress, contractStorageIndex: Fr, newValue: Fr): Promise { + const index = computePublicDataTreeLeafSlot(contract, contractStorageIndex).toBigInt(); this.uncommittedWriteCache.set(index, newValue); return Promise.resolve(index); } diff --git a/yarn-project/simulator/src/public/public_processor.test.ts b/yarn-project/simulator/src/public/public_processor.test.ts index b39fff577826..ce4c0f838030 100644 --- a/yarn-project/simulator/src/public/public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor.test.ts @@ -1109,8 +1109,8 @@ describe('public_processor', () => { }); publicWorldStateDB.storageRead.mockResolvedValue(new Fr(initialBalance)); - publicWorldStateDB.storageWrite.mockImplementation((address: AztecAddress, slot: Fr) => - Promise.resolve(computePublicDataTreeLeafSlot(address, slot).toBigInt()), + publicWorldStateDB.storageWrite.mockImplementation((address: AztecAddress, contractStorageIndex: Fr) => + Promise.resolve(computePublicDataTreeLeafSlot(address, contractStorageIndex).toBigInt()), ); const [processed, failed] = await processor.process([tx], 1, prover); @@ -1149,8 +1149,8 @@ describe('public_processor', () => { }); publicWorldStateDB.storageRead.mockResolvedValue(new Fr(initialBalance)); - publicWorldStateDB.storageWrite.mockImplementation((address: AztecAddress, slot: Fr) => - Promise.resolve(computePublicDataTreeLeafSlot(address, slot).toBigInt()), + publicWorldStateDB.storageWrite.mockImplementation((address: AztecAddress, contractStorageIndex: Fr) => + Promise.resolve(computePublicDataTreeLeafSlot(address, contractStorageIndex).toBigInt()), ); publicExecutor.simulate.mockImplementation(execution => { @@ -1201,8 +1201,8 @@ describe('public_processor', () => { }); publicWorldStateDB.storageRead.mockResolvedValue(Fr.ZERO); - publicWorldStateDB.storageWrite.mockImplementation((address: AztecAddress, slot: Fr) => - Promise.resolve(computePublicDataTreeLeafSlot(address, slot).toBigInt()), + publicWorldStateDB.storageWrite.mockImplementation((address: AztecAddress, contractStorageIndex: Fr) => + Promise.resolve(computePublicDataTreeLeafSlot(address, contractStorageIndex).toBigInt()), ); publicExecutor.simulate.mockImplementation(execution => { @@ -1259,8 +1259,8 @@ describe('public_processor', () => { }); publicWorldStateDB.storageRead.mockResolvedValue(new Fr(initialBalance)); - publicWorldStateDB.storageWrite.mockImplementation((address: AztecAddress, slot: Fr) => - Promise.resolve(computePublicDataTreeLeafSlot(address, slot).toBigInt()), + publicWorldStateDB.storageWrite.mockImplementation((address: AztecAddress, contractStorageIndex: Fr) => + Promise.resolve(computePublicDataTreeLeafSlot(address, contractStorageIndex).toBigInt()), ); const [processed, failed] = await processor.process([tx], 1, prover); diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index b47d347b4649..3564a6263e82 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -28,8 +28,8 @@ import { PublicExecutor, type PublicStateDB, type SimulationProvider, + computeFeePayerBalanceContractStorageIndex, computeFeePayerBalanceLeafSlot, - computeFeePayerBalanceStorageSlot, } from '@aztec/simulator'; import { Attributes, type TelemetryClient, type Tracer, trackSpan } from '@aztec/telemetry-client'; import { type ContractDataSource } from '@aztec/types/contracts'; @@ -189,7 +189,7 @@ export class PublicProcessor { } const gasToken = AztecAddress.fromBigInt(GAS_TOKEN_ADDRESS); - const balanceSlot = computeFeePayerBalanceStorageSlot(feePayer); + const balanceSlot = computeFeePayerBalanceContractStorageIndex(feePayer); const leafSlot = computeFeePayerBalanceLeafSlot(feePayer); const txFee = tx.data.getTransactionFee(this.globalVariables.gasFees); diff --git a/yarn-project/simulator/src/public/side_effect_trace_interface.ts b/yarn-project/simulator/src/public/side_effect_trace_interface.ts index 91326fb021b4..51324e6d85e7 100644 --- a/yarn-project/simulator/src/public/side_effect_trace_interface.ts +++ b/yarn-project/simulator/src/public/side_effect_trace_interface.ts @@ -8,8 +8,14 @@ import { type TracedContractInstance } from './side_effect_trace.js'; export interface PublicSideEffectTraceInterface { fork(): PublicSideEffectTraceInterface; getCounter(): number; - tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr, exists: boolean, cached: boolean): void; - tracePublicStorageWrite(storageAddress: Fr, slot: Fr, value: Fr): void; + tracePublicStorageRead( + storageAddress: Fr, + contractStorageIndex: Fr, + value: Fr, + exists: boolean, + cached: boolean, + ): void; + tracePublicStorageWrite(storageAddress: Fr, contractStorageIndex: Fr, value: Fr): void; traceNoteHashCheck(storageAddress: Fr, noteHash: Fr, leafIndex: Fr, exists: boolean): void; traceNewNoteHash(storageAddress: Fr, noteHash: Fr): void; traceNullifierCheck(storageAddress: Fr, nullifier: Fr, leafIndex: Fr, exists: boolean, isPending: boolean): void; diff --git a/yarn-project/simulator/src/utils.ts b/yarn-project/simulator/src/utils.ts deleted file mode 100644 index 380222fa5785..000000000000 --- a/yarn-project/simulator/src/utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { pedersenHash } from '@aztec/foundation/crypto'; -import { type Fr } from '@aztec/foundation/fields'; - -/** - * Computes the resulting storage slot for an entry in a mapping. - * @param mappingSlot - The slot of the mapping within state. - * @param key - The key of the mapping. - * @returns The slot in the contract storage where the value is stored. - */ -export function computeSlotForMapping( - mappingSlot: Fr, - key: { - /** Serialize to a field. */ - toField: () => Fr; - }, -) { - return pedersenHash([mappingSlot, key.toField()]); -} diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 7fcaebffdb95..45e366c2c7b3 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -363,7 +363,7 @@ export class TXE implements TypedOracle { } getNotes( - storageSlot: Fr, + storageSlot: Point, numSelects: number, selectByIndexes: number[], selectByOffsets: number[], @@ -404,7 +404,13 @@ export class TXE implements TypedOracle { return Promise.resolve(notes); } - notifyCreatedNote(storageSlot: Fr, noteTypeId: NoteSelector, noteItems: Fr[], innerNoteHash: Fr, counter: number) { + notifyCreatedNote( + storageSlot: Point, + noteTypeId: NoteSelector, + noteItems: Fr[], + innerNoteHashX: Fr, + counter: number, + ) { const note = new Note(noteItems); this.noteCache.addNewNote( { @@ -413,7 +419,7 @@ export class TXE implements TypedOracle { nonce: Fr.ZERO, // Nonce cannot be known during private execution. note, siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note. - innerNoteHash, + innerNoteHashX, }, counter, ); @@ -522,7 +528,7 @@ export class TXE implements TypedOracle { computeEncryptedNoteLog( contractAddress: AztecAddress, - storageSlot: Fr, + storageSlot: Point, noteTypeId: NoteSelector, ovKeys: KeyValidationRequest, ivpkM: Point, diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index a8f772931ca1..102b62614286 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -378,7 +378,9 @@ export class TXEService { } async getNotes( - storageSlot: ForeignCallSingle, + storageSlotX: ForeignCallSingle, + storageSlotY: ForeignCallSingle, + storageSlotIsInfinite: ForeignCallSingle, numSelects: ForeignCallSingle, selectByIndexes: ForeignCallArray, selectByOffsets: ForeignCallArray, @@ -394,8 +396,13 @@ export class TXEService { status: ForeignCallSingle, returnSize: ForeignCallSingle, ) { + const storageSlot = new Point( + fromSingle(storageSlotX), + fromSingle(storageSlotY), + !fromSingle(storageSlotIsInfinite).isZero(), + ); const noteDatas = await this.typedOracle.getNotes( - fromSingle(storageSlot), + storageSlot, fromSingle(numSelects).toNumber(), fromArray(selectByIndexes).map(fr => fr.toNumber()), fromArray(selectByOffsets).map(fr => fr.toNumber()), @@ -439,17 +446,24 @@ export class TXEService { } notifyCreatedNote( - storageSlot: ForeignCallSingle, + storageSlotX: ForeignCallSingle, + storageSlotY: ForeignCallSingle, + storageSlotIsInfinite: ForeignCallSingle, noteTypeId: ForeignCallSingle, note: ForeignCallArray, - innerNoteHash: ForeignCallSingle, + innerNoteHashX: ForeignCallSingle, counter: ForeignCallSingle, ) { + const storageSlot = new Point( + fromSingle(storageSlotX), + fromSingle(storageSlotY), + !fromSingle(storageSlotIsInfinite).isZero(), + ); this.typedOracle.notifyCreatedNote( - fromSingle(storageSlot), + storageSlot, NoteSelector.fromField(fromSingle(noteTypeId)), fromArray(note), - fromSingle(innerNoteHash), + fromSingle(innerNoteHashX), fromSingle(counter).toNumber(), ); return toForeignCallResult([toSingle(new Fr(0))]); @@ -583,7 +597,9 @@ export class TXEService { computeEncryptedNoteLog( contractAddress: ForeignCallSingle, - storageSlot: ForeignCallSingle, + storageSlotX: ForeignCallSingle, + storageSlotY: ForeignCallSingle, + storageSlotIsInfinite: ForeignCallSingle, noteTypeId: ForeignCallSingle, ovskApp: ForeignCallSingle, ovpkMX: ForeignCallSingle, @@ -595,12 +611,17 @@ export class TXEService { recipient: ForeignCallSingle, preimage: ForeignCallArray, ) { + const storageSlot = new Point( + fromSingle(storageSlotX), + fromSingle(storageSlotY), + !fromSingle(storageSlotIsInfinite).isZero(), + ); const ovpkM = new Point(fromSingle(ovpkMX), fromSingle(ovpkMY), !fromSingle(ovpkMIsInfinite).isZero()); const ovKeys = new KeyValidationRequest(ovpkM, Fr.fromString(fromSingle(ovskApp).toString())); const ivpkM = new Point(fromSingle(ivpkMX), fromSingle(ivpkMY), !fromSingle(ivpkMIsInfinite).isZero()); const encLog = this.typedOracle.computeEncryptedNoteLog( AztecAddress.fromString(fromSingle(contractAddress).toString()), - Fr.fromString(fromSingle(storageSlot).toString()), + storageSlot, NoteSelector.fromField(Fr.fromString(fromSingle(noteTypeId).toString())), ovKeys, ivpkM, diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index e8cbbb3aabf5..3c55efc71d2b 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -7,12 +7,11 @@ import { type FieldLayout, type FunctionArtifact, FunctionType, - type IntegerValue, NoteSelector, type StructValue, type TypedStructFieldValue, } from '@aztec/foundation/abi'; -import { Fr } from '@aztec/foundation/fields'; +import { Fr, Point } from '@aztec/foundation/fields'; import { AZTEC_INITIALIZER_ATTRIBUTE, @@ -64,6 +63,9 @@ export function contractArtifactFromBuffer(buffer: Buffer): ContractArtifact { if (typeof value === 'object' && value !== null && value.type === 'Fr') { return new Fr(BigInt(value.value)); } + if (typeof value === 'object' && value !== null && value.type === 'Point') { + return Point.fromString(value.value); + } return value; }); } @@ -229,9 +231,11 @@ function getStorageLayout(input: NoirCompiledContract) { return storageFields.reduce((acc: Record, field) => { const name = field.name; - const slot = field.value.fields[0].value as IntegerValue; + const val = field.value.fields[0].value as any; + const slotX = Fr.fromString(val.fields[0].value.value); + const slotY = Fr.fromString(val.fields[1].value.value); acc[name] = { - slot: Fr.fromString(slot.value), + slot: new Point(slotX, slotY, false), }; return acc; }, {});