Skip to content

Commit

Permalink
feat!: Support contracts with no constructor (#5175)
Browse files Browse the repository at this point in the history
Changes:
- **Breaking**: `constructor` is no longer an enshrined function name,
it's now required to flag the constructor with `aztec(initializer)`.
- Remove need for a function named constructor in noir aztec-macros and
yarn-packages
- Add `isInitializer` flag to contract artifact
- Default the initializer to a function that has the
`aztec(initializer)` attribute, if there's more than one, pick one at
random, preferring private ones
- When there's no initializer, the `initializationHash` of a contract
instance is set to zero
- Remove empty constructor functions from our contracts, and flag public
constructors as initializer where missing

cc @AztecProtocol/devrel
  • Loading branch information
spalladino authored Mar 14, 2024
1 parent db9a960 commit df7fa32
Show file tree
Hide file tree
Showing 68 changed files with 244 additions and 254 deletions.
1 change: 1 addition & 0 deletions boxes/boxes/react/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ contract BoxReact {
}

#[aztec(private)]
#[aztec(initializer)]
fn constructor(number: Field, owner: AztecAddress) {
let numbers = storage.numbers;
let mut new_number = ValueNote::new(number, owner);
Expand Down
1 change: 1 addition & 0 deletions boxes/boxes/vanilla/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ contract Vanilla {
}

#[aztec(private)]
#[aztec(initializer)]
fn constructor(number: Field, owner: AztecAddress) {
let numbers = storage.numbers;
let mut new_number = ValueNote::new(number, owner);
Expand Down
18 changes: 18 additions & 0 deletions docs/docs/misc/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@ keywords: [sandbox, cli, aztec, notes, migration, updating, upgrading]

Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them.

## 0.27.0

### `initializer` macro replaces `constructor`

Before this version, every contract was required to have exactly one `constructor` private function, that was used for deployment. We have now removed this requirement, and made `constructor` a function like any other.

To signal that a function can be used to **initialize** a contract, you must now decorate it with the `#[aztec(initializer)]` attribute. Initializers are regular functions that set an "initialized" flag (a nullifier) for the contract. A contract can only be initialized once, and contract functions can only be called after the contract has been initialized, much like a constructor. However, if a contract defines no initializers, it can be called at any time. Additionally, you can define as many initializer functions in a contract as you want, both private and public.

To migrate from current code, simply add an initializer attribute to your constructor functions.

```diff
+ #[aztec(initializer)]
#[aztec(private)]
fn constructor() { ... }
```

If your private constructor was used to just call a public internal initializer, then remove the private constructor and flag the public function as initializer. And if your private constructor was an empty one, just remove it.

## 0.25.0

### [Aztec.nr] Static calls
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ library Constants {
uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE =
0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631;
uint256 internal constant DEPLOYER_CONTRACT_ADDRESS =
0x0747a20ed0c86035e44ea5606f30de459f40b55c5e82012640aa554546af9044;
0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3;
uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 17;
uint256 internal constant MAX_NOTE_FIELDS_LENGTH = 20;
uint256 internal constant GET_NOTE_ORACLE_RETURN_LENGTH = 23;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ contract AvmTest {
// avm lib
use dep::aztec::avm::hash::{keccak256, poseidon, sha256};

#[aztec(private)]
fn constructor() {}

struct Storage {
single: PublicMutable<Field>,
list: PublicMutable<Note>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ contract Benchmarking {
balances: Map<AztecAddress, PublicMutable<Field>>,
}

#[aztec(private)]
fn constructor() {}

// Creates a new value note for the target owner. Use this method to seed an initial set of notes.
#[aztec(private)]
fn create_note(owner: AztecAddress, value: Field) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ contract CardGame {
games: Map<Field, PublicMutable<Game>>,
}

#[aztec(private)]
fn constructor() {}

#[aztec(private)]
fn buy_pack(seed: Field // The randomness used to generate the cards. Passed in for now.
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ contract Child {
a_private_value: PrivateSet<ValueNote>,
}

#[aztec(private)]
fn constructor() {}

// Returns a sum of the input and the chain id and version of the contract in private circuit public input's return_values.
#[aztec(private)]
fn value(input: Field) -> Field {
Expand Down
15 changes: 2 additions & 13 deletions noir-projects/noir-contracts/contracts/claim_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,9 @@ contract Claim {
reward_token: SharedImmutable<AztecAddress>,
}

#[aztec(private)]
fn constructor(target_contract: AztecAddress, reward_token: AztecAddress) {
let selector = FunctionSelector::from_signature("_initialize((Field),(Field))");
context.call_public_function(
context.this_address(),
selector,
[target_contract.to_field(), reward_token.to_field()]
);
}

#[aztec(public)]
#[aztec(internal)]
#[aztec(noinitcheck)]
fn _initialize(target_contract: AztecAddress, reward_token: AztecAddress) {
#[aztec(initializer)]
fn constructor(target_contract: AztecAddress, reward_token: AztecAddress) {
storage.target_contract.initialize(target_contract);
storage.reward_token.initialize(reward_token);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ contract ContractClassRegisterer {

use crate::capsule::pop_capsule;

#[aztec(private)]
fn constructor() {}

#[aztec(private)]
fn register(artifact_hash: Field, private_functions_root: Field, public_bytecode_commitment: Field) {
// TODO: Validate public_bytecode_commitment is the correct commitment of packed_public_bytecode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ contract ContractInstanceDeployer {

use crate::events::{instance_deployed::ContractInstanceDeployed};

#[aztec(private)]
fn constructor() {}

#[aztec(private)]
fn deploy(
salt: Field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ contract Counter {
// docs:start:constructor
#[aztec(private)]
#[aztec(initializer)]
fn constructor(headstart: u64, owner: AztecAddress) {
// We can name our initializer anything we want as long as it's marked as aztec(initializer)
fn initialize(headstart: u64, owner: AztecAddress) {
let counters = storage.counters;
counters.at(owner).add(headstart, owner);
}
// docs:end:constructor

// docs:start:increment
#[aztec(private)]
fn increment(owner: AztecAddress) {
fn increment(owner: AztecAddress) {
let counters = storage.counters;
counters.at(owner).add(1, owner);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,13 @@ contract Crowdfunding {
claim_notes: PrivateSet<ValueNote>,
}

#[aztec(private)]
fn constructor(donation_token: AztecAddress, operator: AztecAddress, deadline: u64) {
let selector = FunctionSelector::from_signature("_initialize((Field),(Field),Field)");
context.call_public_function(
context.this_address(),
selector,
[donation_token.to_field(), operator.to_field(), deadline as Field]
);
}

#[aztec(public)]
#[aztec(internal)]
#[aztec(noinitcheck)]
// TODO(#4990): Make deadline a u64 once the neccessary traits are implemented
fn _initialize(donation_token: AztecAddress, operator: AztecAddress, deadline: Field) {
#[aztec(initializer)]
fn constructor(donation_token: AztecAddress, operator: AztecAddress, deadline: u64) {
// TODO(#4990): Make deadline a u64 once the neccessary traits are implemented
storage.donation_token.initialize(donation_token);
storage.operator.initialize(operator);
storage.deadline.initialize(deadline);
storage.deadline.initialize(deadline as Field);
}

#[aztec(public)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ contract DelegatedOn {
a_private_value: PrivateSet<ValueNote>,
}

#[aztec(private)]
fn constructor() {}

#[aztec(private)]
fn private_set_value(new_value: Field, owner: AztecAddress) -> Field {
let mut note = ValueNote::new(new_value, owner);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ contract Delegator {
a_private_value: PrivateSet<ValueNote>,
}

#[aztec(private)]
fn constructor() {}

#[aztec(private)]
fn private_delegate_set_value(
targetContract: AztecAddress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,6 @@ contract DocsExample {
}
}

#[aztec(private)]
fn constructor() {}

#[aztec(public)]
fn initialize_shared_immutable(points: u8) {
let mut new_leader = Leader { account: context.msg_sender(), points };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ contract GasToken {
balances: Map<AztecAddress, PublicMutable<U128>>,
}

#[aztec(private)]
fn constructor() {}

#[aztec(public)]
fn claim_public(to: AztecAddress, amount: Field, canceller: EthAddress, secret: Field) {
let content_hash = get_bridge_gas_msg_hash(to, amount, canceller);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ contract ImportTest {
ManyNotesADeepStructTestCodeGenStruct
};

// TODO(@spalladino): Delete all empty constructors
#[aztec(private)]
fn constructor(
) {}

// Calls the testCodeGen on the Test contract at the target address
// Used for testing calling a function with arguments of multiple types
// See yarn-project/simulator/src/client/private_execution.ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
contract Parent {
use dep::aztec::prelude::{AztecAddress, FunctionSelector};

#[aztec(private)]
fn constructor() {}

// Private function to call another private function in the targetContract using the provided selector
#[aztec(private)]
fn entryPoint(targetContract: AztecAddress, targetSelector: FunctionSelector) -> Field {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ contract PendingNoteHashes {
// TODO(dbanks12): consolidate code into internal helper functions
// (once Noir's support for this is more robust)

#[aztec(private)]
fn constructor() {}

// Confirm can access pending note hashes by creating / inserting a note and then
// getting / reading that note all in the same contract function
// Realistic way to describe this test is "Mint note A, then burn note A in the same transaction"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ contract PriceFeed {
assets: Map<Field, PublicMutable<Asset>>,
}

#[aztec(private)]
fn constructor() {}

#[aztec(public)]
fn set_price(asset_id: Field, price: Field) {
let asset = storage.assets.at(asset_id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ contract SchnorrHardcodedAccount {

global ACCOUNT_ACTIONS_STORAGE_SLOT = 1;

#[aztec(private)]
fn constructor() {}

// Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts
#[aztec(private)]
fn entrypoint(app_payload: pub AppPayload, fee_payload: pub FeePayload) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ contract SchnorrSingleKeyAccount {

global ACCOUNT_ACTIONS_STORAGE_SLOT = 1;

#[aztec(private)]
fn constructor() {}

// Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts
#[aztec(private)]
fn entrypoint(app_payload: pub AppPayload, fee_payload: pub FeePayload) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ contract SlowTree {
}
// docs:end:constants_and_storage

#[aztec(private)]
fn constructor() {}
// docs:start:initialize
#[aztec(public)]
fn initialize() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ contract Test {
example_set: PrivateSet<FieldNote>,
}

// TODO(@spalladino): Delete all empty constructors
#[aztec(private)]
// docs:start:empty-constructor
fn constructor() {}
// docs:end:empty-constructor

#[aztec(private)]
fn get_public_key(address: AztecAddress) -> [Field; 2] {
let pub_key = get_public_key_oracle(address);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ contract Uniswap {
// gets incremented each time after use to prevent replay attacks
nonce_for_burn_approval: PublicMutable<Field>,
}

#[aztec(private)]
fn constructor() {}
// docs:end:uniswap_setup

// docs:start:swap_public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ global REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af8166354
// CONTRACT INSTANCE CONSTANTS
// sha224sum 'struct ContractInstanceDeployed'
global DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631;
global DEPLOYER_CONTRACT_ADDRESS = 0x0747a20ed0c86035e44ea5606f30de459f40b55c5e82012640aa554546af9044;
global DEPLOYER_CONTRACT_ADDRESS = 0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3;

// NOIR CONSTANTS - constants used only in yarn-packages/noir-contracts
// Some are defined here because Noir doesn't yet support globals referencing other globals yet.
Expand Down
9 changes: 0 additions & 9 deletions noir/noir-repo/aztec_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,6 @@ fn transform_module(
crate_graph.root_file_id,
));
}

let constructor_defined = module.functions.iter().any(|func| func.name() == "constructor");
if !constructor_defined {
let crate_graph = &context.crate_graph[crate_id];
return Err((
AztecMacroError::ContractConstructorMissing { span: Span::default() },
crate_graph.root_file_id,
));
}
}

Ok(has_transformed_module)
Expand Down
6 changes: 0 additions & 6 deletions noir/noir-repo/aztec_macros/src/utils/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use super::constants::MAX_CONTRACT_PRIVATE_FUNCTIONS;
pub enum AztecMacroError {
AztecDepNotFound,
ContractHasTooManyPrivateFunctions { span: Span },
ContractConstructorMissing { span: Span },
UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData },
UnsupportedStorageType { span: Option<Span>, typ: UnresolvedTypeData },
CouldNotAssignStorageSlots { secondary_message: Option<String> },
Expand All @@ -29,11 +28,6 @@ impl From<AztecMacroError> for MacroError {
secondary_message: None,
span: Some(span),
},
AztecMacroError::ContractConstructorMissing { span } => MacroError {
primary_message: "Contract must have a constructor function".to_owned(),
secondary_message: None,
span: Some(span),
},
AztecMacroError::UnsupportedFunctionArgumentType { span, typ } => MacroError {
primary_message: format!("Provided parameter type `{typ:?}` is not supported in Aztec contract interface"),
secondary_message: None,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/accounts/src/defaults/account_contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { DefaultAccountInterface } from '../defaults/account_interface.js';
*/
export abstract class DefaultAccountContract implements AccountContract {
abstract getAuthWitnessProvider(address: CompleteAddress): AuthWitnessProvider;
abstract getDeploymentArgs(): any[];
abstract getDeploymentArgs(): any[] | undefined;

constructor(private artifact: ContractArtifact) {}

Expand Down
4 changes: 2 additions & 2 deletions yarn-project/accounts/src/single_key/account_contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export class SingleKeyAccountContract extends DefaultAccountContract {
super(SchnorrSingleKeyAccountContractArtifact as ContractArtifact);
}

getDeploymentArgs(): any[] {
return [];
getDeploymentArgs(): undefined {
return undefined;
}

getAuthWitnessProvider({ partialAddress }: CompleteAddress): AuthWitnessProvider {
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/accounts/src/testing/create_account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { getSchnorrAccount } from '../schnorr/index.js';
* @returns - A wallet for a fresh account.
*/
export function createAccount(pxe: PXE): Promise<AccountWalletWithPrivateKey> {
return getSchnorrAccount(pxe, GrumpkinScalar.random(), GrumpkinScalar.random()).waitDeploy();
return getSchnorrAccount(pxe, GrumpkinScalar.random(), GrumpkinScalar.random()).waitSetup();
}

/**
Expand Down
Loading

0 comments on commit df7fa32

Please sign in to comment.