Skip to content

Commit

Permalink
feat: Public initializer check (#4894)
Browse files Browse the repository at this point in the history
Adds a public variant for `assert_is_initialized`.
  • Loading branch information
spalladino authored Mar 4, 2024
1 parent c909966 commit 6b861bb
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 20 deletions.
9 changes: 4 additions & 5 deletions noir-projects/aztec-nr/aztec/src/initializer.nr
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
use dep::protocol_types::hash::silo_nullifier;
use crate::context::PrivateContext;
use crate::context::{PrivateContext, ContextInterface};
use crate::history::nullifier_inclusion::prove_nullifier_inclusion;

pub fn mark_as_initialized(context: &mut PrivateContext) {
let init_nullifier = compute_unsiloed_contract_initialization_nullifier(*context);
context.push_new_nullifier(init_nullifier, 0);
}

// TODO(@spalladino): Add a variant using PublicContext once we can read nullifiers or note hashes from public-land.
pub fn assert_is_initialized(context: &mut PrivateContext) {
pub fn assert_is_initialized<TContext>(context: &mut TContext) where TContext: ContextInterface {
let init_nullifier = compute_contract_initialization_nullifier(*context);
prove_nullifier_inclusion(init_nullifier, *context);
}

pub fn compute_contract_initialization_nullifier(context: PrivateContext) -> Field {
pub fn compute_contract_initialization_nullifier<TContext>(context: TContext) -> Field where TContext: ContextInterface {
let address = context.this_address();
silo_nullifier(address, compute_unsiloed_contract_initialization_nullifier(context))
}

pub fn compute_unsiloed_contract_initialization_nullifier(context: PrivateContext) -> Field {
pub fn compute_unsiloed_contract_initialization_nullifier<TContext>(context: TContext) -> Field where TContext: ContextInterface{
context.this_address().to_field()
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ contract StatefulTest {

#[aztec(public)]
fn increment_public_value(owner: AztecAddress, value: Field) {
assert_is_initialized(&mut context);
let loc = storage.public_values.at(owner);
loc.write(loc.read() + value);
}

#[aztec(public)]
fn increment_public_value_no_init_check(owner: AztecAddress, value: Field) {
let loc = storage.public_values.at(owner);
loc.write(loc.read() + value);
}
Expand Down
55 changes: 40 additions & 15 deletions yarn-project/end-to-end/src/e2e_deploy_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ describe('e2e_deploy_contract', () => {
.wait();
logger.info(`Checking if the constructor was run for ${contract.address}`);
expect(await contract.methods.summed_values(owner).view()).toEqual(42n);
logger.info(`Calling a function that requires initialization on ${contract.address}`);
logger.info(`Calling a private function that requires initialization on ${contract.address}`);
await contract.methods.create_note(owner, 10).send().wait();
expect(await contract.methods.summed_values(owner).view()).toEqual(52n);
},
Expand Down Expand Up @@ -310,6 +310,7 @@ describe('e2e_deploy_contract', () => {
let instance: ContractInstanceWithAddress;
let initArgs: StatefulContractCtorArgs;
let publicKey: PublicKey;
let contract: StatefulTestContract;

beforeAll(async () => {
initArgs = [accounts[0].address, 42];
Expand All @@ -321,20 +322,7 @@ describe('e2e_deploy_contract', () => {
const { address, contractClassId } = instance;
logger(`Deploying contract instance at ${address.toString()} class id ${contractClassId.toString()}`);
await deployFn(instance);
}, 60_000);

it('stores contract instance in the aztec node', async () => {
const deployed = await aztecNode.getContract(instance.address);
expect(deployed).toBeDefined();
expect(deployed!.address).toEqual(instance.address);
expect(deployed!.contractClassId).toEqual(contractClass.id);
expect(deployed!.initializationHash).toEqual(instance.initializationHash);
expect(deployed!.portalContractAddress).toEqual(instance.portalContractAddress);
expect(deployed!.publicKeysHash).toEqual(instance.publicKeysHash);
expect(deployed!.salt).toEqual(instance.salt);
});

it('calls a public function on the deployed instance', async () => {
// TODO(@spalladino) We should **not** need the whole instance, including initArgs and salt,
// in order to interact with a public function for the contract. We may even not need
// all of it for running a private function. Consider removing `instance` as a required
Expand All @@ -350,7 +338,44 @@ describe('e2e_deploy_contract', () => {
publicKey,
});
expect(registered.address).toEqual(instance.address);
const contract = await StatefulTestContract.at(instance.address, wallet);
contract = await StatefulTestContract.at(instance.address, wallet);
}, 60_000);

it('stores contract instance in the aztec node', async () => {
const deployed = await aztecNode.getContract(instance.address);
expect(deployed).toBeDefined();
expect(deployed!.address).toEqual(instance.address);
expect(deployed!.contractClassId).toEqual(contractClass.id);
expect(deployed!.initializationHash).toEqual(instance.initializationHash);
expect(deployed!.portalContractAddress).toEqual(instance.portalContractAddress);
expect(deployed!.publicKeysHash).toEqual(instance.publicKeysHash);
expect(deployed!.salt).toEqual(instance.salt);
});

it('calls a public function with no init check on the deployed instance', async () => {
const whom = AztecAddress.random();
await contract.methods
.increment_public_value_no_init_check(whom, 10)
.send({ skipPublicSimulation: true })
.wait();
const stored = await contract.methods.get_public_value(whom).view();
expect(stored).toEqual(10n);
}, 30_000);

it('refuses to call a public function with init check if the instance is not initialized', async () => {
await expect(
contract.methods
.increment_public_value(AztecAddress.random(), 10)
.send({ skipPublicSimulation: true })
.wait(),
).rejects.toThrow(/dropped/i);
}, 30_000);

it('calls a public function with init check after initialization', async () => {
await contract.methods
.constructor(...initArgs)
.send()
.wait();
const whom = AztecAddress.random();
await contract.methods.increment_public_value(whom, 10).send({ skipPublicSimulation: true }).wait();
const stored = await contract.methods.get_public_value(whom).view();
Expand Down

0 comments on commit 6b861bb

Please sign in to comment.