Skip to content

Commit

Permalink
feat: Check initializer by default in private functions (#4832)
Browse files Browse the repository at this point in the history
Injects the initialization check by default on every aztec private
function for contracts that have initializers.
  • Loading branch information
spalladino authored Mar 1, 2024
1 parent f7a7243 commit 3ff9fe0
Show file tree
Hide file tree
Showing 28 changed files with 100 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ contract AppSubscription {

// Constructs the contract
#[aztec(private)]
#[aztec(initializer)]
fn constructor(
target_address: AztecAddress,
subscription_recipient_address: AztecAddress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ contract Counter {

// docs:start:constructor
#[aztec(private)]
#[aztec(initializer)]
fn constructor(headstart: u64, owner: AztecAddress) {
let counters = storage.counters;
counters.at(owner).add(headstart, owner);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ contract EasyPrivateToken {
* initialize the contract's initial state variables.
*/
#[aztec(private)]
#[aztec(initializer)]
fn constructor(initial_supply: u64, owner: AztecAddress) {
let balances = storage.balances;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ contract EasyPrivateVoting {
// docs:end:storage_struct

// docs:start:constructor
#[aztec(private)] // annotation to mark function as private and expose private context
#[aztec(private)]
#[aztec(initializer)] // annotation to mark function as private and expose private context
fn constructor(admin: AztecAddress) { // called when contract is deployed
context.call_public_function(
// we cannot update public state directly from private function but we can call public function (which queues it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ contract EcdsaAccount {

// Creates a new account out of an ECDSA public key to use for signature verification
#[aztec(private)]
#[aztec(initializer)]
fn constructor(signing_pub_key_x: pub [u8; 32], signing_pub_key_y: pub [u8; 32]) {
let this = context.this_address();
let mut pub_key_note = EcdsaPublicKeyNote::new(signing_pub_key_x, signing_pub_key_y, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ contract Escrow {
// Creates a new instance
// docs:start:constructor
#[aztec(private)]
#[aztec(initializer)]
fn constructor(owner: pub AztecAddress) {
let this = context.this_address();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ contract FPC {
}

#[aztec(private)]
#[aztec(initializer)]
fn constructor(other_asset: AztecAddress, fee_asset: AztecAddress) {
let selector = FunctionSelector::from_signature("_initialize((Field),(Field))");
context.call_public_function(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ contract ImportTest {
ManyNotesADeepStructTestCodeGenStruct
};

// TODO(@spalladino): Delete all empty constructors
#[aztec(private)]
fn constructor(
) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ contract InclusionProofs {
}

#[aztec(private)]
#[aztec(initializer)]
fn constructor(public_value: Field) {
let selector = FunctionSelector::from_signature("_initialize(Field)");
context.call_public_function(context.this_address(), selector, [public_value]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ contract Lending {

// Constructs the contract.
#[aztec(private)]
#[aztec(initializer)]
fn constructor(
) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ contract SchnorrAccount {

// Constructs the contract
#[aztec(private)]
#[aztec(initializer)]
fn constructor(signing_pub_key_x: pub Field, signing_pub_key_y: pub Field) {
let this = context.this_address();
// docs:start:initialize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ contract StatefulTest {
}

#[aztec(private)]
#[aztec(initcheck)]
fn create_note(owner: AztecAddress, value: Field) {
if (value != 0) {
let loc = storage.notes.at(owner);
Expand All @@ -34,6 +33,7 @@ contract StatefulTest {
}

#[aztec(private)]
#[aztec(noinitcheck)]
fn create_note_no_init_check(owner: AztecAddress, value: Field) {
if (value != 0) {
let loc = storage.notes.at(owner);
Expand All @@ -54,6 +54,7 @@ contract StatefulTest {
}

#[aztec(private)]
#[aztec(noinitcheck)]
fn destroy_and_create_no_init_check(recipient: AztecAddress, amount: Field) {
let sender = context.msg_sender();

Expand Down
18 changes: 16 additions & 2 deletions noir-projects/noir-contracts/contracts/test_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ contract Test {
// docs:end:unencrypted_import

use dep::aztec::{
context::{Context, inputs::private_context_inputs::PrivateContextInputs}, hash::pedersen_hash,
context::{Context, inputs::private_context_inputs::PrivateContextInputs},
hash::{pedersen_hash, compute_secret_hash},
context::PrivateContext,
note::{
note_header::NoteHeader, utils as note_utils, lifecycle::{create_note, destroy_note},
Expand All @@ -20,7 +21,7 @@ contract Test {
},
deploy::{deploy_contract as aztec_deploy_contract},
oracle::{get_public_key::get_public_key as get_public_key_oracle, context::get_portal_address, rand::rand},
state_vars::PrivateImmutable, log::emit_unencrypted_log_from_private
state_vars::{PrivateImmutable, PrivateSet}, log::emit_unencrypted_log_from_private
};
use dep::token_portal_content_hash_lib::{get_mint_private_content_hash, get_mint_public_content_hash};
use dep::field_note::field_note::FieldNote;
Expand All @@ -33,8 +34,10 @@ contract Test {

struct Storage {
example_constant: PrivateImmutable<FieldNote>,
example_set: PrivateSet<FieldNote>,
}

// TODO(@spalladino): Delete all empty constructors
#[aztec(private)]
// docs:start:empty-constructor
fn constructor() {}
Expand Down Expand Up @@ -350,6 +353,17 @@ contract Test {
aztec_deploy_contract(&mut context, target);
}

#[aztec(private)]
// Adapted from TokenContract#redeem_shield but without an initcheck so it can be run in simulator/src/client/private_execution.test.ts
fn consume_note_from_secret(secret: Field) {
let notes_set = storage.example_set;
let secret_hash = compute_secret_hash(secret);
let options = NoteGetterOptions::new().select(0, secret_hash, Option::none()).set_limit(1);
let notes = notes_set.get_notes(options);
let note = notes[0].unwrap_unchecked();
notes_set.remove(note);
}

unconstrained fn get_constant() -> pub Field {
let constant = storage.example_constant.view_note();
constant.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ contract TokenBlacklist {

// docs:start:constructor
#[aztec(private)]
#[aztec(initializer)]
fn constructor(admin: AztecAddress, slow_updates_contract: AztecAddress) {
// docs:end:constructor
let selector = FunctionSelector::from_signature("_initialize((Field),(Field))");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ contract TokenBridge {

// Constructs the contract.
#[aztec(private)]
#[aztec(initializer)]
fn constructor(token: AztecAddress) {
let selector = FunctionSelector::from_signature("_initialize((Field))");
context.call_public_function(context.this_address(), selector, [token.to_field()]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ contract Token {

// docs:start:constructor
#[aztec(private)]
#[aztec(initializer)]
fn constructor(admin: AztecAddress, name: str<31>, symbol: str<31>, decimals: u8) {
let selector = FunctionSelector::from_signature("_initialize((Field),(Field),(Field),u8)");
let name_s = FieldCompressedString::from_string(name);
Expand Down
22 changes: 16 additions & 6 deletions noir/noir-repo/aztec_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,22 +435,32 @@ fn transform_module(
}
}

let has_initializer = module.functions.iter().any(|func| {
func.def
.attributes
.secondary
.iter()
.any(|attr| is_custom_attribute(&attr, "aztec(initializer)"))
});

for func in module.functions.iter_mut() {
let mut is_private = false;
let mut is_public = false;
let mut is_public_vm = false;
let mut is_initializer = false;
let mut skip_init_check = true; // Default to true once we're confident that the approach works
let mut insert_init_check = has_initializer;

for secondary_attribute in func.def.attributes.secondary.clone() {
if is_custom_attribute(&secondary_attribute, "aztec(private)") {
is_private = true;
} else if is_custom_attribute(&secondary_attribute, "aztec(initializer)") {
is_initializer = true;
} else if is_custom_attribute(&secondary_attribute, "aztec(initcheck)") {
skip_init_check = false;
insert_init_check = false;
} else if is_custom_attribute(&secondary_attribute, "aztec(noinitcheck)") {
insert_init_check = false;
} else if is_custom_attribute(&secondary_attribute, "aztec(public)") {
is_public = true;
insert_init_check = false;
} else if is_custom_attribute(&secondary_attribute, "aztec(public-vm)") {
is_public_vm = true;
}
Expand All @@ -463,7 +473,7 @@ fn transform_module(
func,
storage_defined,
is_initializer,
skip_init_check,
insert_init_check,
)
.map_err(|err| (err, crate_graph.root_file_id))?;
has_transformed_module = true;
Expand Down Expand Up @@ -655,14 +665,14 @@ fn transform_function(
func: &mut NoirFunction,
storage_defined: bool,
is_initializer: bool,
skip_init_check: bool,
insert_init_check: bool,
) -> Result<(), AztecMacroError> {
let context_name = format!("{}Context", ty);
let inputs_name = format!("{}ContextInputs", ty);
let return_type_name = format!("{}CircuitPublicInputs", ty);

// Add initialization check
if !skip_init_check {
if insert_init_check {
if ty == "Public" {
let error = AztecMacroError::UnsupportedAttributes {
span: func.def.name.span(),
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/archiver/src/rpc/archiver_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
L1ToL2Message,
L2Block,
L2BlockL2Logs,
NullifierMembershipWitness,
TxReceipt,
} from '@aztec/circuit-types';
import { EthAddress, Fr } from '@aztec/circuits.js';
Expand All @@ -27,7 +28,7 @@ export const createArchiverClient = (url: string, fetch = makeFetch([1, 2, 3], t
L2Block,
L2BlockL2Logs,
},
{ TxReceipt },
{ TxReceipt, NullifierMembershipWitness },
false,
'archiver',
fetch,
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/archiver/src/rpc/archiver_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
L1ToL2Message,
L2Block,
L2BlockL2Logs,
NullifierMembershipWitness,
TxEffect,
TxReceipt,
} from '@aztec/circuit-types';
Expand Down Expand Up @@ -34,7 +35,7 @@ export function createArchiverRpcServer(archiverService: Archiver): JsonRpcServe
L2BlockL2Logs,
TxEffect,
},
{ TxReceipt },
{ TxReceipt, NullifierMembershipWitness },
['start', 'stop'],
);
}
3 changes: 2 additions & 1 deletion yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
L2Block,
L2BlockL2Logs,
LogId,
NullifierMembershipWitness,
SiblingPath,
Tx,
TxEffect,
Expand Down Expand Up @@ -43,7 +44,7 @@ export function createAztecNodeRpcServer(node: AztecNode) {
SiblingPath,
L1ToL2MessageAndIndex,
},
{ Tx, TxReceipt, L2BlockL2Logs },
{ Tx, TxReceipt, L2BlockL2Logs, NullifierMembershipWitness },
// disable methods not part of the AztecNode interface
['start', 'stop'],
);
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/aztec.js/src/rpc_clients/pxe_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
L2BlockL2Logs,
LogId,
Note,
NullifierMembershipWitness,
PXE,
Tx,
TxEffect,
Expand Down Expand Up @@ -55,7 +56,7 @@ export const createPXEClient = (url: string, fetch = makeFetch([1, 2, 3], false)
TxExecutionRequest,
TxHash,
},
{ Tx, TxReceipt, L2BlockL2Logs },
{ Tx, TxReceipt, L2BlockL2Logs, NullifierMembershipWitness },
false,
'pxe',
fetch,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createJsonRpcClient, defaultFetch } from '@aztec/foundation/json-rpc/cl

import { ContractData, ExtendedContractData } from '../../contract_data.js';
import { AztecNode } from '../../interfaces/aztec-node.js';
import { NullifierMembershipWitness } from '../../interfaces/nullifier_tree.js';
import { L1ToL2MessageAndIndex } from '../../l1_to_l2_message.js';
import { L2Block } from '../../l2_block.js';
import { ExtendedUnencryptedL2Log, L2BlockL2Logs, LogId } from '../../logs/index.js';
Expand Down Expand Up @@ -40,7 +41,7 @@ export function createAztecNodeClient(url: string, fetch = defaultFetch): AztecN
SiblingPath,
L1ToL2MessageAndIndex,
},
{ Tx, TxReceipt, L2BlockL2Logs },
{ Tx, TxReceipt, L2BlockL2Logs, NullifierMembershipWitness },
false,
'node',
fetch,
Expand Down
16 changes: 16 additions & 0 deletions yarn-project/circuit-types/src/interfaces/nullifier_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,20 @@ export class NullifierMembershipWitness {
public toFields(): Fr[] {
return [new Fr(this.index), ...this.leafPreimage.toFields(), ...this.siblingPath.toFields()];
}

public toJSON() {
return {
index: '0x' + this.index.toString(16),
leafPreimage: this.leafPreimage.toJSON(),
siblingPath: this.siblingPath.toString(),
};
}

static fromJSON(json: any): NullifierMembershipWitness {
return new NullifierMembershipWitness(
BigInt(json.index),
NullifierLeafPreimage.fromJSON(json.leafPreimage),
SiblingPath.fromString<typeof NULLIFIER_TREE_HEIGHT>(json.siblingPath),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ export class NullifierLeafPreimage implements IndexedTreeLeafPreimage {
return new NullifierLeafPreimage(this.nullifier, this.nextNullifier, this.nextIndex);
}

toJSON() {
return {
nullifier: this.nullifier.toString(),
nextNullifier: this.nextNullifier.toString(),
nextIndex: '0x' + this.nextIndex.toString(16),
};
}

static empty(): NullifierLeafPreimage {
return new NullifierLeafPreimage(Fr.ZERO, Fr.ZERO, 0n);
}
Expand All @@ -75,6 +83,14 @@ export class NullifierLeafPreimage implements IndexedTreeLeafPreimage {
static clone(preimage: NullifierLeafPreimage): NullifierLeafPreimage {
return new NullifierLeafPreimage(preimage.nullifier, preimage.nextNullifier, preimage.nextIndex);
}

static fromJSON(json: any): NullifierLeafPreimage {
return new NullifierLeafPreimage(
Fr.fromString(json.nullifier),
Fr.fromString(json.nextNullifier),
BigInt(json.nextIndex),
);
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/e2e_deploy_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,8 @@ describe('e2e_deploy_contract', () => {
testDeployingAnInstance('from a contract', async instance => {
// Register the instance to be deployed in the pxe
await wallet.addContracts([{ artifact, instance }]);
// Set up the contract that calls the deployer (which happens to be the StatefulTestContract) and call it
const deployer = await registerContract(wallet, TestContract, [accounts[0].address, 48]);
// Set up the contract that calls the deployer (which happens to be the TestContract) and call it
const deployer = await TestContract.deploy(wallet).send().deployed();
await deployer.methods.deploy_contract(instance.address).send().wait();
});
});
Expand Down
Loading

0 comments on commit 3ff9fe0

Please sign in to comment.