diff --git a/noir-projects/aztec-nr/aztec/src/initializer.nr b/noir-projects/aztec-nr/aztec/src/initializer.nr index 756debb09834..e454776e55ff 100644 --- a/noir-projects/aztec-nr/aztec/src/initializer.nr +++ b/noir-projects/aztec-nr/aztec/src/initializer.nr @@ -1,37 +1,73 @@ use dep::protocol_types::{ - hash::{silo_nullifier, pedersen_hash}, constants::GENERATOR_INDEX__CONSTRUCTOR, - abis::function_selector::FunctionSelector + address::AztecAddress, hash::{silo_nullifier, pedersen_hash}, + constants::GENERATOR_INDEX__CONSTRUCTOR, abis::function_selector::FunctionSelector }; use crate::{ - context::{PrivateContext, PublicContext, ContextInterface}, + context::{PrivateContext, PublicContext, AvmContext, ContextInterface}, oracle::get_contract_instance::get_contract_instance, history::nullifier_inclusion::prove_nullifier_inclusion }; -pub fn mark_as_initialized(context: &mut TContext) where TContext: ContextInterface { - let init_nullifier = compute_unsiloed_contract_initialization_nullifier(*context); +pub fn mark_as_initialized_public(context: &mut PublicContext) { + mark_as_initialized(context); +} + +pub fn mark_as_initialized_avm(context: &mut AvmContext) { + let init_nullifier = compute_unsiloed_contract_initialization_nullifier(context.this_address()); + context.push_new_nullifier(init_nullifier, 0); +} + +pub fn mark_as_initialized_private(context: &mut PrivateContext) { + mark_as_initialized(context); +} + +fn mark_as_initialized(context: &mut TContext) where TContext: ContextInterface { + let init_nullifier = compute_unsiloed_contract_initialization_nullifier((*context).this_address()); ContextInterface::push_new_nullifier(context, init_nullifier, 0); } -pub fn assert_is_initialized(context: &mut TContext) where TContext: ContextInterface { - let init_nullifier = compute_contract_initialization_nullifier(*context); +pub fn assert_is_initialized_public(context: &mut PublicContext) { + let init_nullifier = compute_contract_initialization_nullifier(context.this_address()); prove_nullifier_inclusion(init_nullifier, *context); } -pub fn compute_contract_initialization_nullifier(context: TContext) -> Field where TContext: ContextInterface { - let address = context.this_address(); +pub fn assert_is_initialized_avm(context: &mut AvmContext) { + // FIXME: AVM simulator does not correctly handle nullifier siloing. + // let init_nullifier = compute_contract_initialization_nullifier(context.this_address()); + let init_nullifier = compute_unsiloed_contract_initialization_nullifier(context.this_address()); + assert(context.nullifier_exists(init_nullifier)); +} + +pub fn assert_is_initialized_private(context: &mut PrivateContext) { + let init_nullifier = compute_contract_initialization_nullifier(context.this_address()); + prove_nullifier_inclusion(init_nullifier, *context); +} + +fn compute_contract_initialization_nullifier(address: AztecAddress) -> Field { silo_nullifier( address, - compute_unsiloed_contract_initialization_nullifier(context) + compute_unsiloed_contract_initialization_nullifier(address) ) } -pub fn compute_unsiloed_contract_initialization_nullifier(context: TContext) -> Field where TContext: ContextInterface { - context.this_address().to_field() +fn compute_unsiloed_contract_initialization_nullifier(address: AztecAddress) -> Field { + address.to_field() +} + +pub fn assert_initialization_matches_address_preimage_public(context: PublicContext) { + assert_initialization_matches_address_preimage(context); +} + +pub fn assert_initialization_matches_address_preimage_avm(context: AvmContext) { + assert_initialization_matches_address_preimage(context); +} + +pub fn assert_initialization_matches_address_preimage_private(context: PrivateContext) { + assert_initialization_matches_address_preimage(context); } -pub fn assert_initialization_matches_address_preimage(context: TContext) where TContext: ContextInterface { +fn assert_initialization_matches_address_preimage(context: TContext) where TContext: ContextInterface { let address = context.this_address(); let instance = get_contract_instance(address); let expected_init = compute_initialization_hash(context.selector(), context.get_args_hash()); 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 442d6c2fdfbc..75e5cc00dd51 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 @@ -49,12 +49,11 @@ contract AvmTest { /************************************************************************ * Storage ************************************************************************/ - // FIX: calls unsupported getNullifierMembershipWitness. - // #[aztec(public-vm)] - // #[aztec(initializer)] - // fn constructor() { - // storage.immutable.initialize(42); - // } + #[aztec(public-vm)] + #[aztec(initializer)] + fn constructor() { + storage.immutable.initialize(42); + } unconstrained fn view_storage_immutable() -> pub Field { storage.immutable.read() diff --git a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr index d96acfef3fc8..ea4f6ba50c01 100644 --- a/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/stateful_test_contract/src/main.nr @@ -4,7 +4,7 @@ contract StatefulTest { use dep::value_note::{balance_utils, utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote}}; use dep::aztec::{ deploy::deploy_contract as aztec_deploy_contract, context::{PublicContext, Context}, - oracle::get_contract_instance::get_contract_instance, initializer::assert_is_initialized + oracle::get_contract_instance::get_contract_instance, initializer::assert_is_initialized_private }; struct Storage { @@ -45,7 +45,7 @@ contract StatefulTest { #[aztec(private)] fn destroy_and_create(recipient: AztecAddress, amount: Field) { - assert_is_initialized(&mut context); + assert_is_initialized_private(&mut context); let sender = context.msg_sender(); let sender_notes = storage.notes.at(sender); diff --git a/noir/noir-repo/aztec_macros/src/transforms/functions.rs b/noir/noir-repo/aztec_macros/src/transforms/functions.rs index 9844abc30fe2..870f42c8d060 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/functions.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/functions.rs @@ -45,13 +45,13 @@ pub fn transform_function( // Add initialization check if insert_init_check { - let init_check = create_init_check(); + let init_check = create_init_check(ty); func.def.body.statements.insert(0, init_check); } // Add assertion for initialization arguments and sender if is_initializer { - func.def.body.statements.insert(0, create_assert_initializer()); + func.def.body.statements.insert(0, create_assert_initializer(ty)); } // Add access to the storage struct @@ -85,7 +85,7 @@ pub fn transform_function( // Before returning mark the contract as initialized if is_initializer { - let mark_initialized = create_mark_as_initialized(); + let mark_initialized = create_mark_as_initialized(ty); func.def.body.statements.push(mark_initialized); } @@ -159,9 +159,10 @@ fn create_inputs(ty: &str) -> Param { /// ```noir /// assert_is_initialized(&mut context); /// ``` -fn create_init_check() -> Statement { +fn create_init_check(ty: &str) -> Statement { + let fname = format!("assert_is_initialized_{}", ty.to_case(Case::Snake)); make_statement(StatementKind::Expression(call( - variable_path(chained_dep!("aztec", "initializer", "assert_is_initialized")), + variable_path(chained_dep!("aztec", "initializer", &fname)), vec![mutable_reference("context")], ))) } @@ -172,9 +173,10 @@ fn create_init_check() -> Statement { /// ```noir /// mark_as_initialized(&mut context); /// ``` -fn create_mark_as_initialized() -> Statement { +fn create_mark_as_initialized(ty: &str) -> Statement { + let fname = format!("mark_as_initialized_{}", ty.to_case(Case::Snake)); make_statement(StatementKind::Expression(call( - variable_path(chained_dep!("aztec", "initializer", "mark_as_initialized")), + variable_path(chained_dep!("aztec", "initializer", &fname)), vec![mutable_reference("context")], ))) } @@ -205,13 +207,11 @@ fn create_internal_check(fname: &str) -> Statement { /// ```noir /// assert_initialization_matches_address_preimage(context); /// ``` -fn create_assert_initializer() -> Statement { +fn create_assert_initializer(ty: &str) -> Statement { + let fname = + format!("assert_initialization_matches_address_preimage_{}", ty.to_case(Case::Snake)); make_statement(StatementKind::Expression(call( - variable_path(chained_dep!( - "aztec", - "initializer", - "assert_initialization_matches_address_preimage" - )), + variable_path(chained_dep!("aztec", "initializer", &fname)), vec![variable("context")], ))) } diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index 2e9dfacae341..8f8a804dec8d 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -25,10 +25,9 @@ describe('e2e_avm_simulator', () => { }, 50_000); describe('Storage', () => { - // FIX: Enable once the contract function works. - // it('Read immutable (initialized) storage (Field)', async () => { - // expect(await avmContact.methods.view_storage_immutable().view()).toEqual(42n); - // }); + it('Read immutable (initialized) storage (Field)', async () => { + expect(await avmContact.methods.view_storage_immutable().view()).toEqual(42n); + }); it('Modifies storage (Field)', async () => { await avmContact.methods.set_storage_single(20n).send().wait(); diff --git a/yarn-project/simulator/src/avm/journal/nullifiers.ts b/yarn-project/simulator/src/avm/journal/nullifiers.ts index f8374f6f8a76..e843d4ee9a68 100644 --- a/yarn-project/simulator/src/avm/journal/nullifiers.ts +++ b/yarn-project/simulator/src/avm/journal/nullifiers.ts @@ -1,5 +1,6 @@ import { siloNullifier } from '@aztec/circuits.js/hash'; import { Fr } from '@aztec/foundation/fields'; +import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import type { CommitmentsDB } from '../../index.js'; @@ -9,6 +10,8 @@ import type { CommitmentsDB } from '../../index.js'; * When a contract call completes, its cached nullifier set can be merged into its parent's. */ export class Nullifiers { + private log: DebugLogger = createDebugLogger('aztec:nullifiers'); + /** Cached nullifiers. */ private cache: NullifierCache; /** Parent's nullifier cache. Checked on cache-miss. */ @@ -41,16 +44,19 @@ export class Nullifiers { ): Promise<[/*exists=*/ boolean, /*isPending=*/ boolean, /*leafIndex=*/ Fr]> { // First check this cache let existsAsPending = this.cache.exists(storageAddress, nullifier); + this.log(`Checking nullifier ${nullifier} at contract ${storageAddress} in cache: ${existsAsPending}`); // Then check parent's cache if (!existsAsPending && this.parentCache) { existsAsPending = this.parentCache?.exists(storageAddress, nullifier); } + this.log(`Checking nullifier ${nullifier} at contract ${storageAddress} in parent cache: ${existsAsPending}`); // Finally try the host's Aztec state (a trip to the database) // If the value is found in the database, it will be associated with a leaf index! let leafIndex: bigint | undefined = undefined; if (!existsAsPending) { // silo the nullifier before checking for its existence in the host leafIndex = await this.hostNullifiers.getNullifierIndex(siloNullifier(storageAddress, nullifier)); + this.log(`Checking nullifier index ${siloNullifier(storageAddress, nullifier)} in host: ${leafIndex}`); } const exists = existsAsPending || leafIndex !== undefined; leafIndex = leafIndex === undefined ? BigInt(0) : leafIndex; @@ -64,6 +70,7 @@ export class Nullifiers { * @param nullifier - the nullifier to stage */ public async append(storageAddress: Fr, nullifier: Fr) { + this.log(`Staging nullifier ${nullifier} at contract ${storageAddress}`); const [exists, ,] = await this.checkExists(storageAddress, nullifier); if (exists) { throw new NullifierCollisionError( diff --git a/yarn-project/simulator/src/avm/opcodes/storage.ts b/yarn-project/simulator/src/avm/opcodes/storage.ts index 3ca3bfa19aaf..ad7858f1406b 100644 --- a/yarn-project/simulator/src/avm/opcodes/storage.ts +++ b/yarn-project/simulator/src/avm/opcodes/storage.ts @@ -1,4 +1,5 @@ import { Fr } from '@aztec/foundation/fields'; +import { createDebugLogger } from '@aztec/foundation/log'; import type { AvmContext } from '../avm_context.js'; import { Field } from '../avm_memory_types.js'; @@ -8,6 +9,8 @@ import { Addressing } from './addressing_mode.js'; import { Instruction } from './instruction.js'; abstract class BaseStorageInstruction extends Instruction { + static readonly log = createDebugLogger('aztec:avm_simulator:storage'); + // Informs (de)serialization. See Instruction.deserialize. public static readonly wireFormat: OperandType[] = [ OperandType.UINT8, @@ -47,6 +50,7 @@ export class SStore extends BaseStorageInstruction { const slot = context.machineState.memory.get(slotOffset).toFr(); const data = context.machineState.memory.getSlice(srcOffset, this.size).map(field => field.toFr()); + BaseStorageInstruction.log(`Writing ${data} to slot ${slot}`); for (const [index, value] of Object.entries(data)) { const adjustedSlot = slot.add(new Fr(BigInt(index))); @@ -82,6 +86,7 @@ export class SLoad extends BaseStorageInstruction { context.machineState.memory.set(bOffset + i, new Field(data)); } + BaseStorageInstruction.log(`Read ${context.machineState.memory.getSlice(bOffset, size)} from slot ${slot}.`); context.machineState.incrementPc(); }