diff --git a/boxes/boxes/react/package.json b/boxes/boxes/react/package.json index ea7ded69c00..0cd42e16ae5 100644 --- a/boxes/boxes/react/package.json +++ b/boxes/boxes/react/package.json @@ -7,7 +7,7 @@ "main": "./dist/index.js", "scripts": { "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", - "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o artifacts --ts", + "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o artifacts", "clean": "rm -rf ./dist .tsbuildinfo ./artifacts ./src/contracts/target", "prep": "yarn clean && yarn compile && yarn codegen", "dev": "yarn prep && webpack serve --mode development", diff --git a/boxes/boxes/vanilla/package.json b/boxes/boxes/vanilla/package.json index bed49cf08f8..4e9c549afc6 100644 --- a/boxes/boxes/vanilla/package.json +++ b/boxes/boxes/vanilla/package.json @@ -6,7 +6,7 @@ "type": "module", "scripts": { "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", - "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o artifacts --ts", + "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o artifacts", "clean": "rm -rf ./dest .tsbuildinfo ./artifacts ./src/contracts/target", "prep": "yarn clean && yarn compile && yarn codegen && tsc -b", "dev": "yarn prep && webpack serve --mode development", diff --git a/boxes/contract-only/package.json b/boxes/contract-only/package.json index 361571253f8..93e8c27ca85 100644 --- a/boxes/contract-only/package.json +++ b/boxes/contract-only/package.json @@ -6,7 +6,7 @@ "type": "module", "scripts": { "compile": "cd src && ${AZTEC_NARGO:-aztec-nargo} compile", - "codegen": "${AZTEC_CLI:-aztec-cli} codegen target -o artifacts --ts", + "codegen": "${AZTEC_CLI:-aztec-cli} codegen target -o artifacts", "clean": "rm -rf ./dest .tsbuildinfo ./artifacts ./target", "prep": "yarn clean && yarn compile && yarn codegen && tsc -b", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --runInBand", diff --git a/docs/docs/developers/contracts/compiling_contracts/how_to_compile_contract.md b/docs/docs/developers/contracts/compiling_contracts/how_to_compile_contract.md index 7d78818d4ff..6135b4d8e1a 100644 --- a/docs/docs/developers/contracts/compiling_contracts/how_to_compile_contract.md +++ b/docs/docs/developers/contracts/compiling_contracts/how_to_compile_contract.md @@ -24,10 +24,8 @@ This will output a JSON [artifact](./artifacts.md) for each contract in the proj You can use the code generator to autogenerate type-safe typescript classes for each of your contracts. These classes define type-safe methods for deploying and interacting with your contract based on their artifact. -To generate them, include a `--ts` option in the `codegen` command with a path to the target folder for the typescript files: - ```bash -aztec-cli codegen ./aztec-nargo/output/target/path -o src/artifacts --ts +aztec-cli codegen ./aztec-nargo/output/target/path -o src/artifacts ``` Below is typescript code generated from the [Token](https://github.com/AztecProtocol/aztec-packages/blob/master/noir-projects/noir-contracts/contracts/token_contract/src/main.nr) contract: @@ -119,94 +117,34 @@ Read more about interacting with contracts using `aztec.js` [here](../../getting An Aztec.nr contract can [call a function](../writing_contracts/functions/call_functions.md) in another contract via `context.call_private_function` or `context.call_public_function`. However, this requires manually assembling the function selector and manually serializing the arguments, which is not type-safe. -To make this easier, the compiler can generate contract interface structs that expose a convenience method for each function listed in a given contract artifact. These structs are intended to be used from another contract project that calls into the current one. For each contract, two interface structs are generated: one to be used from private functions with a `PrivateContext`, and one to be used from open functions with a `PublicContext`. - -To generate them, include a `--nr` option in the `codegen` command with a path to the target folder for the generated Aztec.nr interface files: - -```bash -aztec-cli codegen ./aztec-nargo/output/target/path -o ./path/to/output/folder --nr -``` +To make this easier, the compiler automatically generates interface structs that expose a convenience method for each function listed in a given contract artifact. These structs are intended to be used from another contract project that calls into the current one. -Below is an example interface, also generated from the [Token](https://github.com/AztecProtocol/aztec-packages/blob/master/noir-projects/noir-contracts/contracts/token_contract/src/main.nr) contract: +Below is an example of interface usage generated from the [Token](https://github.com/AztecProtocol/aztec-packages/blob/master/noir-projects/noir-contracts/contracts/token_contract/src/main.nr) contract, used from the [FPC](https://github.com/AztecProtocol/aztec-packages/blob/master/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr): ```rust -impl TokenPrivateContextInterface { - pub fn at(address: Field) -> Self { - Self { - address, - } - } - - pub fn burn( - self, - context: &mut PrivateContext, - from: FromBurnStruct, - amount: Field, - nonce: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 3]; - serialized_args[0] = from.address; - serialized_args[1] = amount; - serialized_args[2] = nonce; - - context.call_private_function(self.address, 0xd4fcc96e, serialized_args) - } - - - pub fn burn_public( - self, - context: &mut PrivateContext, - from: FromBurnPublicStruct, - amount: Field, - nonce: Field - ) { - let mut serialized_args = [0; 3]; - serialized_args[0] = from.address; - serialized_args[1] = amount; - serialized_args[2] = nonce; +contract FPC { - context.call_public_function(self.address, 0xb0e964d5, serialized_args) - } - ... - -} + ... -impl TokenPublicContextInterface { - pub fn at(address: Field) -> Self { - Self { - address, - } - } + use dep::token::Token; - pub fn burn_public( - self, - context: PublicContext, - from: FromBurnPublicStruct, - amount: Field, - nonce: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 3]; - serialized_args[0] = from.address; - serialized_args[1] = amount; - serialized_args[2] = nonce; - - context.call_public_function(self.address, 0xb0e964d5, serialized_args) - } + ... - pub fn mint_private( - self, - context: PublicContext, - amount: Field, - secret_hash: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = secret_hash; + #[aztec(private)] + fn fee_entrypoint_private(amount: Field, asset: AztecAddress, secret_hash: Field, nonce: Field) { + assert(asset == storage.other_asset.read_private()); + Token::at(asset).unshield(context.msg_sender(), context.this_address(), amount, nonce).call(&mut context); + FPC::at(context.this_address()).pay_fee_with_shielded_rebate(amount, asset, secret_hash).enqueue(&mut context); + } - context.call_public_function(self.address, 0x10763932, serialized_args) - } + #[aztec(private)] + fn fee_entrypoint_public(amount: Field, asset: AztecAddress, nonce: Field) { + FPC::at(context.this_address()).prepare_fee(context.msg_sender(), amount, asset, nonce).enqueue(&mut context); + FPC::at(context.this_address()).pay_fee(context.msg_sender(), amount, asset).enqueue(&mut context); + } + ... } ``` diff --git a/docs/docs/developers/contracts/deploying_contracts/how_to_deploy_contract.md b/docs/docs/developers/contracts/deploying_contracts/how_to_deploy_contract.md index 2ac74e787e2..dd646d383fb 100644 --- a/docs/docs/developers/contracts/deploying_contracts/how_to_deploy_contract.md +++ b/docs/docs/developers/contracts/deploying_contracts/how_to_deploy_contract.md @@ -40,7 +40,7 @@ aztec-nargo compile Generate the typescript class: ```bash -aztec-cli codegen ./aztec-nargo/output/target/path -o src/artifacts --ts +aztec-cli codegen ./aztec-nargo/output/target/path -o src/artifacts ``` This would create a typescript file like `Example.ts` in `./src/artifacts`. Read more on the [compiling page](../compiling_contracts/how_to_compile_contract.md). diff --git a/docs/docs/developers/contracts/writing_contracts/historical_data/slow_updates_tree/implement_slow_updates.md b/docs/docs/developers/contracts/writing_contracts/historical_data/slow_updates_tree/implement_slow_updates.md index d738af37879..6de1c8abae6 100644 --- a/docs/docs/developers/contracts/writing_contracts/historical_data/slow_updates_tree/implement_slow_updates.md +++ b/docs/docs/developers/contracts/writing_contracts/historical_data/slow_updates_tree/implement_slow_updates.md @@ -8,25 +8,19 @@ On this page you will learn how to implement a slow updates tree into your contr # How to implement a slow updates tree -1. Copy the _SlowTree.nr_ example and its dependencies, found [here](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts/slow_tree_contract). Replace the constants with whatever you like and deploy it to your sandbox -2. Copy the _SlowMap interface_ for easy interaction with your deployed SlowTree. Find it [here](https://github.com/AztecProtocol/aztec-packages/blob/master/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/interfaces.nr) -3. Import this interface into your contract - -#include_code interface noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr rust - -5. Store the SlowTree address in private storage as a FieldNote +1. Store the SlowTree address in private storage as a FieldNote #include_code constructor noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr rust -6. Store the SlowTree address in public storage and initialize an instance of SlowMap using this address +2. Store the SlowTree address in public storage and initialize an instance of SlowMap using this address #include_code write_slow_update_public noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr rust -7. Now you can read and update from private functions: +3. Now you can read and update from private functions: #include_code get_and_update_private noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr rust -8. Or from public functions: +4. Or from public functions: #include_code get_public noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr rust diff --git a/docs/docs/developers/contracts/writing_contracts/historical_data/slow_updates_tree/main.md b/docs/docs/developers/contracts/writing_contracts/historical_data/slow_updates_tree/main.md index 170981942d9..4edc82d0537 100644 --- a/docs/docs/developers/contracts/writing_contracts/historical_data/slow_updates_tree/main.md +++ b/docs/docs/developers/contracts/writing_contracts/historical_data/slow_updates_tree/main.md @@ -16,10 +16,6 @@ There are generally 4 main components involved to make it easier to use a slow u This is the primary smart contract that will use the slow updates tree. In the example we use a [token with blacklisting features](./implement_slow_updates.md#exploring-an-example-integration-through-a-tokenblacklist-smart-contract). -## Interface - -This interface of the slow updates tree contract allows your contract to interact with the Slow Updates Tree contract. It provides methods for reading and updating values in the tree in both public and private contexts. You can find it [here](https://github.com/AztecProtocol/aztec-packages/blob/master/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/interfaces.nr). - ## SlowTree.nr contract This is a smart contract developed by Aztec that establishes and manages a slow updates tree structure. It allows developers to access and interact with the tree, such as reading and updating data. diff --git a/docs/docs/developers/getting_started/aztecnr-getting-started.md b/docs/docs/developers/getting_started/aztecnr-getting-started.md index ae8b1e83e23..d7b9dd1eb10 100644 --- a/docs/docs/developers/getting_started/aztecnr-getting-started.md +++ b/docs/docs/developers/getting_started/aztecnr-getting-started.md @@ -147,7 +147,7 @@ This will compile the smart contract and create a `target` folder with a `.json` After compiling, you can generate a typescript class. In the same directory, run this: ```bash -aztec-cli codegen target -o src/artifacts --ts +aztec-cli codegen target -o src/artifacts ``` ### Deploy diff --git a/docs/docs/developers/tutorials/token_portal/minting_on_aztec.md b/docs/docs/developers/tutorials/token_portal/minting_on_aztec.md index 877a507122d..0eb400f0acb 100644 --- a/docs/docs/developers/tutorials/token_portal/minting_on_aztec.md +++ b/docs/docs/developers/tutorials/token_portal/minting_on_aztec.md @@ -12,13 +12,6 @@ In our `token-bridge` Aztec project in `aztec-contracts`, under `src` there is a #include_code token_bridge_storage_and_constructor /noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr rust -This imports Aztec-related dependencies and our helper file `token_interface.nr`. -(The code above will give errors right now - this is because we haven't implemented util and token_interface yet.) - -In `token_interface.nr`, add this: - -#include_code token_bridge_token_interface /noir-projects/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr rust - ## Consume the L1 message In the previous step, we have moved our funds to the portal and created a L1->L2 message. Upon building the next rollup, the sequencer asks the inbox for any incoming messages and adds them to Aztec’s L1->L2 message tree, so an application on L2 can prove that the message exists and consumes it. diff --git a/docs/docs/developers/tutorials/token_portal/setup.md b/docs/docs/developers/tutorials/token_portal/setup.md index 1678df1f98e..8eb45c6d435 100644 --- a/docs/docs/developers/tutorials/token_portal/setup.md +++ b/docs/docs/developers/tutorials/token_portal/setup.md @@ -65,7 +65,7 @@ aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_ token_portal_content_hash_lib = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/noir-contracts/contracts/token_portal_content_hash_lib" } ``` -We will also be writing some helper functions that should exist elsewhere so we don't overcomplicated our contract. In `src` create two more files - one called `util.nr` and one called `token_interface` - so your dir structure should now look like this: +We will also be writing some helper functions that should exist elsewhere so we don't overcomplicated our contract. In `src` create one more file called `util.nr` - so your dir structure should now look like this: ```tree aztec-contracts @@ -73,7 +73,6 @@ aztec-contracts ├── Nargo.toml ├── src ├── main.nr - ├── token_interface.nr ├── util.nr ``` diff --git a/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md b/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md index 4e2246e20fb..524e106ef51 100644 --- a/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md +++ b/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md @@ -81,7 +81,7 @@ aztec-nargo compile And generate the TypeScript interface for the contract and add it to the test dir: ```bash -aztec-cli codegen target -o ../../src/test/fixtures --ts +aztec-cli codegen target -o ../../src/test/fixtures ``` This will create a TS interface in our `src/test` folder! diff --git a/docs/docs/developers/tutorials/uniswap/setup.md b/docs/docs/developers/tutorials/uniswap/setup.md index a80b63e734a..ba86ecb73be 100644 --- a/docs/docs/developers/tutorials/uniswap/setup.md +++ b/docs/docs/developers/tutorials/uniswap/setup.md @@ -50,6 +50,8 @@ Inside `uniswap/Nargo.toml` paste this in `[dependencies]`: [dependencies] aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/aztec" } authwit = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/authwit"} +token = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/noir-contracts/token_contract" } +token_bridge = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/noir-contracts/token_bridge_contract" } ``` ## L2 contracts @@ -57,18 +59,9 @@ authwit = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#includ The `main.nr` will utilize a few helper functions that are outside the scope of this tutorial. Inside `uniswap/src` create two new files: ```bash -cd uniswap/src && touch util.nr && touch interfaces.nr +cd uniswap/src && touch util.nr ``` -Inside `interfaces.nr` paste this: - -#include_code interfaces noir-projects/noir-contracts/contracts/uniswap_contract/src/interfaces.nr rust - -This creates interfaces for the `Token` contract and `TokenBridge` contract - -- `Token` is a reference implementation for a token on Aztec. Here we just need two methods - [`transfer_public`](../writing_token_contract.md#transfer_public) and [`unshield()`](../writing_token_contract.md#unshield). -- The `TokenBridge` facilitates interactions with our [bridge contract](../token_portal/main.md). Here we just need its [`exit_to_l1_public`](../token_portal/withdrawing_to_l1.md) - ## Run Aztec sandbox You will need a running sandbox. diff --git a/docs/docs/developers/tutorials/uniswap/typescript_glue_code.md b/docs/docs/developers/tutorials/uniswap/typescript_glue_code.md index 805a9249826..2d5d04e4a1b 100644 --- a/docs/docs/developers/tutorials/uniswap/typescript_glue_code.md +++ b/docs/docs/developers/tutorials/uniswap/typescript_glue_code.md @@ -36,7 +36,7 @@ aztec-nargo compile And then generate the typescript interface: ```bash -aztec-cli codegen ./target/ -o ../../../src/test/fixtures uniswap --ts +aztec-cli codegen ./target/ -o ../../../src/test/fixtures uniswap ``` This will create a TS interface in our `src/test` folder that will help us write our test. diff --git a/docs/docs/developers/tutorials/writing_private_voting_contract.md b/docs/docs/developers/tutorials/writing_private_voting_contract.md index 685b89a9412..1178600c72a 100644 --- a/docs/docs/developers/tutorials/writing_private_voting_contract.md +++ b/docs/docs/developers/tutorials/writing_private_voting_contract.md @@ -158,7 +158,7 @@ aztec-nargo compile This will create a new directory called `target` and a JSON artifact inside it. To optionally create a typescript interface, run: ```bash -aztec-cli codegen target -o src/artifacts --ts +aztec-cli codegen target -o src/artifacts ``` Once it is compiled you can [deploy](../contracts/deploying_contracts/how_to_deploy_contract.md) it to the sandbox. Ensure your [sandbox is running](../sandbox/references/sandbox-reference.md) and run this in the same dir as before: diff --git a/docs/docs/developers/tutorials/writing_token_contract.md b/docs/docs/developers/tutorials/writing_token_contract.md index a4f5926c5b5..a9b9e942391 100644 --- a/docs/docs/developers/tutorials/writing_token_contract.md +++ b/docs/docs/developers/tutorials/writing_token_contract.md @@ -431,7 +431,7 @@ aztec-nargo compile Once your contract is compiled, optionally generate a typescript interface with the following command: ```bash -aztec-cli codegen target -o src/artifacts --ts +aztec-cli codegen target -o src/artifacts ``` ## Next Steps diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index 8f4f8b84217..5b0a8c0cf29 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -8,6 +8,54 @@ Aztec is in full-speed development. Literally every version breaks compatibility ## TBD +### [Aztec.nr] Contract interfaces + +It is now possible to import contracts on another contracts and use their automatic interfaces to perform calls. The interfaces have the same name as the contract, and are automatically exported. Parameters are automatically serialized (using the `Serialize` trait) and return values are automatically deserialized (using the `Deserialize` trait). Serialize and Deserialize methods have to conform to the standard ACVM serialization schema for the interface to work! + +1. Only fixed length types are supported +2. All numeric types become Fields +3. Strings become arrays of Fields, one per char +4. Arrays become arrays of Fields following rules 2 and 3 +5. Structs become arrays of Fields, with every item defined in the same order as they are in Noir code, following rules 2, 3, 4 and 5 (recursive) + + +```diff +- context.call_public_function( +- storage.gas_token_address.read_private(), +- FunctionSelector::from_signature("pay_fee(Field)"), +- [42] +- ); +- +- context.call_public_function( +- storage.gas_token_address.read_private(), +- FunctionSelector::from_signature("pay_fee(Field)"), +- [42] +- ); +- +- let _ = context.call_private_function( +- storage.subscription_token_address.read_private(), +- FunctionSelector::from_signature("transfer((Field),(Field),Field,Field)"), +- [ +- context.msg_sender().to_field(), +- storage.subscription_recipient_address.read_private().to_field(), +- storage.subscription_price.read_private(), +- nonce +- ] +- ); ++ use dep::gas_token::GasToken; ++ use dep::token::Token; ++ ++ ... ++ // Public call from public land ++ GasToken::at(storage.gas_token_address.read_private()).pay_fee(42).call(&mut context); ++ // Public call from private land ++ GasToken::at(storage.gas_token_address.read_private()).pay_fee(42).enqueue(&mut context); ++ // Private call from private land ++ Token::at(asset).transfer(context.msg_sender(), storage.subscription_recipient_address.read_private(), amount, nonce).call(&mut context); +``` + +It is also possible to use these automatic interfaces from the local contract, and thus enqueue public calls from private without having to rely on low level `context` calls. + ### [Aztec.nr] Rename max block number setter The `request_max_block_number` function has been renamed to `set_tx_max_block_number` to better reflect that it is not a getter, and that the setting is transaction-wide. diff --git a/noir-projects/aztec-nr/authwit/src/auth.nr b/noir-projects/aztec-nr/authwit/src/auth.nr index 643f725e2f2..55c15acec77 100644 --- a/noir-projects/aztec-nr/authwit/src/auth.nr +++ b/noir-projects/aztec-nr/authwit/src/auth.nr @@ -37,7 +37,7 @@ pub fn assert_current_call_valid_authwit_public( context, on_behalf_of, function_selector, - [inner_hash], + [inner_hash].as_slice(), GasOpts::default() ).deserialize_into(); assert(result == IS_VALID_SELECTOR, "Message not authorized by account"); diff --git a/noir-projects/aztec-nr/aztec/src/context.nr b/noir-projects/aztec-nr/aztec/src/context.nr index 2b546b1e411..c7d79f2e24b 100644 --- a/noir-projects/aztec-nr/aztec/src/context.nr +++ b/noir-projects/aztec-nr/aztec/src/context.nr @@ -7,7 +7,10 @@ mod avm_context; mod interface; mod gas; -use interface::ContextInterface; +use interface::{ + ContextInterface, PrivateCallInterface, PublicCallInterface, PrivateVoidCallInterface, + PublicVoidCallInterface, AvmCallInterface, AvmVoidCallInterface +}; use private_context::PrivateContext; use private_context::PackedReturns; use public_context::PublicContext; diff --git a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr index 0446511184d..c3379ea7419 100644 --- a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr @@ -50,32 +50,32 @@ impl AvmContext { l1_to_l2_msg_exists(msg_hash, msg_leaf_index) == 1 } - fn call_public_function_raw( + fn call_public_function_raw( self: &mut Self, gas: GasOpts, contract_address: AztecAddress, temporary_function_selector: Field, - args: [Field; ARGS_COUNT] + args: [Field] ) -> ([Field; RET_COUNT], u8) { call( gas_for_call(gas), contract_address, - args.as_slice(), + args, temporary_function_selector ) } - fn static_call_public_function_raw( + fn static_call_public_function_raw( self: &mut Self, gas: GasOpts, contract_address: AztecAddress, temporary_function_selector: Field, - args: [Field; ARGS_COUNT] + args: [Field] ) -> ([Field; RET_COUNT], u8) { call_static( gas_for_call(gas), contract_address, - args.as_slice(), + args, temporary_function_selector ) } @@ -136,17 +136,17 @@ impl PublicContextInterface for AvmContext { send_l2_to_l1_msg(recipient, content); } - fn call_public_function( + fn call_public_function( self: &mut Self, contract_address: AztecAddress, temporary_function_selector: FunctionSelector, - args: [Field; ARGS_COUNT], + args: [Field], gas_opts: GasOpts ) -> FunctionReturns { let results = call( gas_for_call(gas_opts), contract_address, - args.as_slice(), + args, temporary_function_selector.to_field() ); let data_to_return: [Field; RETURNS_COUNT] = results.0; @@ -156,17 +156,17 @@ impl PublicContextInterface for AvmContext { FunctionReturns::new(data_to_return) } - fn static_call_public_function( + fn static_call_public_function( self: &mut Self, contract_address: AztecAddress, temporary_function_selector: FunctionSelector, - args: [Field; ARGS_COUNT], + args: [Field], gas_opts: GasOpts ) -> FunctionReturns { let (data_to_return, success): ([Field; RETURNS_COUNT], u8) = call_static( gas_for_call(gas_opts), contract_address, - args.as_slice(), + args, temporary_function_selector.to_field() ); @@ -174,11 +174,11 @@ impl PublicContextInterface for AvmContext { FunctionReturns::new(data_to_return) } - fn delegate_call_public_function( + fn delegate_call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + args: [Field] ) -> FunctionReturns { assert(false, "'delegate_call_public_function' not implemented!"); FunctionReturns::new([0; RETURNS_COUNT]) diff --git a/noir-projects/aztec-nr/aztec/src/context/interface.nr b/noir-projects/aztec-nr/aztec/src/context/interface.nr index ef64964368c..d087160c654 100644 --- a/noir-projects/aztec-nr/aztec/src/context/interface.nr +++ b/noir-projects/aztec-nr/aztec/src/context/interface.nr @@ -1,5 +1,11 @@ -use dep::protocol_types::{abis::function_selector::FunctionSelector, address::{AztecAddress, EthAddress}, header::Header}; +use dep::protocol_types::{ + abis::function_selector::FunctionSelector, address::{AztecAddress, EthAddress}, header::Header, + traits::Deserialize +}; +use crate::context::private_context::PrivateContext; +use crate::context::public_context::PublicContext; +use crate::context::avm_context::AvmContext; use crate::context::gas::GasOpts; use crate::context::public_context::FunctionReturns; @@ -30,25 +36,223 @@ trait PublicContextInterface { fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress); fn accumulate_encrypted_logs(&mut self, log: [Field; N]); fn accumulate_unencrypted_logs(&mut self, log: T); - fn call_public_function( + fn call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT], + args: [Field], gas_opts: GasOpts ) -> FunctionReturns; - fn static_call_public_function( + fn static_call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT], + args: [Field], gas_opts: GasOpts ) -> FunctionReturns; - fn delegate_call_public_function( + fn delegate_call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + args: [Field] ) -> FunctionReturns; fn nullifier_exists(self, unsiloed_nullifier: Field, address: AztecAddress) -> bool; } + +struct PrivateCallInterface { + target_contract: AztecAddress, + selector: FunctionSelector, + args_hash: Field, +} + +impl PrivateCallInterface { + pub fn call(self, context: &mut PrivateContext) -> T where T: Deserialize { + let returns = context.call_private_function_with_packed_args( + self.target_contract, + self.selector, + self.args_hash, + false, + false + ); + let unpacked: T = returns.unpack_into(); + unpacked + } + + pub fn static_call(self, context: &mut PrivateContext) -> T where T: Deserialize { + let returns = context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false); + returns.unpack_into() + } + + pub fn delegate_call(self, context: &mut PrivateContext) -> T where T: Deserialize { + let returns = context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, false, true); + returns.unpack_into() + } +} + +struct PrivateVoidCallInterface { + target_contract: AztecAddress, + selector: FunctionSelector, + args_hash: Field, +} + +impl PrivateVoidCallInterface { + pub fn call(self, context: &mut PrivateContext) { + context.call_private_function_with_packed_args( + self.target_contract, + self.selector, + self.args_hash, + false, + false + ).assert_empty(); + } + + pub fn static_call(self, context: &mut PrivateContext) { + context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false).assert_empty(); + } + + pub fn delegate_call(self, context: &mut PrivateContext) { + context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, false, true).assert_empty(); + } +} + +struct PublicCallInterface { + target_contract: AztecAddress, + selector: FunctionSelector, + args_hash: Field, +} + +impl PublicCallInterface { + + pub fn call(self, context: &mut PublicContext) -> T where T: Deserialize { + let returns = context.call_public_function_with_packed_args( + self.target_contract, + self.selector, + self.args_hash, + false, + false + ); + returns.deserialize_into() + } + + pub fn static_call(self, context: &mut PublicContext) -> T where T: Deserialize { + let returns = context.call_public_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false); + returns.deserialize_into() + } + + pub fn delegate_call(self, context: &mut PublicContext) -> T where T: Deserialize { + let returns = context.call_public_function_with_packed_args(self.target_contract, self.selector, self.args_hash, false, true); + returns.deserialize_into() + } + + pub fn enqueue(self, context: &mut PrivateContext) { + context.call_public_function_with_packed_args( + self.target_contract, + self.selector, + self.args_hash, + false, + false + ) + } + + pub fn static_enqueue(self, context: &mut PrivateContext) { + context.call_public_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false) + } + + pub fn delegate_enqueue(self, context: &mut PrivateContext) { + context.call_public_function_with_packed_args(self.target_contract, self.selector, self.args_hash, false, true) + } +} + +struct PublicVoidCallInterface { + target_contract: AztecAddress, + selector: FunctionSelector, + args_hash: Field +} + +impl PublicVoidCallInterface { + pub fn call(self, context: &mut PublicContext) { + context.call_public_function_with_packed_args( + self.target_contract, + self.selector, + self.args_hash, + false, + false + ).assert_empty() + } + + pub fn static_call(self, context: &mut PublicContext) { + context.call_public_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false).assert_empty(); + } + + pub fn delegate_call(self, context: &mut PublicContext) { + context.call_public_function_with_packed_args(self.target_contract, self.selector, self.args_hash, false, true).assert_empty(); + } + + pub fn enqueue(self, context: &mut PrivateContext) { + context.call_public_function_with_packed_args( + self.target_contract, + self.selector, + self.args_hash, + false, + false + ) + } + + pub fn static_enqueue(self, context: &mut PrivateContext) { + context.call_public_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false) + } + + pub fn delegate_enqueue(self, context: &mut PrivateContext) { + context.call_public_function_with_packed_args(self.target_contract, self.selector, self.args_hash, false, true) + } +} + +struct AvmCallInterface { + target_contract: AztecAddress, + selector: FunctionSelector, + args: [Field], +} + +impl AvmCallInterface { + pub fn call(self, context: &mut AvmContext, gas_opts: GasOpts) -> T where T: Deserialize { + let returns = context.call_public_function(self.target_contract, self.selector, self.args, gas_opts); + returns.deserialize_into() + } + + pub fn static_call( + self, + context: &mut AvmContext, + gas_opts: GasOpts + ) -> T where T: Deserialize { + let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, gas_opts); + returns.deserialize_into() + } + + pub fn delegate_call(self, context: &mut AvmContext) -> T where T: Deserialize { + let returns = context.delegate_call_public_function(self.target_contract, self.selector, self.args); + returns.deserialize_into() + } +} + +struct AvmVoidCallInterface { + target_contract: AztecAddress, + selector: FunctionSelector, + args: [Field], +} + +impl AvmVoidCallInterface { + pub fn call(self, context: &mut AvmContext, gas_opts: GasOpts) { + let returns = context.call_public_function(self.target_contract, self.selector, self.args, gas_opts); + returns.assert_empty() + } + + pub fn static_call(self, context: &mut AvmContext, gas_opts: GasOpts) { + let returns = context.static_call_public_function(self.target_contract, self.selector, self.args, gas_opts); + returns.assert_empty() + } + + pub fn delegate_call(self, context: &mut AvmContext) { + let returns = context.delegate_call_public_function(self.target_contract, self.selector, self.args); + returns.assert_empty() + } +} diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index b70680db040..0fcdaa74bac 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -282,7 +282,7 @@ impl PrivateContext { args: [Field; ARGS_COUNT] ) -> PackedReturns { let args_hash = hash_args_array(args); - assert(args_hash == arguments::pack_arguments(args)); + assert(args_hash == arguments::pack_arguments_array(args)); self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, false, false) } @@ -293,7 +293,7 @@ impl PrivateContext { args: [Field; ARGS_COUNT] ) -> PackedReturns { let args_hash = hash_args_array(args); - assert(args_hash == arguments::pack_arguments(args)); + assert(args_hash == arguments::pack_arguments_array(args)); self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, true, false) } @@ -304,7 +304,7 @@ impl PrivateContext { args: [Field; ARGS_COUNT] ) -> PackedReturns { let args_hash = hash_args_array(args); - assert(args_hash == arguments::pack_arguments(args)); + assert(args_hash == arguments::pack_arguments_array(args)); self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, false, true) } @@ -402,7 +402,7 @@ impl PrivateContext { args: [Field; ARGS_COUNT] ) { let args_hash = hash_args_array(args); - assert(args_hash == arguments::pack_arguments(args)); + assert(args_hash == arguments::pack_arguments_array(args)); self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, false, false) } @@ -413,7 +413,7 @@ impl PrivateContext { args: [Field; ARGS_COUNT] ) { let args_hash = hash_args_array(args); - assert(args_hash == arguments::pack_arguments(args)); + assert(args_hash == arguments::pack_arguments_array(args)); self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, true, false) } @@ -424,7 +424,7 @@ impl PrivateContext { args: [Field; ARGS_COUNT] ) { let args_hash = hash_args_array(args); - assert(args_hash == arguments::pack_arguments(args)); + assert(args_hash == arguments::pack_arguments_array(args)); self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, false, true) } 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 1c61e53712e..cdbbb3e939b 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -5,7 +5,7 @@ use crate::{ }, messaging::process_l1_to_l2_message, oracle::{arguments, public_call::call_public_function_internal, returns}, - hash::{hash_args_array, ArgsHasher} + hash::{hash_args, ArgsHasher} }; use dep::protocol_types::{ abis::{ @@ -291,37 +291,37 @@ impl PublicContextInterface for PublicContext { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) } - fn call_public_function( + fn call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT], + args: [Field], _gas: GasOpts ) -> FunctionReturns { - let args_hash = hash_args_array(args); + let args_hash = hash_args(args); assert(args_hash == arguments::pack_arguments(args)); self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, false, false) } - fn static_call_public_function( + fn static_call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT], + args: [Field], _gas: GasOpts ) -> FunctionReturns { - let args_hash = hash_args_array(args); + let args_hash = hash_args(args); assert(args_hash == arguments::pack_arguments(args)); self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, true, false) } - fn delegate_call_public_function( + fn delegate_call_public_function( self: &mut Self, contract_address: AztecAddress, function_selector: FunctionSelector, - args: [Field; ARGS_COUNT] + args: [Field] ) -> FunctionReturns { - let args_hash = hash_args_array(args); + let args_hash = hash_args(args); assert(args_hash == arguments::pack_arguments(args)); self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, false, true) } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/arguments.nr b/noir-projects/aztec-nr/aztec/src/oracle/arguments.nr index f1de424f840..2bbf2337117 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/arguments.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/arguments.nr @@ -1,11 +1,24 @@ +#[oracle(packArgumentsArray)] +fn pack_arguments_array_oracle(_args: [Field; N]) -> Field {} + #[oracle(packArguments)] -fn pack_arguments_oracle(_args: [Field; N]) -> Field {} +fn pack_arguments_oracle(_args: [Field]) -> Field {} + +/// - Pack arguments (array version) will notify the simulator that these arguments will be used later at +/// some point in the call. +/// - When the external call is made later, the simulator will know what the values unpack to. +/// - This oracle will not be required in public vm functions, as the vm will keep track of arguments +/// itself. +unconstrained pub fn pack_arguments_array(args: [Field; N]) -> Field { + pack_arguments_array_oracle(args) +} -/// - Pack arguments will notify the simulator that these arguments will be used later at +/// - Pack arguments (slice version) will notify the simulator that these arguments will be used later at /// some point in the call. /// - When the external call is made later, the simulator will know what the values unpack to. /// - This oracle will not be required in public vm functions, as the vm will keep track of arguments /// itself. -unconstrained pub fn pack_arguments(args: [Field; N]) -> Field { +unconstrained pub fn pack_arguments(args: [Field]) -> Field { pack_arguments_oracle(args) } + diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/app_subscription_contract/Nargo.toml index 34df286511b..cf460fd9497 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/Nargo.toml @@ -7,3 +7,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } authwit = { path = "../../../aztec-nr/authwit" } +gas_token = { path = "../gas_token_contract" } +token = { path = "../token_contract" } \ No newline at end of file diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr index 999f07771bd..eb316f30186 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr @@ -17,6 +17,9 @@ contract AppSubscription { use crate::subscription_note::{SubscriptionNote, SUBSCRIPTION_NOTE_LEN}; + use dep::gas_token::GasToken; + use dep::token::Token; + #[aztec(storage)] struct Storage { // The following is only needed in private but we use ShareImmutable here instead of PrivateImmutable because @@ -44,19 +47,11 @@ contract AppSubscription { note.remaining_txs -= 1; storage.subscriptions.at(user_address).replace(&mut note, true); - context.call_public_function( - storage.gas_token_address.read_private(), - FunctionSelector::from_signature("pay_fee(Field)"), - [42] - ); + GasToken::at(storage.gas_token_address.read_private()).pay_fee(42).enqueue(&mut context); context.capture_min_revertible_side_effect_counter(); - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("assert_not_expired(Field)"), - [note.expiry_block_number] - ); + AppSubscription::at(context.this_address()).assert_not_expired(note.expiry_block_number).enqueue(&mut context); payload.execute_calls(&mut context, storage.target_address.read_private()); } @@ -102,20 +97,15 @@ contract AppSubscription { ) { assert(tx_count as u64 <= SUBSCRIPTION_TXS as u64); - let _ = context.call_private_function( - storage.subscription_token_address.read_private(), - FunctionSelector::from_signature("transfer((Field),(Field),Field,Field)"), - [ - context.msg_sender().to_field(), storage.subscription_recipient_address.read_private().to_field(), storage.subscription_price.read_private(), nonce - ] - ); + Token::at(storage.subscription_token_address.read_private()).transfer( + context.msg_sender(), + storage.subscription_recipient_address.read_private(), + storage.subscription_price.read_private(), + nonce + ).call(&mut context); // Assert that the given expiry_block_number < current_block_number + SUBSCRIPTION_DURATION_IN_BLOCKS. - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("assert_block_number(Field)"), - [expiry_block_number] - ); + AppSubscription::at(context.this_address()).assert_block_number(expiry_block_number).enqueue(&mut context); let mut subscription_note = SubscriptionNote::new(subscriber_address, expiry_block_number, tx_count); if (!is_initialized(subscriber_address)) { diff --git a/noir-projects/noir-contracts/contracts/avm_acvm_interop_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_acvm_interop_test_contract/src/main.nr index c1af41666e9..bece62fc8dc 100644 --- a/noir-projects/noir-contracts/contracts/avm_acvm_interop_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_acvm_interop_test_contract/src/main.nr @@ -42,7 +42,7 @@ contract AvmAcvmInteropTest { context.call_public_function( context.this_address(), FunctionSelector::from_signature("constant_field_acvm()"), - [], + [].as_slice(), GasOpts::default() ).deserialize_into() } @@ -52,14 +52,19 @@ contract AvmAcvmInteropTest { context.call_public_function( context.this_address(), FunctionSelector::from_signature("constant_field_avm()"), - [], + [].as_slice(), GasOpts::default() ).deserialize_into() } #[aztec(public-vm)] fn avm_to_acvm_call(selector: FunctionSelector, args: Field) { - context.call_public_function(context.this_address(), selector, [args], GasOpts::default()).assert_empty(); + context.call_public_function( + context.this_address(), + selector, + [args].as_slice(), + GasOpts::default() + ).assert_empty(); } /************************************************************************ diff --git a/noir-projects/noir-contracts/contracts/avm_nested_calls_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_nested_calls_test_contract/src/main.nr index 4c056f838de..e5d34183f42 100644 --- a/noir-projects/noir-contracts/contracts/avm_nested_calls_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_nested_calls_test_contract/src/main.nr @@ -42,7 +42,7 @@ contract AvmNestedCallsTest { GasOpts::default(), context.this_address(), selector, - [arg_a, arg_b] + [arg_a, arg_b].as_slice() ); let data_to_return: [Field; 1] = results.0; // this explicit size is necessary to ensure that ret_size is compile-time @@ -71,7 +71,7 @@ contract AvmNestedCallsTest { GasOpts::new(l1_gas, l2_gas, da_gas), context.this_address(), selector, - [arg_a, arg_b] + [arg_a, arg_b].as_slice() ); let data_to_return: [Field; 1] = results.0; // this explicit size is necessary to ensure that ret_size is compile-time @@ -85,15 +85,7 @@ contract AvmNestedCallsTest { // Use the `call_public_function` wrapper to initiate a nested call to the add function #[aztec(public-vm)] fn nested_call_to_add(arg_a: Field, arg_b: Field) -> pub Field { - let selector = FunctionSelector::from_signature("add_args_return(Field,Field)"); - - // Nested call using standard context interface function - context.call_public_function( - context.this_address(), - selector, - [arg_a, arg_b], - GasOpts::default() - ).deserialize_into() + AvmNestedCallsTest::at(context.this_address()).add_args_return(arg_a, arg_b).call(&mut context, GasOpts::default()) } // Directly call_static the external call opcode to initiate a nested call to the add function @@ -105,7 +97,7 @@ contract AvmNestedCallsTest { GasOpts::default(), context.this_address(), selector, - [arg_a, arg_b] + [arg_a, arg_b].as_slice() ); (result_data[0], success) @@ -117,7 +109,12 @@ contract AvmNestedCallsTest { let selector = FunctionSelector::from_signature("set_storage_single(Field)").to_field(); let calldata: [Field; 1] = [20]; - let (_data_to_return, success): ([Field; 0], u8) = context.static_call_public_function_raw(GasOpts::default(), context.this_address(), selector, calldata); + let (_data_to_return, success): ([Field; 0], u8) = context.static_call_public_function_raw( + GasOpts::default(), + context.this_address(), + selector, + calldata.as_slice() + ); success } @@ -125,40 +122,24 @@ contract AvmNestedCallsTest { // Indirectly call_static the external call opcode to initiate a nested call to the add function #[aztec(public-vm)] fn nested_static_call_to_add(arg_a: Field, arg_b: Field) -> pub Field { - let selector = FunctionSelector::from_signature("add_args_return(Field,Field)"); - - context.static_call_public_function( - context.this_address(), - selector, - [arg_a, arg_b], - GasOpts::default() - ).deserialize_into() + AvmNestedCallsTest::at(context.this_address()).add_args_return(arg_a, arg_b).static_call(&mut context, GasOpts::default()) } // Indirectly call_static `set_storage_single`. Should revert since it's accessing storage. #[aztec(public-vm)] fn nested_static_call_to_set_storage() { - let selector = FunctionSelector::from_signature("set_storage_single(Field)"); - let calldata: [Field; 1] = [20]; - - context.static_call_public_function(context.this_address(), selector, calldata, GasOpts::default()).assert_empty(); + AvmNestedCallsTest::at(context.this_address()).set_storage_single(20).static_call(&mut context, GasOpts::default()); } #[aztec(public-vm)] fn create_same_nullifier_in_nested_call(nestedAddress: AztecAddress, nullifier: Field) { context.push_new_nullifier(nullifier, 0); - - let selector = FunctionSelector::from_signature("new_nullifier(Field)"); - let calldata: [Field; 1] = [nullifier]; - let _ : FunctionReturns<0> = context.call_public_function(nestedAddress, selector, calldata, GasOpts::default()); + AvmNestedCallsTest::at(nestedAddress).new_nullifier(nullifier).call(&mut context, GasOpts::default()); } #[aztec(public-vm)] fn create_different_nullifier_in_nested_call(nestedAddress: AztecAddress, nullifier: Field) { context.push_new_nullifier(nullifier, 0); - - let selector = FunctionSelector::from_signature("new_nullifier(Field)"); - let calldata: [Field; 1] = [nullifier + 1]; - let _ : FunctionReturns<0> = context.call_public_function(nestedAddress, selector, calldata, GasOpts::default()); + AvmNestedCallsTest::at(nestedAddress).new_nullifier(nullifier + 1).call(&mut context, GasOpts::default()); } } diff --git a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr index 4e688ceb987..38511fc3a9f 100644 --- a/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/benchmarking_contract/src/main.nr @@ -44,12 +44,7 @@ contract Benchmarking { fn increment_balance(owner: AztecAddress, value: Field) { let current = storage.balances.at(owner).read(); storage.balances.at(owner).write(current + value); - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("broadcast((Field))"), - [owner.to_field()], - GasOpts::default() - ).assert_empty(); + Benchmarking::at(context.this_address()).broadcast(owner).call(&mut context); } // Emits a public log. diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr index 1e54f0c2866..d4416eff7cd 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/main.nr @@ -39,13 +39,8 @@ contract CardGame { collection.remove_cards(cards, player); let mut game_deck = storage.game_decks.at(game as Field).at(player); let _added_to_game_deck = game_deck.add_cards(cards, player); - let selector = FunctionSelector::from_signature("on_game_joined(u32,(Field),u32)"); let strength = compute_deck_strength(cards); - context.call_public_function( - context.this_address(), - selector, - [game as Field, player.to_field(), strength] - ); + CardGame::at(context.this_address()).on_game_joined(game, player, strength as u32).enqueue(&mut context); } #[aztec(public)] @@ -75,13 +70,8 @@ contract CardGame { let mut game_deck = storage.game_decks.at(game as Field).at(player); game_deck.remove_cards([card], player); - let selector = FunctionSelector::from_signature("on_card_played(u32,(Field),Field)"); // docs:start:call_public_function - context.call_public_function( - context.this_address(), - selector, - [game as Field, player.to_field(), card.to_field()] - ); + CardGame::at(context.this_address()).on_card_played(game, player, card.to_field()).enqueue(&mut context); // docs:end:call_public_function } @@ -107,13 +97,7 @@ contract CardGame { let mut collection = storage.collections.at(player); let _inserted_cards = collection.add_cards(cards, player); - - let selector = FunctionSelector::from_signature("on_cards_claimed(u32,(Field),Field)"); - context.call_public_function( - context.this_address(), - selector, - [game as Field, player.to_field(), pedersen_hash(cards_fields, 0)] - ); + CardGame::at(context.this_address()).on_cards_claimed(game, player, pedersen_hash(cards_fields, 0)).enqueue(&mut context); } #[aztec(public)] diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index 8304aaecb3b..15117e2dbfa 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -94,14 +94,7 @@ contract Child { #[aztec(public)] fn set_value_twice_with_nested_first() { - let pub_set_value_selector = FunctionSelector::from_signature("pub_set_value(Field)"); - let _result: Field = context.call_public_function( - context.this_address(), - pub_set_value_selector, - [10], - GasOpts::default() - ).deserialize_into(); - + let _result = Child::at(context.this_address()).pub_set_value(10).call(&mut context); storage.current_value.write(20); emit_unencrypted_log(&mut context, 20); } @@ -110,13 +103,6 @@ contract Child { fn set_value_twice_with_nested_last() { storage.current_value.write(20); emit_unencrypted_log(&mut context, 20); - - let pub_set_value_selector = FunctionSelector::from_signature("pub_set_value(Field)"); - let _result: Field = context.call_public_function( - context.this_address(), - pub_set_value_selector, - [10], - GasOpts::default() - ).deserialize_into(); + let _result = Child::at(context.this_address()).pub_set_value(10).call(&mut context); } } diff --git a/noir-projects/noir-contracts/contracts/claim_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/claim_contract/Nargo.toml index 1db74bdf3ac..7d81995453f 100644 --- a/noir-projects/noir-contracts/contracts/claim_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/claim_contract/Nargo.toml @@ -7,3 +7,4 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } value_note = { path = "../../../aztec-nr/value-note" } +token = { path = "../token_contract" } diff --git a/noir-projects/noir-contracts/contracts/claim_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/claim_contract/src/interfaces.nr deleted file mode 100644 index a7e14c8d9fc..00000000000 --- a/noir-projects/noir-contracts/contracts/claim_contract/src/interfaces.nr +++ /dev/null @@ -1,37 +0,0 @@ -use dep::aztec::{ - protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}, - context::PrivateContext -}; - -struct Token { - address: AztecAddress, -} - -impl Token { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - fn mint_public(self: Self, context: &mut PrivateContext, to: AztecAddress, amount: Field) { - let _ret = context.call_public_function( - self.address, - FunctionSelector::from_signature("mint_public((Field),Field)"), - [to.to_field(), amount] - ); - } - - pub fn transfer( - self: Self, - context: &mut PrivateContext, - from: AztecAddress, - to: AztecAddress, - amount: Field, - nonce: Field - ) { - let _ret = context.call_private_function( - self.address, - FunctionSelector::from_signature("transfer((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] - ); - } -} diff --git a/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr b/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr index 6cac882ba6f..090ea87a0a0 100644 --- a/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr @@ -1,13 +1,11 @@ contract Claim { - mod interfaces; - use dep::aztec::{ history::note_inclusion::prove_note_inclusion, protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}, state_vars::SharedImmutable }; use dep::value_note::value_note::ValueNote; - use interfaces::Token; + use dep::token::Token; #[aztec(storage)] struct Storage { @@ -43,7 +41,6 @@ contract Claim { context.push_new_nullifier(proof_note.compute_nullifier(&mut context), 0); // 4) Finally we mint the reward token to the sender of the transaction - let reward_token = Token::at(storage.reward_token.read_private()); - reward_token.mint_public(&mut context, recipient, proof_note.value); + Token::at(storage.reward_token.read_private()).mint_public(recipient, proof_note.value).enqueue(&mut context); } } diff --git a/noir-projects/noir-contracts/contracts/crowdfunding_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/crowdfunding_contract/Nargo.toml index 78810768a4d..aa72f2f65f4 100644 --- a/noir-projects/noir-contracts/contracts/crowdfunding_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/crowdfunding_contract/Nargo.toml @@ -7,3 +7,4 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } value_note = { path = "../../../aztec-nr/value-note" } +token = { path = "../token_contract" } diff --git a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/interfaces.nr deleted file mode 100644 index 746f8e0e24c..00000000000 --- a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/interfaces.nr +++ /dev/null @@ -1,27 +0,0 @@ -use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::{AztecAddress, EthAddress}}; -use dep::aztec::{context::{PrivateContext, PublicContext}}; - -struct Token { - address: AztecAddress, -} - -impl Token { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn transfer( - self: Self, - context: &mut PrivateContext, - from: AztecAddress, - to: AztecAddress, - amount: Field, - nonce: Field - ) { - let _ret = context.call_private_function( - self.address, - FunctionSelector::from_signature("transfer((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] - ); - } -} diff --git a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr index 42a0883fabd..76d6335e6ff 100644 --- a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr @@ -1,5 +1,4 @@ contract Crowdfunding { - mod interfaces; use dep::aztec::{ log::emit_unencrypted_log_from_private, @@ -7,7 +6,7 @@ contract Crowdfunding { state_vars::{PrivateSet, PublicImmutable, SharedImmutable} }; use dep::value_note::value_note::ValueNote; - use interfaces::Token; + use dep::token::Token; #[aztec(event)] struct WithdrawalProcessed { @@ -51,21 +50,15 @@ contract Crowdfunding { #[aztec(private)] fn donate(amount: u64) { // 1) Check that the deadline has not passed - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("_check_deadline()"), - [] - ); + Crowdfunding::at(context.this_address())._check_deadline().enqueue(&mut context); // 2) Transfer the donation tokens from donor to this contract - let donation_token = Token::at(storage.donation_token.read_private()); - donation_token.transfer( - &mut context, + Token::at(storage.donation_token.read_private()).transfer( context.msg_sender(), context.this_address(), amount as Field, 0 - ); + ).call(&mut context); // 3) Create a value note for the donor so that he can later on claim a rewards token in the Claim // contract by proving that the hash of this note exists in the note hash tree. @@ -81,14 +74,7 @@ contract Crowdfunding { assert(context.msg_sender() == operator_address, "Not an operator"); // 2) Transfer the donation tokens from this contract to the operator - let donation_token = Token::at(storage.donation_token.read_private()); - donation_token.transfer( - &mut context, - context.this_address(), - operator_address, - amount as Field, - 0 - ); + Token::at(storage.donation_token.read_private()).transfer(context.this_address(), operator_address, amount as Field, 0).call(&mut context); // 3) Emit an unencrypted event so that anyone can audit how much the operator has withdrawn let event = WithdrawalProcessed { amount, who: operator_address }; diff --git a/noir-projects/noir-contracts/contracts/delegator_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/delegator_contract/Nargo.toml index 8299f932ad2..1aad229b828 100644 --- a/noir-projects/noir-contracts/contracts/delegator_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/delegator_contract/Nargo.toml @@ -6,4 +6,5 @@ compiler_version = ">=0.25.0" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } -value_note = { path = "../../../aztec-nr/value-note" } \ No newline at end of file +value_note = { path = "../../../aztec-nr/value-note" } +delegated_on = { path = "../delegated_on_contract"} \ No newline at end of file diff --git a/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr b/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr index 5cb189e7410..939c2f1907f 100644 --- a/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/delegator_contract/src/main.nr @@ -5,6 +5,7 @@ contract Delegator { PublicMutable, PrivateSet, Deserialize }; use dep::value_note::value_note::ValueNote; + use dep::delegated_on::DelegatedOn; #[aztec(storage)] struct Storage { @@ -15,29 +16,21 @@ contract Delegator { #[aztec(private)] fn private_delegate_set_value( target_contract: AztecAddress, - target_selector: FunctionSelector, - args: [Field; 2] + value: Field, + owner: AztecAddress ) -> Field { // Call the target private function - context.delegate_call_private_function(target_contract, target_selector, args).unpack_into() + DelegatedOn::at(target_contract).private_set_value(value, owner).delegate_call(&mut context) } #[aztec(private)] - fn enqueued_delegate_set_value( - target_contract: AztecAddress, - target_selector: FunctionSelector, - args: [Field; 1] - ) { - context.delegate_call_public_function(target_contract, target_selector, args); + fn enqueued_delegate_set_value(target_contract: AztecAddress, value: Field) { + DelegatedOn::at(target_contract).public_set_value(value).delegate_enqueue(&mut context) } #[aztec(public)] - fn public_delegate_set_value( - target_contract: AztecAddress, - target_selector: FunctionSelector, - args: [Field; 1] - ) { - let _result: Field = context.delegate_call_public_function(target_contract, target_selector, args).deserialize_into(); + fn public_delegate_set_value(target_contract: AztecAddress, value: Field) -> Field { + DelegatedOn::at(target_contract).public_set_value(value).delegate_call(&mut context) } unconstrained fn view_private_value(amount: Field, owner: AztecAddress) -> pub Field { 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 6284c1af9a5..177c61a6669 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 @@ -115,11 +115,7 @@ contract DocsExample { // and returns the response. // Used to test that we can retrieve values through calls and // correctly return them in the simulation - let mut leader: Leader = context.call_private_function_no_args( - context.this_address(), - FunctionSelector::from_signature("get_shared_immutable_constrained_private()") - ).unpack_into(); - + let mut leader = DocsExample::at(context.this_address()).get_shared_immutable_constrained_private().call(&mut context); leader.points += 1; leader } @@ -130,11 +126,7 @@ contract DocsExample { // and returns the response. // Used to test that we can retrieve values through calls and // correctly return them in the simulation - let mut leader: Leader = context.call_public_function_no_args( - context.this_address(), - FunctionSelector::from_signature("get_shared_immutable_constrained_public()") - ).deserialize_into(); - + let mut leader = DocsExample::at(context.this_address()).get_shared_immutable_constrained_public().call(&mut context); leader.points += 1; leader } @@ -222,12 +214,7 @@ contract DocsExample { fn update_legendary_card(randomness: Field, points: u8) { let mut new_card = CardNote::new(points, randomness, context.msg_sender()); storage.legendary_card.replace(&mut new_card, true); - - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("update_leader((Field),u8)"), - [context.msg_sender().to_field(), points as Field] - ); + DocsExample::at(context.this_address()).update_leader(context.msg_sender(), points).enqueue(&mut context); } #[aztec(private)] @@ -246,11 +233,7 @@ contract DocsExample { storage.legendary_card.replace(&mut new_card, true); // docs:end:state_vars-PrivateMutableReplace - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("update_leader((Field),u8)"), - [context.msg_sender().to_field(), points as Field] - ); + DocsExample::at(context.this_address()).update_leader(context.msg_sender(), points).enqueue(&mut context); } #[aztec(private)] diff --git a/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr index a9081073066..1b5af4b8280 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr @@ -1,7 +1,7 @@ // docs:start:easy_private_token_contract contract EasyPrivateToken { use dep::aztec::prelude::{AztecAddress, NoteHeader, Map}; - use dep::value_note::{balance_utils, value_note::ValueNote}; + use dep::value_note::balance_utils; use dep::easy_private_state::EasyPrivateUint; #[aztec(storage)] diff --git a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr index 76cc88e1201..634acdb45bd 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr @@ -30,11 +30,7 @@ contract EasyPrivateVoting { let secret = context.request_nullifier_secret_key(context.msg_sender()); // get secret key of caller of function let nullifier = dep::std::hash::pedersen_hash([context.msg_sender().to_field(), secret.low, secret.high]); // compute nullifier with this secret key so others can't descrypt it context.push_new_nullifier(nullifier, 0); // push nullifier - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("add_to_tally_public(Field)"), - [candidate] - ); + EasyPrivateVoting::at(context.this_address()).add_to_tally_public(candidate).enqueue(&mut context); } // docs:end:cast_vote diff --git a/noir-projects/noir-contracts/contracts/escrow_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/escrow_contract/Nargo.toml index 9982ff8b67f..a0c107d21a4 100644 --- a/noir-projects/noir-contracts/contracts/escrow_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/escrow_contract/Nargo.toml @@ -7,3 +7,4 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } address_note = { path = "../../../aztec-nr/address-note" } +token = { path = "../token_contract" } \ No newline at end of file diff --git a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr index 709c65b3ec5..c1ec425486b 100644 --- a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr @@ -6,6 +6,8 @@ contract Escrow { use dep::address_note::address_note::AddressNote; + use dep::token::Token; + #[aztec(storage)] struct Storage { owner: PrivateImmutable, @@ -28,11 +30,6 @@ contract Escrow { let note = storage.owner.get_note(); assert(note.address == sender); - let selector = FunctionSelector::from_signature("transfer((Field),(Field),Field,Field)"); - let _callStackItem = context.call_private_function( - token, - selector, - [this.to_field(), recipient.to_field(), amount, 0] - ); + Token::at(token).transfer(this, recipient, amount, 0).call(&mut context); } } diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/fpc_contract/Nargo.toml index 887ffaf17d0..96bd8bacba7 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/fpc_contract/Nargo.toml @@ -7,3 +7,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } authwit = { path = "../../../aztec-nr/authwit" } +token = { path = "../token_contract" } +gas_token = { path = "../gas_token_contract" } diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/interfaces.nr deleted file mode 100644 index f2f94f9884a..00000000000 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/interfaces.nr +++ /dev/null @@ -1,60 +0,0 @@ -use dep::aztec::prelude::{AztecAddress, EthAddress, FunctionSelector, PrivateContext, Deserialize}; -use dep::aztec::context::{PublicContext, gas::GasOpts}; - -struct Token { - address: AztecAddress, -} - -impl Token { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn transfer_public( - self: Self, - context: &mut PublicContext, - from: AztecAddress, - to: AztecAddress, - amount: Field, - nonce: Field - ) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("transfer_public((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce], - GasOpts::default() - ).assert_empty(); - } - - pub fn shield( - self: Self, - context: &mut PublicContext, - from: AztecAddress, - amount: Field, - secret_hash: Field, - nonce: Field - ) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("shield((Field),Field,Field,Field)"), - [from.to_field(), amount, secret_hash, nonce], - GasOpts::default() - ).assert_empty(); - } - - // Private - pub fn unshield( - self: Self, - context: &mut PrivateContext, - from: AztecAddress, - to: AztecAddress, - amount: Field, - nonce: Field - ) { - context.call_private_function( - self.address, - FunctionSelector::from_signature("unshield((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] - ); - } -} diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index 118fcd71c07..f7636711d0e 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -1,11 +1,9 @@ -mod interfaces; - contract FPC { use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress, traits::is_empty}; use dep::aztec::state_vars::SharedImmutable; - use dep::aztec::prelude::Deserialize; + use dep::token::Token; + use dep::gas_token::GasToken; use dep::aztec::context::gas::GasOpts; - use crate::interfaces::Token; #[aztec(storage)] struct Storage { @@ -23,67 +21,34 @@ contract FPC { #[aztec(private)] fn fee_entrypoint_private(amount: Field, asset: AztecAddress, secret_hash: Field, nonce: Field) { assert(asset == storage.other_asset.read_private()); - - let _res = Token::at(asset).unshield( - &mut context, - context.msg_sender(), - context.this_address(), - amount, - nonce - ); - - let _void = context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("pay_fee_with_shielded_rebate(Field,(Field),Field)"), - [amount, asset.to_field(), secret_hash] - ); + Token::at(asset).unshield(context.msg_sender(), context.this_address(), amount, nonce).call(&mut context); + FPC::at(context.this_address()).pay_fee_with_shielded_rebate(amount, asset, secret_hash).enqueue(&mut context); } #[aztec(private)] fn fee_entrypoint_public(amount: Field, asset: AztecAddress, nonce: Field) { - let _void = context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("prepare_fee((Field),Field,(Field),Field)"), - [context.msg_sender().to_field(), amount, asset.to_field(), nonce] - ); - - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("pay_fee((Field),Field,(Field))"), - [context.msg_sender().to_field(), amount, asset.to_field()] - ); + FPC::at(context.this_address()).prepare_fee(context.msg_sender(), amount, asset, nonce).enqueue(&mut context); + FPC::at(context.this_address()).pay_fee(context.msg_sender(), amount, asset).enqueue(&mut context); } #[aztec(public)] #[aztec(internal)] fn prepare_fee(from: AztecAddress, amount: Field, asset: AztecAddress, nonce: Field) { - let _res = Token::at(asset).transfer_public(&mut context, from, context.this_address(), amount, nonce); + Token::at(asset).transfer_public(from, context.this_address(), amount, nonce).call(&mut context); } #[aztec(public)] #[aztec(internal)] fn pay_fee(refund_address: AztecAddress, amount: Field, asset: AztecAddress) { - let refund: Field = context.call_public_function( - storage.gas_token_address.read_public(), - FunctionSelector::from_signature("pay_fee(Field)"), - [amount], - GasOpts::default() - ).deserialize_into(); - + let refund = GasToken::at(storage.gas_token_address.read_public()).pay_fee(amount).call(&mut context); // Just do public refunds for the present - Token::at(asset).transfer_public(&mut context, context.this_address(), refund_address, refund, 0) + Token::at(asset).transfer_public(context.this_address(), refund_address, refund, 0).call(&mut context); } #[aztec(public)] #[aztec(internal)] fn pay_fee_with_shielded_rebate(amount: Field, asset: AztecAddress, secret_hash: Field) { - let refund: Field = context.call_public_function( - storage.gas_token_address.read_public(), - FunctionSelector::from_signature("pay_fee(Field)"), - [amount], - GasOpts::default() - ).deserialize_into(); - - Token::at(asset).shield(&mut context, context.this_address(), refund, secret_hash, 0); + let refund = GasToken::at(storage.gas_token_address.read_public()).pay_fee(amount).call(&mut context); + Token::at(asset).shield(context.this_address(), refund, secret_hash, 0).call(&mut context); } } diff --git a/noir-projects/noir-contracts/contracts/import_test_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/import_test_contract/Nargo.toml index f4ec2663e2b..fe205fd0f17 100644 --- a/noir-projects/noir-contracts/contracts/import_test_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/import_test_contract/Nargo.toml @@ -6,3 +6,4 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } +test = { path = "../test_contract"} diff --git a/noir-projects/noir-contracts/contracts/import_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/import_test_contract/src/main.nr index 0168119b822..9224c0c2a3e 100644 --- a/noir-projects/noir-contracts/contracts/import_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/import_test_contract/src/main.nr @@ -1,54 +1,44 @@ -mod test_contract_interface; - // Contract that uses the autogenerated interface of the Test contract for calling its functions. // Used for testing calling into other contracts via autogenerated interfaces. contract ImportTest { - use dep::aztec::prelude::{AztecAddress, Deserialize}; + use dep::aztec::prelude::AztecAddress; + + use dep::test::{Test, Test::DeepStruct, Test::DummyNote}; - use crate::test_contract_interface::{ - TestPrivateContextInterface, TestPublicContextInterface, AStructTestCodeGenStruct, - ADeepStructTestCodeGenStruct, ANoteADeepStructTestCodeGenStruct, - ManyNotesADeepStructTestCodeGenStruct - }; + use dep::test::Test::FieldNote; + use dep::test::Test::ValueNote; // Calls the test_code_gen 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 // See yarn-project/end-to-end/src/e2e_nested_contract.test.ts #[aztec(private)] - fn main(target: AztecAddress) -> Field { - let test_contract_instance = TestPrivateContextInterface::at(target); - let returned_field: Field = test_contract_instance.test_code_gen( - &mut context, + fn main_contract(target: AztecAddress) -> Field { + Test::at(target).test_code_gen( 1, true, 1 as u32, [1, 2], - AStructTestCodeGenStruct { amount: 1, secret_hash: 2 }, - ADeepStructTestCodeGenStruct { + DummyNote { amount: 1, secret_hash: 2 }, + DeepStruct { a_field: 1, a_bool: true, - a_note: ANoteADeepStructTestCodeGenStruct { amount: 1, secret_hash: 2 }, + a_note: DummyNote { amount: 1, secret_hash: 2 }, many_notes: [ - ManyNotesADeepStructTestCodeGenStruct { amount: 1, secret_hash: 2 }, - ManyNotesADeepStructTestCodeGenStruct { amount: 1, secret_hash: 2 }, - ManyNotesADeepStructTestCodeGenStruct { amount: 1, secret_hash: 2 } + DummyNote { amount: 1, secret_hash: 2 }, + DummyNote { amount: 1, secret_hash: 2 }, + DummyNote { amount: 1, secret_hash: 2 } ] } - ).unpack_into(); - - returned_field + ).call(&mut context) } // Calls the get_this_address on the Test contract at the target address // Used for testing calling a function with no arguments // See yarn-project/end-to-end/src/e2e_nested_contract.test.ts #[aztec(private)] - fn call_no_args(target: AztecAddress) -> Field { - let test_contract_instance = TestPrivateContextInterface::at(target); - let returned_field: Field = test_contract_instance.get_this_address(&mut context).unpack_into(); - - returned_field + fn call_no_args(target: AztecAddress) -> AztecAddress { + Test::at(target).get_this_address().call(&mut context) } // Calls the create_nullifier_public on the Test contract at the target address @@ -56,8 +46,7 @@ contract ImportTest { // See yarn-project/end-to-end/src/e2e_nested_contract.test.ts #[aztec(private)] fn call_open_fn(target: AztecAddress) { - let test_contract_instance = TestPrivateContextInterface::at(target); - test_contract_instance.create_nullifier_public(&mut context, 1, 2); + Test::at(target).create_nullifier_public(1, 2).enqueue(&mut context); } // Calls the create_nullifier_public on the Test contract at the target address @@ -65,8 +54,7 @@ contract ImportTest { // See yarn-project/end-to-end/src/e2e_nested_contract.test.ts #[aztec(public)] fn pub_call_open_fn(target: AztecAddress) { - let test_contract_instance = TestPublicContextInterface::at(target); - test_contract_instance.create_nullifier_public(&mut context, 1, 2).assert_empty(); + Test::at(target).create_nullifier_public(1, 2).call(&mut context); } } diff --git a/noir-projects/noir-contracts/contracts/import_test_contract/src/test_contract_interface.nr b/noir-projects/noir-contracts/contracts/import_test_contract/src/test_contract_interface.nr deleted file mode 120000 index 1113fbe816e..00000000000 --- a/noir-projects/noir-contracts/contracts/import_test_contract/src/test_contract_interface.nr +++ /dev/null @@ -1 +0,0 @@ -../../test_contract/src/interface.nr \ No newline at end of file diff --git a/noir-projects/noir-contracts/contracts/lending_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/lending_contract/Nargo.toml index e6a337360bc..f1e58a4ef1f 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/lending_contract/Nargo.toml @@ -6,3 +6,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } +token = { path = "../token_contract" } +price_feed = { path = "../price_feed_contract" } diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/asset.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/asset.nr index 5dc8cb7f300..7415ec54d92 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/asset.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/asset.nr @@ -1,7 +1,7 @@ use dep::aztec::prelude::AztecAddress; use dep::aztec::protocol_types::traits::{Deserialize, Serialize}; -// Struct to be used to represent "totals". Generally, there should be one per asset. +// Struct to be used to represent "totals". Generally, there should be one per Asset. // It stores the global values that are shared among all users, such as an accumulator // and last time it was updated. // In practice, it should also point to an oracle and have more fields related to @@ -13,23 +13,23 @@ struct Asset { oracle: AztecAddress, } -global ASSET_SERIALIZED_LEN: Field = 4; +global SERIALIZED_LEN: Field = 4; -impl Serialize for Asset { - fn serialize(asset: Asset) -> [Field; ASSET_SERIALIZED_LEN] { +impl Serialize for Asset { + fn serialize(Asset: Asset) -> [Field; SERIALIZED_LEN] { [ - asset.interest_accumulator.to_integer(), - asset.last_updated_ts as Field, - asset.loan_to_value.to_integer(), - asset.oracle.to_field() + Asset.interest_accumulator.to_integer(), + Asset.last_updated_ts as Field, + Asset.loan_to_value.to_integer(), + Asset.oracle.to_field() ] } } -impl Deserialize for Asset { +impl Deserialize for Asset { // Right now we are wasting so many writes. If changing last_updated_ts // we will end up rewriting all of them, wasting writes. - fn deserialize(fields: [Field; ASSET_SERIALIZED_LEN]) -> Asset { + fn deserialize(fields: [Field; SERIALIZED_LEN]) -> Asset { let interest_accumulator = U128::from_integer(fields[0]); let last_updated_ts = fields[1] as u64; let loan_to_value = U128::from_integer(fields[2]); diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr deleted file mode 100644 index 5a2e31ec79f..00000000000 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr +++ /dev/null @@ -1,117 +0,0 @@ -use dep::aztec::prelude::{FunctionSelector, AztecAddress, PrivateContext, Deserialize}; - -use dep::aztec::context::{PublicContext, gas::GasOpts}; - -use crate::asset::Asset; - -struct PriceFeed { - address: AztecAddress, -} - -impl PriceFeed { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn get_price(self: Self, context: &mut PublicContext) -> U128 { - let price_field: Field = context.call_public_function( - self.address, - FunctionSelector::from_signature("get_price(Field)"), - [0], - GasOpts::default() - ).deserialize_into(); - - U128::from_integer(price_field) - } -} - -struct Token { - address: AztecAddress, -} - -impl Token { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn transfer_public( - self: Self, - context: &mut PublicContext, - from: AztecAddress, - to: AztecAddress, - amount: Field, - nonce: Field - ) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("transfer_public((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce], - GasOpts::default() - ).assert_empty(); - } - - pub fn mint_public(self: Self, context: &mut PublicContext, to: AztecAddress, amount: Field) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("mint_public((Field),Field)"), - [to.to_field(), amount], - GasOpts::default() - ).assert_empty(); - } - - pub fn burn_public( - self: Self, - context: &mut PublicContext, - from: AztecAddress, - amount: Field, - nonce: Field - ) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("burn_public((Field),Field,Field)"), - [from.to_field(), amount, nonce], - GasOpts::default() - ).assert_empty(); - } - - // Private - pub fn unshield( - self: Self, - context: &mut PrivateContext, - from: AztecAddress, - to: AztecAddress, - amount: Field, - nonce: Field - ) { - context.call_private_function( - self.address, - FunctionSelector::from_signature("unshield((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] - ); - } - - pub fn burn(self: Self, context: &mut PrivateContext, from: AztecAddress, amount: Field, nonce: Field) { - context.call_private_function( - self.address, - FunctionSelector::from_signature("burn((Field),Field,Field)"), - [from.to_field(), amount, nonce] - ); - } -} - -struct Lending { - address: AztecAddress, -} - -impl Lending { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn update_accumulator(self: Self, context: &mut PublicContext) -> Asset { - context.call_public_function_no_args( - self.address, - FunctionSelector::from_signature("update_accumulator()") - ).deserialize_into() - } -} diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr index 37727015e25..c2c05f58a09 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr @@ -1,7 +1,6 @@ mod asset; mod interest_math; mod helpers; -mod interfaces; // Single asset CDP contract. // Shoving re-entries up the ass. @@ -17,7 +16,8 @@ contract Lending { use crate::asset::Asset; use crate::interest_math::compute_multiplier; use crate::helpers::{covered_by_collateral, DebtReturn, debt_updates, debt_value, compute_identifier}; - use crate::interfaces::{Token, Lending, PriceFeed}; + use dep::token::Token; + use dep::price_feed::PriceFeed; // Storage structure, containing all storage, and specifying what slots they use. #[aztec(storage)] @@ -49,7 +49,7 @@ contract Lending { stable_coin: AztecAddress ) { let asset_loc = storage.assets.at(0); - let asset = asset_loc.read(); + let asset: Asset = asset_loc.read(); let loan_to_value = U128::from_integer(loan_to_value); @@ -71,7 +71,7 @@ contract Lending { #[aztec(public)] fn update_accumulator() -> Asset { let asset_loc = storage.assets.at(0); - let mut asset = asset_loc.read(); + let mut asset: Asset = asset_loc.read(); let timestamp = context.timestamp(); let dt = timestamp - asset.last_updated_ts; @@ -103,38 +103,28 @@ contract Lending { collateral_asset: AztecAddress ) { let on_behalf_of = compute_identifier(secret, on_behalf_of, context.msg_sender().to_field()); - let _res = Token::at(collateral_asset).unshield(&mut context, from, context.this_address(), amount, nonce); - // _deposit(on_behalf_of, amount, collateral_asset) - let selector = FunctionSelector::from_signature("_deposit((Field),Field,(Field))"); - context.call_public_function( - context.this_address(), - selector, - [on_behalf_of, amount, collateral_asset.to_field()] - ); + let _res = Token::at(collateral_asset).unshield(from, context.this_address(), amount, nonce).call(&mut context); + Lending::at(context.this_address())._deposit( + AztecAddress::from_field(on_behalf_of), + amount, + collateral_asset + ).enqueue(&mut context); } #[aztec(public)] fn deposit_public(amount: Field, nonce: Field, on_behalf_of: Field, collateral_asset: AztecAddress) { - Token::at(collateral_asset).transfer_public( - &mut context, - context.msg_sender(), - context.this_address(), + let _ = Token::at(collateral_asset).transfer_public(context.msg_sender(), context.this_address(), amount, nonce).call(&mut context); + let _ = Lending::at(context.this_address())._deposit( + AztecAddress::from_field(on_behalf_of), amount, - nonce - ); - let selector = FunctionSelector::from_signature("_deposit((Field),Field,(Field))"); - context.call_public_function( - context.this_address(), - selector, - [on_behalf_of, amount, collateral_asset.to_field()], - GasOpts::default() - ).assert_empty(); + collateral_asset + ).call(&mut context); } #[aztec(public)] #[aztec(internal)] fn _deposit(owner: AztecAddress, amount: Field, collateral_asset: AztecAddress) { - let _asset = Lending::at(context.this_address()).update_accumulator(&mut context); + let _asset = Lending::at(context.this_address()).update_accumulator().call(&mut context); let coll_asset = storage.collateral_asset.read(); assert(coll_asset.eq(collateral_asset)); @@ -147,30 +137,19 @@ contract Lending { #[aztec(private)] fn withdraw_private(secret: Field, to: AztecAddress, amount: Field) { let on_behalf_of = compute_identifier(secret, 0, context.msg_sender().to_field()); - let selector = FunctionSelector::from_signature("_withdraw((Field),(Field),Field)"); - context.call_public_function( - context.this_address(), - selector, - [on_behalf_of, to.to_field(), amount] - ); + Lending::at(context.this_address())._withdraw(AztecAddress::from_field(on_behalf_of), to, amount).enqueue(&mut context); } #[aztec(public)] fn withdraw_public(to: AztecAddress, amount: Field) { - let selector = FunctionSelector::from_signature("_withdraw((Field),(Field),Field)"); - context.call_public_function( - context.this_address(), - selector, - [context.msg_sender().to_field(), to.to_field(), amount], - GasOpts::default() - ).assert_empty(); + let _ = Lending::at(context.this_address())._withdraw(context.msg_sender(), to, amount).call(&mut context); } #[aztec(public)] #[aztec(internal)] fn _withdraw(owner: AztecAddress, recipient: AztecAddress, amount: Field) { - let asset = Lending::at(context.this_address()).update_accumulator(&mut context); - let price = PriceFeed::at(asset.oracle).get_price(&mut context); + let asset = Lending::at(context.this_address()).update_accumulator().call(&mut context); + let price = PriceFeed::at(asset.oracle).get_price(0).call(&mut context).price; let coll_loc = storage.collateral.at(owner); let collateral: Field = coll_loc.read(); @@ -200,36 +179,25 @@ contract Lending { // @todo @LHerskind Support both shielding and transfers (for now just transfer) let collateral_asset = storage.collateral_asset.read(); - Token::at(collateral_asset).transfer_public(&mut context, context.this_address(), recipient, amount, 0); + let _ = Token::at(collateral_asset).transfer_public(context.this_address(), recipient, amount, 0).call(&mut context); } #[aztec(private)] fn borrow_private(secret: Field, to: AztecAddress, amount: Field) { let on_behalf_of = compute_identifier(secret, 0, context.msg_sender().to_field()); - let selector = FunctionSelector::from_signature("_borrow((Field),(Field),Field)"); - context.call_public_function( - context.this_address(), - selector, - [on_behalf_of, to.to_field(), amount] - ); + let _ = Lending::at(context.this_address())._borrow(AztecAddress::from_field(on_behalf_of), to, amount).enqueue(&mut context); } #[aztec(public)] fn borrow_public(to: AztecAddress, amount: Field) { - let selector = FunctionSelector::from_signature("_borrow((Field),(Field),Field)"); - context.call_public_function( - context.this_address(), - selector, - [context.msg_sender().to_field(), to.to_field(), amount], - GasOpts::default() - ).assert_empty(); + let _ = Lending::at(context.this_address())._borrow(context.msg_sender(), to, amount).call(&mut context); } #[aztec(public)] #[aztec(internal)] fn _borrow(owner: AztecAddress, to: AztecAddress, amount: Field) { - let asset = Lending::at(context.this_address()).update_accumulator(&mut context); - let price = PriceFeed::at(asset.oracle).get_price(&mut context); + let asset = Lending::at(context.this_address()).update_accumulator().call(&mut context); + let price = PriceFeed::at(asset.oracle).get_price(0).call(&mut context).price; // Fetch collateral and static_debt, compute health of current position let collateral = U128::from_integer(storage.collateral.at(owner).read()); @@ -255,7 +223,7 @@ contract Lending { // @todo @LHerskind Need to support both private and public minting. let stable_coin = storage.stable_coin.read(); - Token::at(stable_coin).mint_public(&mut context, to, amount); + let _ = Token::at(stable_coin).mint_public(to, amount).call(&mut context); } #[aztec(private)] @@ -268,31 +236,20 @@ contract Lending { stable_coin: AztecAddress ) { let on_behalf_of = compute_identifier(secret, on_behalf_of, context.msg_sender().to_field()); - let _res = Token::at(stable_coin).burn(&mut context, from, amount, nonce); - let selector = FunctionSelector::from_signature("_repay((Field),Field,(Field))"); - context.call_public_function( - context.this_address(), - selector, - [on_behalf_of, amount, stable_coin.to_field()] - ); + let _ = Token::at(stable_coin).burn(from, amount, nonce).call(&mut context); + let _ = Lending::at(context.this_address())._repay(AztecAddress::from_field(on_behalf_of), amount, stable_coin).enqueue(&mut context); } #[aztec(public)] fn repay_public(amount: Field, nonce: Field, owner: AztecAddress, stable_coin: AztecAddress) { - Token::at(stable_coin).burn_public(&mut context, context.msg_sender(), amount, nonce); - let selector = FunctionSelector::from_signature("_repay((Field),Field,(Field))"); - context.call_public_function( - context.this_address(), - selector, - [owner.to_field(), amount, stable_coin.to_field()], - GasOpts::default() - ).assert_empty(); + let _ = Token::at(stable_coin).burn_public(context.msg_sender(), amount, nonce).call(&mut context); + let _ = Lending::at(context.this_address())._repay(owner, amount, stable_coin).call(&mut context); } #[aztec(public)] #[aztec(internal)] fn _repay(owner: AztecAddress, amount: Field, stable_coin: AztecAddress) { - let asset = Lending::at(context.this_address()).update_accumulator(&mut context); + let asset = Lending::at(context.this_address()).update_accumulator().call(&mut context); // To ensure that private is using the correct token. assert(stable_coin.eq(storage.stable_coin.read())); @@ -315,7 +272,7 @@ contract Lending { unconstrained fn get_position(owner: AztecAddress) -> pub Position { let collateral = storage.collateral.at(owner).read(); let static_debt = storage.static_debt.at(owner).read(); - let asset = storage.assets.at(0).read(); + let asset: Asset = storage.assets.at(0).read(); let debt = debt_value( U128::from_integer(static_debt), U128::from_integer(asset.interest_accumulator) 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 5dffc5e6493..fb066de5ad6 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -1,8 +1,8 @@ + // A contract used along with `Child` contract to test nested calls. contract Parent { use dep::aztec::prelude::{AztecAddress, FunctionSelector, Deserialize}; use dep::aztec::context::gas::GasOpts; - // Private function to call another private function in the target_contract using the provided selector #[aztec(private)] fn entry_point(target_contract: AztecAddress, target_selector: FunctionSelector) -> Field { @@ -20,7 +20,7 @@ contract Parent { context.call_public_function( target_contract, target_selector, - [init_value], + [init_value].as_slice(), GasOpts::default() ).deserialize_into() } @@ -35,13 +35,13 @@ contract Parent { let return_value: Field = context.call_public_function( target_contract, target_selector, - [init_value], + [init_value].as_slice(), GasOpts::default() ).deserialize_into(); context.call_public_function( target_contract, target_selector, - [return_value], + [return_value].as_slice(), GasOpts::default() ).deserialize_into() } @@ -189,7 +189,12 @@ contract Parent { target_selector: FunctionSelector, args: [Field; 1] ) -> Field { - context.static_call_public_function(target_contract, target_selector, args, GasOpts::default()).deserialize_into() + context.static_call_public_function( + target_contract, + target_selector, + args.as_slice(), + GasOpts::default() + ).deserialize_into() } // Public function to set a static context and verify correct propagation for nested public calls @@ -205,7 +210,7 @@ contract Parent { context.static_call_public_function( this_address, pub_entry_point_selector, - [target_contract.to_field(), target_selector.to_field(), args[0]], + [target_contract.to_field(), target_selector.to_field(), args[0]].as_slice(), GasOpts::default() ).deserialize_into() } diff --git a/noir-projects/noir-contracts/contracts/slow_tree_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/slow_tree_contract/Nargo.toml index 54270ccd97e..2edcc357b5a 100644 --- a/noir-projects/noir-contracts/contracts/slow_tree_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/slow_tree_contract/Nargo.toml @@ -6,5 +6,4 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } -value_note = { path = "../../../aztec-nr/value-note" } slow_updates_tree = { path = "../../../aztec-nr/slow-updates-tree" } diff --git a/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr b/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr index 2fa7b935a4b..f71c2ff62c5 100644 --- a/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/slow_tree_contract/src/main.nr @@ -12,7 +12,6 @@ contract SlowTree { Map, PublicMutable, PrivateSet }; - use dep::value_note::{balance_utils, utils::{increment, decrement}, value_note::ValueNote}; use dep::aztec::{context::{PublicContext, Context}, protocol_types::type_serialization::FIELD_SERIALIZED_LEN}; use dep::slow_updates_tree::{SlowMap, Leaf, SlowUpdateProof, compute_merkle_root, deserialize_slow_update_proof}; 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 3c7ac5c3af0..db8efde4a25 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 @@ -17,8 +17,7 @@ contract StatefulTest { #[aztec(private)] #[aztec(initializer)] fn constructor(owner: AztecAddress, value: Field) { - let selector = FunctionSelector::from_signature("create_note_no_init_check((Field),Field)"); - context.call_private_function(context.this_address(), selector, [owner.to_field(), value]).assert_empty(); + StatefulTest::at(context.this_address()).create_note_no_init_check(owner, value).call(&mut context); } #[aztec(private)] @@ -31,13 +30,7 @@ contract StatefulTest { #[aztec(public)] #[aztec(initializer)] fn public_constructor(owner: AztecAddress, value: Field) { - let selector = FunctionSelector::from_signature("increment_public_value_no_init_check((Field),Field)"); - context.call_public_function( - context.this_address(), - selector, - [owner.to_field(), value], - GasOpts::default() - ).assert_empty(); + StatefulTest::at(context.this_address()).increment_public_value_no_init_check(owner, value).call(&mut context); } #[aztec(private)] diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/interface.nr b/noir-projects/noir-contracts/contracts/test_contract/src/interface.nr deleted file mode 100644 index 71133236902..00000000000 --- a/noir-projects/noir-contracts/contracts/test_contract/src/interface.nr +++ /dev/null @@ -1,675 +0,0 @@ -/* Autogenerated file, do not edit! */ - -use dep::std; -use dep::aztec::context::{ PrivateContext, PublicContext, PackedReturns, FunctionReturns, gas::GasOpts }; -use dep::aztec::protocol_types::{ - address::AztecAddress, - abis::function_selector::FunctionSelector, -}; - -struct AddressGetPublicKeyStruct { - inner: Field, -} - -struct TargetDeployContractStruct { - inner: Field, -} - -struct SenderConsumeMessageFromArbitrarySenderPublicStruct { - inner: Field, -} - -struct ToConsumeMintPublicMessageStruct { - inner: Field, -} - -struct RecipientCreateL2ToL1MessageArbitraryRecipientPrivateStruct { - inner: Field, -} - -struct SenderConsumeMessageFromArbitrarySenderPrivateStruct { - inner: Field, -} - -struct CoinbaseAssertPublicGlobalVarsStruct { - inner: Field, -} - -struct FeeRecipientAssertPublicGlobalVarsStruct { - inner: Field, -} - -struct OwnerCallCreateNoteStruct { - inner: Field, -} - -struct AStructTestCodeGenStruct { - amount: Field, - secret_hash: Field, -} - -struct ADeepStructTestCodeGenStruct { - a_field: Field, - a_bool: bool, - a_note: ANoteADeepStructTestCodeGenStruct, - many_notes: [ManyNotesADeepStructTestCodeGenStruct;3], -} - -struct ANoteADeepStructTestCodeGenStruct { - amount: Field, - secret_hash: Field, -} - -struct ManyNotesADeepStructTestCodeGenStruct { - amount: Field, - secret_hash: Field, -} - -struct RecipientCreateL2ToL1MessageArbitraryRecipientPublicStruct { - inner: Field, -} - -struct AztecAddressGetPortalContractAddressStruct { - inner: Field, -} - - -// Interface for calling Test functions from a private context -struct TestPrivateContextInterface { - address: AztecAddress, -} - -impl TestPrivateContextInterface { - pub fn at(address: AztecAddress) -> Self { - Self { - address, - } - } - - pub fn get_public_key( - self, - context: &mut PrivateContext, - address: AddressGetPublicKeyStruct - ) -> PackedReturns{ - let mut serialized_args = [0; 1]; - serialized_args[0] = address.inner; - - context.call_private_function(self.address, FunctionSelector::from_field(0x501e4f48), serialized_args) - } - - - pub fn assert_header_public( - self, - context: &mut PrivateContext, - header_hash: Field - ) { - let mut serialized_args = [0; 1]; - serialized_args[0] = header_hash; - - context.call_public_function(self.address, FunctionSelector::from_field(0x86e38c61), serialized_args) - } - - - pub fn deploy_contract( - self, - context: &mut PrivateContext, - target: TargetDeployContractStruct - ) -> PackedReturns{ - let mut serialized_args = [0; 1]; - serialized_args[0] = target.inner; - - context.call_private_function(self.address, FunctionSelector::from_field(0x5acec588), serialized_args) - } - - - pub fn get_this_address( - self, - context: &mut PrivateContext - ) -> PackedReturns{ - let mut serialized_args = [0; 0]; - - context.call_private_function(self.address, FunctionSelector::from_field(0x95a7b2ae), serialized_args) - } - - - pub fn emit_msg_sender( - self, - context: &mut PrivateContext - ) -> PackedReturns{ - let mut serialized_args = [0; 0]; - - context.call_private_function(self.address, FunctionSelector::from_field(0x11fb5d45), serialized_args) - } - - - pub fn consume_message_from_arbitrary_sender_public( - self, - context: &mut PrivateContext, - content: Field, - secret: Field, - sender: SenderConsumeMessageFromArbitrarySenderPublicStruct - ) { - let mut serialized_args = [0; 3]; - serialized_args[0] = content; - serialized_args[1] = secret; - serialized_args[2] = sender.inner; - - context.call_public_function(self.address, FunctionSelector::from_field(0x42ca6d60), serialized_args) - } - - - pub fn emit_unencrypted( - self, - context: &mut PrivateContext, - value: Field - ) { - let mut serialized_args = [0; 1]; - serialized_args[0] = value; - - context.call_public_function(self.address, FunctionSelector::from_field(0x817a64cb), serialized_args) - } - - - pub fn consume_mint_public_message( - self, - context: &mut PrivateContext, - to: ToConsumeMintPublicMessageStruct, - amount: Field, - secret: Field - ) { - let mut serialized_args = [0; 3]; - serialized_args[0] = to.inner; - serialized_args[1] = amount; - serialized_args[2] = secret; - - context.call_public_function(self.address, FunctionSelector::from_field(0xa0f84219), serialized_args) - } - - - pub fn create_nullifier_public( - self, - context: &mut PrivateContext, - amount: Field, - secret_hash: Field - ) { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = secret_hash; - - context.call_public_function(self.address, FunctionSelector::from_field(0xdf02db8d), serialized_args) - } - - - pub fn call_get_notes_many( - self, - context: &mut PrivateContext, - storage_slot: Field, - active_or_nullified: bool - ) -> PackedReturns{ - let mut serialized_args = [0; 2]; - serialized_args[0] = storage_slot; - serialized_args[1] = active_or_nullified as Field; - - context.call_private_function(self.address, FunctionSelector::from_field(0xcfcadbce), serialized_args) - } - - - pub fn create_l2_to_l1_message_arbitrary_recipient_private( - self, - context: &mut PrivateContext, - content: Field, - recipient: RecipientCreateL2ToL1MessageArbitraryRecipientPrivateStruct - ) -> PackedReturns{ - let mut serialized_args = [0; 2]; - serialized_args[0] = content; - serialized_args[1] = recipient.inner; - - context.call_private_function(self.address, FunctionSelector::from_field(0xaccc5d5d), serialized_args) - } - - - pub fn consume_message_from_arbitrary_sender_private( - self, - context: &mut PrivateContext, - content: Field, - secret: Field, - sender: SenderConsumeMessageFromArbitrarySenderPrivateStruct - ) -> PackedReturns{ - let mut serialized_args = [0; 3]; - serialized_args[0] = content; - serialized_args[1] = secret; - serialized_args[2] = sender.inner; - - context.call_private_function(self.address, FunctionSelector::from_field(0x2847cb26), serialized_args) - } - - - pub fn create_l2_to_l1_message_public( - self, - context: &mut PrivateContext, - amount: Field, - secret_hash: Field - ) { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = secret_hash; - - context.call_public_function(self.address, FunctionSelector::from_field(0x9749ca06), serialized_args) - } - - - pub fn is_time_equal( - self, - context: &mut PrivateContext, - time: u64 - ) { - let mut serialized_args = [0; 1]; - serialized_args[0] = time as Field; - - context.call_public_function(self.address, FunctionSelector::from_field(0xb5bb17fa), serialized_args) - } - - - pub fn assert_private_global_vars( - self, - context: &mut PrivateContext, - chain_id: Field, - version: Field - ) -> PackedReturns{ - let mut serialized_args = [0; 2]; - serialized_args[0] = chain_id; - serialized_args[1] = version; - - context.call_private_function(self.address, FunctionSelector::from_field(0x7a8e9b66), serialized_args) - } - - - pub fn assert_public_global_vars( - self, - context: &mut PrivateContext, - chain_id: Field, - version: Field, - block_number: Field, - timestamp: u64, - coinbase: CoinbaseAssertPublicGlobalVarsStruct, - fee_recipient: FeeRecipientAssertPublicGlobalVarsStruct, - fee_per_da_gas: Field, - fee_per_l1_gas: Field, - fee_per_l2_gas: Field - ) { - let mut serialized_args = [0; 9]; - serialized_args[0] = chain_id; - serialized_args[1] = version; - serialized_args[2] = block_number; - serialized_args[3] = timestamp as Field; - serialized_args[4] = coinbase.inner; - serialized_args[5] = fee_recipient.inner; - serialized_args[6] = fee_per_da_gas; - serialized_args[7] = fee_per_l1_gas; - serialized_args[8] = fee_per_l2_gas; - - context.call_public_function(self.address, FunctionSelector::from_field(0x33b3a245), serialized_args) - } - - - pub fn consume_note_from_secret( - self, - context: &mut PrivateContext, - secret: Field - ) -> PackedReturns{ - let mut serialized_args = [0; 1]; - serialized_args[0] = secret; - - context.call_private_function(self.address, FunctionSelector::from_field(0x754932c8), serialized_args) - } - - - pub fn call_destroy_note( - self, - context: &mut PrivateContext, - storage_slot: Field - ) -> PackedReturns{ - let mut serialized_args = [0; 1]; - serialized_args[0] = storage_slot; - - context.call_private_function(self.address, FunctionSelector::from_field(0xf52a62f7), serialized_args) - } - - - pub fn call_create_note( - self, - context: &mut PrivateContext, - value: Field, - owner: OwnerCallCreateNoteStruct, - storage_slot: Field - ) -> PackedReturns{ - let mut serialized_args = [0; 3]; - serialized_args[0] = value; - serialized_args[1] = owner.inner; - serialized_args[2] = storage_slot; - - context.call_private_function(self.address, FunctionSelector::from_field(0x946991ff), serialized_args) - } - - - pub fn get_this_portal_address( - self, - context: &mut PrivateContext - ) -> PackedReturns{ - let mut serialized_args = [0; 0]; - - context.call_private_function(self.address, FunctionSelector::from_field(0xc71384f5), serialized_args) - } - - - pub fn call_get_notes( - self, - context: &mut PrivateContext, - storage_slot: Field, - active_or_nullified: bool - ) -> PackedReturns{ - let mut serialized_args = [0; 2]; - serialized_args[0] = storage_slot; - serialized_args[1] = active_or_nullified as Field; - - context.call_private_function(self.address, FunctionSelector::from_field(0x11eeb3ea), serialized_args) - } - - - pub fn test_code_gen( - self, - context: &mut PrivateContext, - a_field: Field, - a_bool: bool, - a_number: u32, - an_array: [Field;2], - a_struct: AStructTestCodeGenStruct, - a_deep_struct: ADeepStructTestCodeGenStruct - ) -> PackedReturns{ - let mut serialized_args = [0; 17]; - serialized_args[0] = a_field; - serialized_args[1] = a_bool as Field; - serialized_args[2] = a_number as Field; - serialized_args[3] = an_array[0]; - serialized_args[4] = an_array[1]; - serialized_args[5] = a_struct.amount; - serialized_args[6] = a_struct.secret_hash; - serialized_args[7] = a_deep_struct.a_field; - serialized_args[8] = a_deep_struct.a_bool as Field; - serialized_args[9] = a_deep_struct.a_note.amount; - serialized_args[10] = a_deep_struct.a_note.secret_hash; - serialized_args[11] = a_deep_struct.many_notes[0].amount; - serialized_args[12] = a_deep_struct.many_notes[0].secret_hash; - serialized_args[13] = a_deep_struct.many_notes[1].amount; - serialized_args[14] = a_deep_struct.many_notes[1].secret_hash; - serialized_args[15] = a_deep_struct.many_notes[2].amount; - serialized_args[16] = a_deep_struct.many_notes[2].secret_hash; - - context.call_private_function(self.address, FunctionSelector::from_field(0x0f054f9b), serialized_args) - } - - - pub fn set_constant( - self, - context: &mut PrivateContext, - value: Field - ) -> PackedReturns{ - let mut serialized_args = [0; 1]; - serialized_args[0] = value; - - context.call_private_function(self.address, FunctionSelector::from_field(0x1b3b9e18), serialized_args) - } - - - pub fn consume_mint_private_message( - self, - context: &mut PrivateContext, - secret_hash_for_redeeming_minted_notes: Field, - amount: Field, - secret_for_L1_to_L2_message_consumption: Field - ) -> PackedReturns{ - let mut serialized_args = [0; 3]; - serialized_args[0] = secret_hash_for_redeeming_minted_notes; - serialized_args[1] = amount; - serialized_args[2] = secret_for_L1_to_L2_message_consumption; - - context.call_private_function(self.address, FunctionSelector::from_field(0xa0fdbaa9), serialized_args) - } - - - pub fn create_l2_to_l1_message_arbitrary_recipient_public( - self, - context: &mut PrivateContext, - content: Field, - recipient: RecipientCreateL2ToL1MessageArbitraryRecipientPublicStruct - ) { - let mut serialized_args = [0; 2]; - serialized_args[0] = content; - serialized_args[1] = recipient.inner; - - context.call_public_function(self.address, FunctionSelector::from_field(0x2fb25188), serialized_args) - } - - - pub fn emit_nullifier( - self, - context: &mut PrivateContext, - nullifier: Field - ) -> PackedReturns{ - let mut serialized_args = [0; 1]; - serialized_args[0] = nullifier; - - context.call_private_function(self.address, FunctionSelector::from_field(0x82a8b183), serialized_args) - } - - - pub fn emit_array_as_unencrypted_log( - self, - context: &mut PrivateContext, - fields: [Field;5] - ) -> PackedReturns{ - let mut serialized_args = [0; 5]; - serialized_args[0] = fields[0]; - serialized_args[1] = fields[1]; - serialized_args[2] = fields[2]; - serialized_args[3] = fields[3]; - serialized_args[4] = fields[4]; - - context.call_private_function(self.address, FunctionSelector::from_field(0xe25cbdd3), serialized_args) - } - - - pub fn get_portal_contract_address( - self, - context: &mut PrivateContext, - aztec_address: AztecAddressGetPortalContractAddressStruct - ) -> PackedReturns{ - let mut serialized_args = [0; 1]; - serialized_args[0] = aztec_address.inner; - - context.call_private_function(self.address, FunctionSelector::from_field(0x30e5344b), serialized_args) - } - - - pub fn request_max_block_number( - self, - context: &mut PrivateContext, - max_block_number: u32, - enqueue_public_call: bool - ) -> PackedReturns{ - let mut serialized_args = [0; 2]; - serialized_args[0] = max_block_number as Field; - serialized_args[1] = enqueue_public_call as Field; - - context.call_private_function(self.address, FunctionSelector::from_field(0x6db24b2e), serialized_args) - } - - - pub fn assert_header_private( - self, - context: &mut PrivateContext, - header_hash: Field - ) -> PackedReturns{ - let mut serialized_args = [0; 1]; - serialized_args[0] = header_hash; - - context.call_private_function(self.address, FunctionSelector::from_field(0x4e45eb9e), serialized_args) - } - -} - - - - -// Interface for calling Test functions from a public context -struct TestPublicContextInterface { - address: AztecAddress, -} - -impl TestPublicContextInterface { - pub fn at(address: AztecAddress) -> Self { - Self { - address, - } - } - - pub fn assert_header_public( - self, - context: &mut PublicContext, - header_hash: Field - ) -> FunctionReturns { - let mut serialized_args = [0; 1]; - serialized_args[0] = header_hash; - - context.call_public_function(self.address, FunctionSelector::from_field(0x86e38c61), serialized_args, GasOpts::default()) - } - - - pub fn consume_message_from_arbitrary_sender_public( - self, - context: &mut PublicContext, - content: Field, - secret: Field, - sender: SenderConsumeMessageFromArbitrarySenderPublicStruct - ) -> FunctionReturns { - let mut serialized_args = [0; 3]; - serialized_args[0] = content; - serialized_args[1] = secret; - serialized_args[2] = sender.inner; - - context.call_public_function(self.address, FunctionSelector::from_field(0x42ca6d60), serialized_args, GasOpts::default()) - } - - - pub fn emit_unencrypted( - self, - context: &mut PublicContext, - value: Field - ) -> FunctionReturns { - let mut serialized_args = [0; 1]; - serialized_args[0] = value; - - context.call_public_function(self.address, FunctionSelector::from_field(0x817a64cb), serialized_args, GasOpts::default()) - } - - - pub fn consume_mint_public_message( - self, - context: &mut PublicContext, - to: ToConsumeMintPublicMessageStruct, - amount: Field, - secret: Field - ) -> FunctionReturns { - let mut serialized_args = [0; 3]; - serialized_args[0] = to.inner; - serialized_args[1] = amount; - serialized_args[2] = secret; - - context.call_public_function(self.address, FunctionSelector::from_field(0xa0f84219), serialized_args, GasOpts::default()) - } - - - pub fn create_nullifier_public( - self, - context: &mut PublicContext, - amount: Field, - secret_hash: Field - ) -> FunctionReturns { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = secret_hash; - - context.call_public_function(self.address, FunctionSelector::from_field(0xdf02db8d), serialized_args, GasOpts::default()) - } - - - pub fn create_l2_to_l1_message_public( - self, - context: &mut PublicContext, - amount: Field, - secret_hash: Field - ) -> FunctionReturns { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = secret_hash; - - context.call_public_function(self.address, FunctionSelector::from_field(0x9749ca06), serialized_args, GasOpts::default()) - } - - - pub fn is_time_equal( - self, - context: &mut PublicContext, - time: u64 - ) -> FunctionReturns { - let mut serialized_args = [0; 1]; - serialized_args[0] = time as Field; - - context.call_public_function(self.address, FunctionSelector::from_field(0xb5bb17fa), serialized_args, GasOpts::default()) - } - - - pub fn assert_public_global_vars( - self, - context: &mut PublicContext, - chain_id: Field, - version: Field, - block_number: Field, - timestamp: u64, - coinbase: CoinbaseAssertPublicGlobalVarsStruct, - fee_recipient: FeeRecipientAssertPublicGlobalVarsStruct, - fee_per_da_gas: Field, - fee_per_l1_gas: Field, - fee_per_l2_gas: Field - ) -> FunctionReturns { - let mut serialized_args = [0; 9]; - serialized_args[0] = chain_id; - serialized_args[1] = version; - serialized_args[2] = block_number; - serialized_args[3] = timestamp as Field; - serialized_args[4] = coinbase.inner; - serialized_args[5] = fee_recipient.inner; - serialized_args[6] = fee_per_da_gas; - serialized_args[7] = fee_per_l1_gas; - serialized_args[8] = fee_per_l2_gas; - - context.call_public_function(self.address, FunctionSelector::from_field(0x33b3a245), serialized_args, GasOpts::default()) - } - - - pub fn create_l2_to_l1_message_arbitrary_recipient_public( - self, - context: &mut PublicContext, - content: Field, - recipient: RecipientCreateL2ToL1MessageArbitraryRecipientPublicStruct - ) -> FunctionReturns { - let mut serialized_args = [0; 2]; - serialized_args[0] = content; - serialized_args[1] = recipient.inner; - - context.call_public_function(self.address, FunctionSelector::from_field(0x2fb25188), serialized_args, GasOpts::default()) - } - -} - - 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 663cafa4860..535e96dc43a 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -7,7 +7,7 @@ contract Test { use dep::aztec::protocol_types::{ abis::private_circuit_public_inputs::PrivateCircuitPublicInputs, - constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NOTES_PER_PAGE} + constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NOTES_PER_PAGE}, traits::Serialize }; // docs:start:unencrypted_import use dep::aztec::prelude::emit_unencrypted_log; @@ -72,11 +72,7 @@ contract Test { context.set_tx_max_block_number(max_block_number); if enqueue_public_call { - let _ = context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("dummy_public_call()"), - [] - ); + Test::at(context.this_address()).dummy_public_call().enqueue(&mut context) } } @@ -179,18 +175,15 @@ contract Test { destroy_note(&mut context, note); } - // Test codegen for Aztec.nr interfaces - // See yarn-project/simulator/src/client/private_execution.test.ts 'nested calls through autogenerated interface' - // Note; this function is deliberately NOT annotated with #[aztec(private)] due to its use in tests + #[aztec(private)] fn test_code_gen( - inputs: PrivateContextInputs, a_field: Field, a_bool: bool, a_number: u32, an_array: [Field; 2], a_struct: DummyNote, a_deep_struct: DeepStruct - ) -> distinct pub PrivateCircuitPublicInputs { + ) -> Field { let mut args = ArgsHasher::new(); args.add(a_field); args.add(a_bool as Field); @@ -206,12 +199,7 @@ contract Test { args.add(note.amount); args.add(note.secret_hash); } - let args_hash = args.hash(); - let mut context = PrivateContext::new(inputs, args_hash); - let mut returns = ArgsHasher::new(); - returns.add(args_hash); - context.set_return_hash(returns); - context.finish() + args.hash() } // Purely exists for testing @@ -408,10 +396,36 @@ contract Test { } } + impl Serialize<2> for DummyNote { + fn serialize(self) -> [Field; 2] { + [self.amount, self.secret_hash] + } + } + struct DeepStruct { a_field: Field, a_bool: bool, a_note: DummyNote, many_notes: [DummyNote; 3], } + + // Serializing using "canonical" form. + // 1. Everything that fits in a field, *becomes* a Field + // 2. Strings become arrays of bytes (no strings here) + // 4. Arrays become arrays of Fields following rules 2 and 3 (no arrays here) + // 5. Structs become arrays of Fields, with every item defined in the same order as they are in Noir code, following rules 2, 3, 4 and 5 (recursive) + impl Serialize<10> for DeepStruct { + fn serialize(self) -> [Field; 10] { + let mut result = [0; 10]; + result[0] = self.a_field; + result[1] = self.a_bool as Field; + result[2] = self.a_note.amount; + result[3] = self.a_note.secret_hash; + for i in 0..3 { + result[4 + i * 2] = self.many_notes[i].amount; + result[5 + i * 2] = self.many_notes[i].secret_hash; + } + result + } + } } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/token_blacklist_contract/Nargo.toml index 493c9159332..54bc420b804 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/Nargo.toml @@ -8,3 +8,4 @@ type = "contract" aztec = { path = "../../../aztec-nr/aztec" } field_note = { path = "../../../aztec-nr/field-note" } authwit = { path = "../../../aztec-nr/authwit" } +slow_tree = { path = "../slow_tree_contract" } \ No newline at end of file diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/interfaces.nr deleted file mode 100644 index 601d9ba65f0..00000000000 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/interfaces.nr +++ /dev/null @@ -1,45 +0,0 @@ -use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}; -use dep::aztec::prelude::Deserialize; -use dep::aztec::context::{PrivateContext, PublicContext, Context, gas::GasOpts}; - -struct SlowMap { - address: AztecAddress, -} - -impl SlowMap { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn initialize(self: Self, context: &mut PublicContext) { - context.call_public_function_no_args( - self.address, - FunctionSelector::from_signature("initialize()") - ).assert_empty(); - } - - pub fn read_at_pub(self: Self, context: &mut PublicContext, index: Field) -> Field { - context.call_public_function( - self.address, - FunctionSelector::from_signature("read_at_pub(Field)"), - [index], - GasOpts::default() - ).deserialize_into() - } - - pub fn read_at(self: Self, context: &mut PrivateContext, index: Field) -> Field { - context.call_private_function( - self.address, - FunctionSelector::from_signature("read_at(Field)"), - [index] - ).unpack_into() - } - - pub fn update_at_private(self: Self, context: &mut PrivateContext, index: Field, new_value: Field) { - let _ = context.call_private_function( - self.address, - FunctionSelector::from_signature("update_at_private(Field,Field)"), - [index, new_value] - ); - } -} diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr index cfad6717cc9..ddb115c721e 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr @@ -1,6 +1,4 @@ mod types; -mod interfaces; - // Minimal token implementation that supports `AuthWit` accounts and the slow update tree. // The auth message follows a similar pattern to the cross-chain message and includes a designated caller. // The designated caller is ALWAYS used here, and not based on a flag as cross-chain. @@ -27,7 +25,7 @@ contract TokenBlacklist { use crate::types::{transparent_note::TransparentNote, token_note::TokenNote, balances_map::BalancesMap, roles::UserFlags}; // docs:start:interface - use crate::interfaces::SlowMap; + use dep::slow_tree::SlowTree; // docs:end:interface #[aztec(storage)] @@ -51,25 +49,20 @@ contract TokenBlacklist { storage.slow_update.initialize(slow_updates_contract); // docs:end:write_slow_update_public // docs:start:slowmap_initialize - SlowMap::at(slow_updates_contract).initialize(&mut context); + SlowTree::at(slow_updates_contract).initialize().call(&mut context); // docs:end:slowmap_initialize // We cannot do the following atm // let roles = UserFlags { is_admin: true, is_minter: false, is_blacklisted: false }.get_value().to_field(); - // SlowMap::at(slow_updates_contract).update_at_private(&mut context, admin.to_field(), roles); + // SlowTree::at(slow_updates_contract).update_at_private(&mut context, admin.to_field(), roles); } #[aztec(private)] fn init_slow_tree(user: AztecAddress) { let roles = UserFlags { is_admin: true, is_minter: false, is_blacklisted: false }.get_value().to_field(); // docs:start:get_and_update_private - let slow = SlowMap::at(storage.slow_update.read_private()); - slow.update_at_private(&mut context, user.to_field(), roles); + SlowTree::at(storage.slow_update.read_private()).update_at_private(user.to_field(), roles).call(&mut context); // docs:end:get_and_update_private - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("_init_slow_tree((Field))"), - [context.msg_sender().to_field()] - ); + TokenBlacklist::at(context.this_address())._init_slow_tree(context.msg_sender()).enqueue(&mut context); } #[aztec(public)] @@ -81,25 +74,27 @@ contract TokenBlacklist { #[aztec(private)] fn update_roles(user: AztecAddress, roles: Field) { // docs:start:slowmap_at - let slow = SlowMap::at(storage.slow_update.read_private()); + let slow = SlowTree::at(storage.slow_update.read_private()); // docs:end:slowmap_at - let caller_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, context.msg_sender().to_field()))); + let role = slow.read_at(context.msg_sender().to_field()).call(&mut context); + + let caller_roles = UserFlags::new(U128::from_integer(role)); assert(caller_roles.is_admin, "caller is not admin"); - slow.update_at_private(&mut context, user.to_field(), roles); + slow.update_at_private(user.to_field(), roles).call(&mut context); } #[aztec(public)] fn mint_public(to: AztecAddress, amount: Field) { // docs:start:get_public - let slow = SlowMap::at(storage.slow_update.read_public()); + let slow = SlowTree::at(storage.slow_update.read_public()); // docs:end:get_public // docs:start:read_at_pub - let to_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(&mut context, to.to_field()))); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(to.to_field()).call(&mut context))); // docs:end:read_at_pub assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); - let caller_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(&mut context, context.msg_sender().to_field()))); + let caller_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context.msg_sender().to_field()).call(&mut context))); assert(caller_roles.is_minter, "caller is not minter"); let amount = U128::from_integer(amount); @@ -112,8 +107,8 @@ contract TokenBlacklist { #[aztec(public)] fn mint_private(amount: Field, secret_hash: Field) { - let slow = SlowMap::at(storage.slow_update.read_public()); - let caller_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(&mut context, context.msg_sender().to_field()))); + let slow = SlowTree::at(storage.slow_update.read_public()); + let caller_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context.msg_sender().to_field()).call(&mut context))); assert(caller_roles.is_minter, "caller is not minter"); let pending_shields = storage.pending_shields; @@ -126,8 +121,8 @@ contract TokenBlacklist { #[aztec(public)] fn shield(from: AztecAddress, amount: Field, secret_hash: Field, nonce: Field) { - let slow = SlowMap::at(storage.slow_update.read_public()); - let from_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(&mut context, from.to_field()))); + let slow = SlowTree::at(storage.slow_update.read_public()); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(from.to_field()).call(&mut context))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); if (!from.eq(context.msg_sender())) { @@ -149,10 +144,10 @@ contract TokenBlacklist { #[aztec(public)] fn transfer_public(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { - let slow = SlowMap::at(storage.slow_update.read_public()); - let from_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(&mut context, from.to_field()))); + let slow = SlowTree::at(storage.slow_update.read_public()); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(from.to_field()).call(&mut context))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); - let to_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(&mut context, to.to_field()))); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(to.to_field()).call(&mut context))); assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); if (!from.eq(context.msg_sender())) { @@ -171,8 +166,8 @@ contract TokenBlacklist { #[aztec(public)] fn burn_public(from: AztecAddress, amount: Field, nonce: Field) { - let slow = SlowMap::at(storage.slow_update.read_public()); - let from_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(&mut context, from.to_field()))); + let slow = SlowTree::at(storage.slow_update.read_public()); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(from.to_field()).call(&mut context))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); if (!from.eq(context.msg_sender())) { @@ -191,9 +186,9 @@ contract TokenBlacklist { #[aztec(private)] fn redeem_shield(to: AztecAddress, amount: Field, secret: Field) { - let slow = SlowMap::at(storage.slow_update.read_private()); + let slow = SlowTree::at(storage.slow_update.read_private()); // docs:start:slowmap_read_at - let to_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, to.to_field()))); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at(to.to_field()).call(&mut context))); // docs:end:slowmap_read_at assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); @@ -218,10 +213,10 @@ contract TokenBlacklist { #[aztec(private)] fn unshield(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { - let slow = SlowMap::at(storage.slow_update.read_private()); - let from_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, from.to_field()))); + let slow = SlowTree::at(storage.slow_update.read_private()); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at(from.to_field()).call(&mut context))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); - let to_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, to.to_field()))); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at(to.to_field()).call(&mut context))); assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); if (!from.eq(context.msg_sender())) { @@ -239,10 +234,10 @@ contract TokenBlacklist { // docs:start:transfer_private #[aztec(private)] fn transfer(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { - let slow = SlowMap::at(storage.slow_update.read_private()); - let from_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, from.to_field()))); + let slow = SlowTree::at(storage.slow_update.read_private()); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at(from.to_field()).call(&mut context))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); - let to_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, to.to_field()))); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at(to.to_field()).call(&mut context))); assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); // docs:end:transfer_private @@ -259,8 +254,8 @@ contract TokenBlacklist { #[aztec(private)] fn burn(from: AztecAddress, amount: Field, nonce: Field) { - let slow = SlowMap::at(storage.slow_update.read_private()); - let from_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, from.to_field()))); + let slow = SlowTree::at(storage.slow_update.read_private()); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at(from.to_field()).call(&mut context))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); if (!from.eq(context.msg_sender())) { diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/token_bridge_contract/Nargo.toml index da41973739d..75c729d9c50 100644 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/token_bridge_contract/Nargo.toml @@ -7,3 +7,4 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } token_portal_content_hash_lib = { path = "../token_portal_content_hash_lib" } +token = { path = "../token_contract" } diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr index 673e4b8aa7f..c5814ed81ce 100644 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr @@ -1,5 +1,4 @@ // docs:start:token_bridge_imports -mod token_interface; // Minimal implementation of the token bridge that can move funds between L1 <> L2. // The bridge has a corresponding Portal contract on L1 that it is attached to @@ -13,7 +12,7 @@ contract TokenBridge { use dep::token_portal_content_hash_lib::{get_mint_public_content_hash, get_mint_private_content_hash, get_withdraw_content_hash}; - use crate::token_interface::Token; + use dep::token::Token; // docs:end:token_bridge_imports // docs:start:token_bridge_storage_and_constructor @@ -42,7 +41,7 @@ contract TokenBridge { context.consume_l1_to_l2_message(content_hash, secret, context.this_portal_address()); // Mint tokens - Token::at(storage.token.read()).mint_public(&mut context, to, amount); + Token::at(storage.token.read()).mint_public(to, amount).call(&mut context); } // docs:end:claim_public @@ -61,7 +60,7 @@ contract TokenBridge { context.message_portal(context.this_portal_address(), content); // Burn tokens - Token::at(storage.token.read()).burn_public(&mut context, context.msg_sender(), amount, nonce); + Token::at(storage.token.read()).burn_public(context.msg_sender(), amount, nonce).call(&mut context); } // docs:end:exit_to_l1_public // docs:start:claim_private @@ -118,7 +117,7 @@ contract TokenBridge { // docs:end:call_assert_token_is_same // Burn tokens - Token::at(token).burn(&mut context, context.msg_sender(), amount, nonce); + Token::at(token).burn(context.msg_sender(), amount, nonce).call(&mut context); } /// docs:end:exit_to_l1_private @@ -151,7 +150,7 @@ contract TokenBridge { #[aztec(public)] #[aztec(internal)] fn _call_mint_on_token(amount: Field, secret_hash: Field) { - Token::at(storage.token.read()).mint_private(&mut context, amount, secret_hash); + Token::at(storage.token.read()).mint_private(amount, secret_hash).call(&mut context); } // docs:end:call_mint_on_token diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr deleted file mode 100644 index 8cebb78da04..00000000000 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr +++ /dev/null @@ -1,59 +0,0 @@ -// docs:start:token_bridge_token_interface -use dep::aztec::prelude::{FunctionSelector, AztecAddress, EthAddress, PrivateContext}; -use dep::aztec::context::{PublicContext, Context, gas::GasOpts}; - -struct Token { - address: AztecAddress, -} - -impl Token { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn mint_public(self: Self, context: &mut PublicContext, to: AztecAddress, amount: Field) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("mint_public((Field),Field)"), - [to.to_field(), amount], - GasOpts::default() - ).assert_empty(); - } - - // docs:start:public_burn_interface - pub fn burn_public( - self: Self, - context: &mut PublicContext, - from: AztecAddress, - amount: Field, - nonce: Field - ) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("burn_public((Field),Field,Field)"), - [from.to_field(), amount, nonce], - GasOpts::default() - ).assert_empty(); - } - // docs:end:public_burn_interface - - pub fn mint_private(self: Self, context: &mut PublicContext, amount: Field, secret_hash: Field) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("mint_private(Field,Field)"), - [amount, secret_hash], - GasOpts::default() - ).assert_empty(); - } - - // docs:start:private_burn_interface - pub fn burn(self: Self, context: &mut PrivateContext, from: AztecAddress, amount: Field, nonce: Field) { - let _return_values = context.call_private_function( - self.address, - FunctionSelector::from_signature("burn((Field),Field,Field)"), - [from.to_field(), amount, nonce] - ); - } - // docs:end:private_burn_interface -} -// docs:end:token_bridge_token_interface diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 7924299b27c..7978fe76412 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -168,12 +168,7 @@ contract Token { #[aztec(private)] fn privately_mint_private_note(amount: Field) { storage.balances.add(context.msg_sender(), U128::from_integer(amount)); - let selector = FunctionSelector::from_signature("assert_minter_and_mint((Field),Field)"); - let _void = context.call_public_function( - context.this_address(), - selector, - [context.msg_sender().to_field(), amount] - ); + Token::at(context.this_address()).assert_minter_and_mint(context.msg_sender(), amount).enqueue(&mut context); } #[aztec(public)] @@ -277,8 +272,7 @@ contract Token { storage.balances.sub(from, U128::from_integer(amount)); - let selector = FunctionSelector::from_signature("_increase_public_balance((Field),Field)"); - let _void = context.call_public_function(context.this_address(), selector, [to.to_field(), amount]); + Token::at(context.this_address())._increase_public_balance(to, amount).enqueue(&mut context); } // docs:end:unshield @@ -312,8 +306,7 @@ contract Token { storage.balances.sub(from, U128::from_integer(amount)); - let selector = FunctionSelector::from_signature("_reduce_total_supply(Field)"); - let _void = context.call_public_function(context.this_address(), selector, [amount]); + Token::at(context.this_address())._reduce_total_supply(amount).enqueue(&mut context); } // docs:end:burn diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/uniswap_contract/Nargo.toml index cabd23e359e..dbc2b0cf06d 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/Nargo.toml @@ -7,3 +7,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } authwit = { path = "../../../aztec-nr/authwit" } +token = { path = "../token_contract" } +token_bridge = { path = "../token_bridge_contract" } \ No newline at end of file diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/interfaces.nr deleted file mode 100644 index cd68492fabb..00000000000 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/interfaces.nr +++ /dev/null @@ -1,80 +0,0 @@ -// docs:start:interfaces -use dep::aztec::prelude::{FunctionSelector, AztecAddress, EthAddress, PrivateContext, Deserialize}; -use dep::aztec::context::{PublicContext, gas::GasOpts}; - -struct Token { - address: AztecAddress, -} - -impl Token { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn transfer_public( - self: Self, - context: &mut PublicContext, - from: AztecAddress, - to: AztecAddress, - amount: Field, - nonce: Field - ) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("transfer_public((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce], - GasOpts::default() - ).assert_empty(); - } - - pub fn unshield( - self: Self, - context: &mut PrivateContext, - from: AztecAddress, - to: AztecAddress, - amount: Field, - nonce: Field - ) { - let _ret = context.call_private_function( - self.address, - FunctionSelector::from_signature("unshield((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] - ); - } -} - -struct TokenBridge { - address: AztecAddress, -} - -impl TokenBridge { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn token(self: Self, context: &mut PublicContext) -> AztecAddress { - context.call_public_function( - self.address, - FunctionSelector::from_signature("get_token()"), - [], - GasOpts::default() - ).deserialize_into() - } - - pub fn exit_to_l1_public( - self: Self, - context: &mut PublicContext, - recipient: EthAddress, - amount: Field, - caller_on_l1: EthAddress, - nonce: Field - ) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("exit_to_l1_public((Field),Field,(Field),Field)"), - [recipient.to_field(), amount, caller_on_l1.to_field(), nonce], - GasOpts::default() - ).assert_empty(); - } -} -// docs:end:interfaces diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr index a595616248e..32a89fcc704 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -1,5 +1,4 @@ // docs:start:uniswap_setup -mod interfaces; mod util; // Demonstrates how to use portal contracts to swap on L1 Uniswap with funds on L2 @@ -15,7 +14,8 @@ contract Uniswap { compute_outer_authwit_hash }; - use crate::interfaces::{Token, TokenBridge}; + use dep::token::Token; + use dep::token_bridge::TokenBridge; use crate::util::{compute_swap_private_content_hash, compute_swap_public_content_hash}; #[aztec(storage)] @@ -51,25 +51,18 @@ contract Uniswap { assert_current_call_valid_authwit_public(&mut context, sender); } - let input_asset = TokenBridge::at(input_asset_bridge).token(&mut context); + let input_asset = TokenBridge::at(input_asset_bridge).get_token().call(&mut context); // Transfer funds to this contract Token::at(input_asset).transfer_public( - &mut context, sender, context.this_address(), input_amount, nonce_for_transfer_approval - ); + ).call(&mut context); // Approve bridge to burn this contract's funds and exit to L1 Uniswap Portal - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("_approve_bridge_and_exit_input_asset_to_L1((Field),(Field),Field)"), - [input_asset.to_field(), input_asset_bridge.to_field(), input_amount], - GasOpts::default() - ).assert_empty(); - + Uniswap::at(context.this_address())._approve_bridge_and_exit_input_asset_to_L1(input_asset, input_asset_bridge, input_amount).call(&mut context); // Create swap message and send to Outbox for Uniswap Portal // this ensures the integrity of what the user originally intends to do on L1. let input_asset_bridge_portal_address = get_portal_address(input_asset_bridge); @@ -115,27 +108,18 @@ contract Uniswap { ) { // Assert that user provided token address is same as expected by token bridge. // we can't directly use `input_asset_bridge.token` because that is a public method and public can't return data to private - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("_assert_token_is_same((Field),(Field))"), - [input_asset.to_field(), input_asset_bridge.to_field()] - ); + Uniswap::at(context.this_address())._assert_token_is_same(input_asset, input_asset_bridge).enqueue(&mut context); // Transfer funds to this contract Token::at(input_asset).unshield( - &mut context, context.msg_sender(), context.this_address(), input_amount, nonce_for_unshield_approval - ); + ).call(&mut context); // Approve bridge to burn this contract's funds and exit to L1 Uniswap Portal - context.call_public_function( - context.this_address(), - FunctionSelector::from_signature("_approve_bridge_and_exit_input_asset_to_L1((Field),(Field),Field)"), - [input_asset.to_field(), input_asset_bridge.to_field(), input_amount] - ); + Uniswap::at(context.this_address())._approve_bridge_and_exit_input_asset_to_L1(input_asset, input_asset_bridge, input_amount).enqueue(&mut context); // Create swap message and send to Outbox for Uniswap Portal // this ensures the integrity of what the user originally intends to do on L1. @@ -193,7 +177,11 @@ contract Uniswap { // this method is used for both private and public swaps. #[aztec(public)] #[aztec(internal)] - fn _approve_bridge_and_exit_input_asset_to_L1(token: AztecAddress, token_bridge: AztecAddress, amount: Field) { + fn _approve_bridge_and_exit_input_asset_to_L1( + token: AztecAddress, + token_bridge: AztecAddress, + amount: Field + ) { // approve bridge to burn this contract's funds (required when exiting on L1, as it burns funds on L2): let nonce_for_burn_approval = storage.nonce_for_burn_approval.read(); let selector = FunctionSelector::from_signature("burn_public((Field),Field,Field)"); @@ -212,12 +200,11 @@ contract Uniswap { // Exit to L1 Uniswap Portal ! TokenBridge::at(token_bridge).exit_to_l1_public( - &mut context, context.this_portal_address(), amount, context.this_portal_address(), nonce_for_burn_approval - ); + ).call(&mut context) } // docs:end:authwit_uniswap_set @@ -226,7 +213,7 @@ contract Uniswap { #[aztec(internal)] fn _assert_token_is_same(token: AztecAddress, token_bridge: AztecAddress) { assert( - token.eq(TokenBridge::at(token_bridge).token(&mut context)), "input_asset address is not the same as seen in the bridge contract" + token.eq(TokenBridge::at(token_bridge).get_token().call(&mut context)), "input_asset address is not the same as seen in the bridge contract" ); } // docs:end:assert_token_is_same diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr index 70870f64d84..4d272c90cba 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr @@ -1,4 +1,5 @@ use dep::std::cmp::Eq; +use crate::utils::field::field_from_bytes; // Trait: is_empty // @@ -55,6 +56,12 @@ impl ToField for U128 { self.to_integer() } } +impl ToField for str { + fn to_field(self) -> Field { + assert(N < 32, "String doesn't fit in a field, consider using Serialize instead"); + field_from_bytes(self.as_bytes(), true) + } +} trait FromField { fn from_field(value: Field) -> Self; @@ -83,6 +90,17 @@ trait Serialize { } // docs:end:serialize +impl Serialize for str { + fn serialize(self) -> [Field; N] { + let mut result = [0; N]; + let bytes: [u8; N] = self.as_bytes(); + for i in 0..N { + result[i] = field_from_bytes([bytes[i];1], true); + } + result + } +} + // docs:start:deserialize trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index 48c30ab6ffa..dff3193a327 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -3,6 +3,9 @@ mod utils; use transforms::{ compute_note_hash_and_nullifier::inject_compute_note_hash_and_nullifier, + contract_interface::{ + generate_contract_interface, stub_function, update_fn_signatures_in_contract_interface, + }, events::{generate_selector_impl, transform_events}, functions::{export_fn_abi, transform_function, transform_unconstrained}, note_interface::{generate_note_interface_impl, inject_note_exports}, @@ -59,7 +62,14 @@ fn transform( // Usage -> mut ast -> aztec_library::transform(&mut ast) // Covers all functions in the ast for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { - if transform_module(&mut submodule.contents).map_err(|err| (err.into(), file_id))? { + if transform_module( + crate_id, + context, + &mut submodule.contents, + submodule.name.0.contents.as_str(), + ) + .map_err(|err| (err.into(), file_id))? + { check_for_aztec_dependency(crate_id, context)?; } } @@ -72,7 +82,12 @@ fn transform( /// Determines if ast nodes are annotated with aztec attributes. /// For annotated functions it calls the `transform` function which will perform the required transformations. /// Returns true if an annotated node is found, false otherwise -fn transform_module(module: &mut SortedModule) -> Result { +fn transform_module( + crate_id: &CrateId, + context: &HirContext, + module: &mut SortedModule, + module_name: &str, +) -> Result { let mut has_transformed_module = false; // Check for a user defined storage struct @@ -84,7 +99,12 @@ fn transform_module(module: &mut SortedModule) -> Result if !check_for_storage_implementation(module, &storage_struct_name) { generate_storage_implementation(module, &storage_struct_name)?; } - generate_storage_layout(module, storage_struct_name)?; + // Make sure we're only generating the storage layout for the root crate + // In case we got a contract importing other contracts for their interface, we + // don't want to generate the storage layout for them + if crate_id == context.root_crate_id() { + generate_storage_layout(module, storage_struct_name)?; + } } for structure in module.types.iter_mut() { @@ -102,6 +122,8 @@ fn transform_module(module: &mut SortedModule) -> Result .any(|attr| is_custom_attribute(attr, "aztec(initializer)")) }); + let mut stubs: Vec<_> = vec![]; + for func in module.functions.iter_mut() { let mut is_private = false; let mut is_public = false; @@ -129,15 +151,18 @@ fn transform_module(module: &mut SortedModule) -> Result // Apply transformations to the function based on collected attributes if is_private || is_public || is_public_vm { + let fn_type = if is_private { + "Private" + } else if is_public_vm { + "Avm" + } else { + "Public" + }; + stubs.push(stub_function(fn_type, func)); + export_fn_abi(&mut module.types, func)?; transform_function( - if is_private { - "Private" - } else if is_public_vm { - "Avm" - } else { - "Public" - }, + fn_type, func, storage_defined, is_initializer, @@ -171,6 +196,8 @@ fn transform_module(module: &mut SortedModule) -> Result span: Span::default(), }); } + + generate_contract_interface(module, module_name, &stubs)?; } Ok(has_transformed_module) @@ -189,7 +216,8 @@ fn transform_hir( transform_events(crate_id, context)?; inject_compute_note_hash_and_nullifier(crate_id, context)?; assign_storage_slots(crate_id, context)?; - inject_note_exports(crate_id, context) + inject_note_exports(crate_id, context)?; + update_fn_signatures_in_contract_interface(crate_id, context) } else { Ok(()) } diff --git a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs index 1b6630935d9..4ff97a5dcae 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs @@ -49,20 +49,20 @@ pub fn inject_compute_note_hash_and_nullifier( crate_id: &CrateId, context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { - if let Some((module_id, file_id)) = get_contract_module_data(context, crate_id) { + if let Some((_, module_id, file_id)) = get_contract_module_data(context, crate_id) { // If compute_note_hash_and_nullifier is already defined by the user, we skip auto-generation in order to provide an // escape hatch for this mechanism. // TODO(#4647): improve this diagnosis and error messaging. - if check_for_compute_note_hash_and_nullifier_definition(crate_id, context) { + if context.crate_graph.root_crate_id() != crate_id + || check_for_compute_note_hash_and_nullifier_definition(crate_id, context) + { return Ok(()); } // In order to implement compute_note_hash_and_nullifier, we need to know all of the different note types the // contract might use. These are the types that are marked as #[aztec(note)]. - let note_types = fetch_notes(context) - .iter() - .map(|(_, note)| note.borrow().name.0.contents.clone()) - .collect::>(); + let note_types = + fetch_notes(context).iter().map(|(path, _)| path.to_string()).collect::>(); // We can now generate a version of compute_note_hash_and_nullifier tailored for the contract in this crate. let func = generate_compute_note_hash_and_nullifier(¬e_types); @@ -73,7 +73,14 @@ pub fn inject_compute_note_hash_and_nullifier( // pass an empty span. This function should not produce errors anyway so this should not matter. let location = Location::new(Span::empty(0), file_id); - inject_fn(crate_id, context, func, location, module_id, file_id); + inject_fn(crate_id, context, func, location, module_id, file_id).map_err(|err| { + ( + AztecMacroError::CouldNotImplementComputeNoteHashAndNullifier { + secondary_message: err.secondary_message, + }, + file_id, + ) + })?; } Ok(()) } @@ -100,7 +107,7 @@ fn generate_compute_note_hash_and_nullifier_source(note_types: &[String]) -> Str // so we include a dummy version. " unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, + contract_address: dep::aztec::protocol_types::address::AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field, @@ -130,7 +137,7 @@ fn generate_compute_note_hash_and_nullifier_source(note_types: &[String]) -> Str format!( " unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, + contract_address: dep::aztec::protocol_types::address::AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field, diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs new file mode 100644 index 00000000000..4401c867df9 --- /dev/null +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -0,0 +1,326 @@ +use noirc_frontend::{ + graph::CrateId, + macros_api::{FileId, HirContext, HirExpression, HirLiteral, HirStatement}, + parse_program, + parser::SortedModule, + NoirFunction, Type, UnresolvedTypeData, +}; + +use crate::utils::{ + constants::SELECTOR_PLACEHOLDER, + errors::AztecMacroError, + hir_utils::{collect_crate_structs, get_contract_module_data, signature_of_type}, +}; + +// Generates the stubs for contract functions as low level calls using CallInterface, turning +// #[aztec(public)] // also private +// fn a_function(first_arg: Field, second_arg: Struct, third_arg: [Field; 4]) -> Field { +// ... +// } +// +// into +// +// pub fn a_function(self, first_arg: Field, second_arg: Struct, third_arg: [Field; 4]) -> PublicCallInterface { +// let mut args_acc: [Field] = &[]; +// args_acc = args_acc.append(first_arg.serialize().as_slice()); +// args_acc = args_acc.append(second_arg.serialize().as_slice()); +// let hash_third_arg = third_arg.map(|x: Field| x.serialize()); +// for i in 0..third_arg.len() { +// args_acc = args_acc.append(third_arg[i].serialize().as_slice()); +// } +// let args_hash = dep::aztec::hash::hash_args(args_acc); +// assert(args_hash == dep::aztec::oracle::arguments::pack_arguments(args_acc)); +// PublicCallInterface { +// target_contract: self.target_contract, +// selector: FunctionSelector::from_signature("SELECTOR_PLACEHOLDER"), +// args_hash +// } +// } +// +// The selector placeholder has to be replaced with the actual function signature after type checking in the next macro pass +pub fn stub_function(aztec_visibility: &str, func: &NoirFunction) -> String { + let fn_name = func.name().to_string(); + let fn_parameters = func + .parameters() + .iter() + .map(|param| { + format!( + "{}: {}", + param.pattern.name_ident().0.contents, + param.typ.to_string().replace("plain::", "") + ) + }) + .collect::>() + .join(", "); + let fn_return_type: noirc_frontend::UnresolvedType = func.return_type(); + + let fn_selector = format!("dep::aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature(\"{}\")", SELECTOR_PLACEHOLDER); + + let parameters = func.parameters(); + let is_void = if matches!(fn_return_type.typ, UnresolvedTypeData::Unit) { "Void" } else { "" }; + let return_type_hint = if is_void == "Void" { + "".to_string() + } else { + format!("<{}>", fn_return_type.typ.to_string().replace("plain::", "")) + }; + let call_args = parameters + .iter() + .map(|arg| { + let param_name = arg.pattern.name_ident().0.contents.clone(); + match &arg.typ.typ { + UnresolvedTypeData::Array(_, typ) => { + format!( + "let hash_{0} = {0}.map(|x: {1}| x.serialize()); + for i in 0..{0}.len() {{ + args_acc = args_acc.append(hash_{0}[i].as_slice()); + }}\n", + param_name, typ.typ + ) + } + _ => { + format!("args_acc = args_acc.append({}.serialize().as_slice());\n", param_name) + } + } + }) + .collect::>() + .join(""); + if aztec_visibility != "Avm" { + let args_hash = if !parameters.is_empty() { + format!( + "let mut args_acc: [Field] = &[]; + {} + let args_hash = dep::aztec::hash::hash_args(args_acc); + assert(args_hash == dep::aztec::oracle::arguments::pack_arguments(args_acc));", + call_args + ) + } else { + "let args_hash = 0;".to_string() + }; + + let fn_body = format!( + "{} + dep::aztec::context::{}{}CallInterface {{ + target_contract: self.target_contract, + selector: {}, + args_hash, + }}", + args_hash, aztec_visibility, is_void, fn_selector, + ); + format!( + "pub fn {}(self, {}) -> dep::aztec::context::{}{}CallInterface{} {{ + {} + }}", + fn_name, fn_parameters, aztec_visibility, is_void, return_type_hint, fn_body + ) + } else { + let args = format!( + "let mut args_acc: [Field] = &[]; + {} + ", + call_args + ); + let fn_body = format!( + "{} + dep::aztec::context::Avm{}CallInterface {{ + target_contract: self.target_contract, + selector: {}, + args: args_acc, + }}", + args, is_void, fn_selector, + ); + format!( + "pub fn {}(self, {}) -> dep::aztec::context::Avm{}CallInterface{} {{ + {} + }}", + fn_name, fn_parameters, is_void, return_type_hint, fn_body + ) + } +} + +// Generates the contract interface as a struct with an `at` function that holds the stubbed functions and provides +// them with a target contract address. The struct has the same name as the contract (which is technically a module) +// so imports look nice. The `at` function is also exposed as a contract library method for external use. +pub fn generate_contract_interface( + module: &mut SortedModule, + module_name: &str, + stubs: &[String], +) -> Result<(), AztecMacroError> { + let contract_interface = format!( + " + struct {0} {{ + target_contract: dep::aztec::protocol_types::address::AztecAddress + }} + + impl {0} {{ + {1} + + pub fn at( + target_contract: dep::aztec::protocol_types::address::AztecAddress + ) -> Self {{ + Self {{ target_contract }} + }} + }} + + #[contract_library_method] + pub fn at( + target_contract: dep::aztec::protocol_types::address::AztecAddress + ) -> {0} {{ + {0} {{ target_contract }} + }} + ", + module_name, + stubs.join("\n"), + ); + + let (contract_interface_ast, errors) = parse_program(&contract_interface); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotGenerateContractInterface { secondary_message: Some("Failed to parse Noir macro code during contract interface generation. This is either a bug in the compiler or the Noir macro code".to_string()), }); + } + + let mut contract_interface_ast = contract_interface_ast.into_sorted(); + module.types.push(contract_interface_ast.types.pop().unwrap()); + module.impls.push(contract_interface_ast.impls.pop().unwrap()); + module.functions.push(contract_interface_ast.functions.pop().unwrap()); + + Ok(()) +} + +fn compute_fn_signature(fn_name: &str, parameters: &[Type]) -> String { + format!( + "{}({})", + fn_name, + parameters.iter().map(signature_of_type).collect::>().join(",") + ) +} + +// Updates the function signatures in the contract interface with the actual ones, replacing the placeholder. +// This is done by locating the contract interface struct, its functions (stubs) and assuming the last statement of each +// is the constructor for a CallInterface. This constructor has a selector field that holds a +// FunctionSelector::from_signature function that receives the signature as a string literal. +pub fn update_fn_signatures_in_contract_interface( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { + if let Some((name, _, file_id)) = get_contract_module_data(context, crate_id) { + let maybe_interface_struct = + collect_crate_structs(crate_id, context).iter().find_map(|struct_id| { + let r#struct = context.def_interner.get_struct(*struct_id); + if r#struct.borrow().name.0.contents == name { + Some(r#struct) + } else { + None + } + }); + + if let Some(interface_struct) = maybe_interface_struct { + let methods = context.def_interner.get_struct_methods(interface_struct.borrow().id); + + for func_id in methods.iter().flat_map(|methods| methods.direct.iter()) { + let name = context.def_interner.function_name(func_id); + let fn_parameters = &context.def_interner.function_meta(func_id).parameters.clone(); + + if name == "at" { + continue; + } + + let fn_signature = compute_fn_signature( + name, + &fn_parameters + .iter() + .skip(1) + .map(|(_, typ, _)| typ.clone()) + .collect::>(), + ); + let hir_func = context.def_interner.function(func_id).block(&context.def_interner); + let call_interface_constructor_statement = context.def_interner.statement( + hir_func + .statements() + .last() + .ok_or((AztecMacroError::AztecDepNotFound, file_id))?, + ); + let call_interface_constructor_expression = + match call_interface_constructor_statement { + HirStatement::Expression(expression_id) => { + match context.def_interner.expression(&expression_id) { + HirExpression::Constructor(hir_constructor_expression) => { + Ok(hir_constructor_expression) + } + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "CallInterface constructor statement must be a constructor expression" + .to_string(), + ), + }, + file_id, + )), + } + } + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "CallInterface constructor statement must be an expression" + .to_string(), + ), + }, + file_id, + )), + }?; + let (_, function_selector_expression_id) = + call_interface_constructor_expression.fields[1]; + let function_selector_expression = + context.def_interner.expression(&function_selector_expression_id); + + let current_fn_signature_expression_id = match function_selector_expression { + HirExpression::Call(call_expr) => Ok(call_expr.arguments[0]), + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "Function selector argument expression must be call expression" + .to_string(), + ), + }, + file_id, + )), + }?; + + let current_fn_signature_expression = + context.def_interner.expression(¤t_fn_signature_expression_id); + + match current_fn_signature_expression { + HirExpression::Literal(HirLiteral::Str(signature)) => { + if signature != SELECTOR_PLACEHOLDER { + Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some(format!( + "Function signature argument must be a placeholder: {}", + SELECTOR_PLACEHOLDER + )), + }, + file_id, + )) + } else { + Ok(()) + } + } + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Function signature argument must be a literal string".to_string(), + ), + }, + file_id, + )), + }?; + + context + .def_interner + .update_expression(current_fn_signature_expression_id, |expr| { + *expr = HirExpression::Literal(HirLiteral::Str(fn_signature)) + }); + } + } + } + Ok(()) +} diff --git a/noir/noir-repo/aztec_macros/src/transforms/events.rs b/noir/noir-repo/aztec_macros/src/transforms/events.rs index 4f2b70453df..b77a5821b81 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/events.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/events.rs @@ -174,7 +174,7 @@ pub fn transform_events( crate_id: &CrateId, context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { - for (_, struct_id) in collect_crate_structs(crate_id, context) { + for struct_id in collect_crate_structs(crate_id, context) { let attributes = context.def_interner.struct_attributes(&struct_id); if attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) { transform_event(struct_id, &mut context.def_interner)?; diff --git a/noir/noir-repo/aztec_macros/src/transforms/mod.rs b/noir/noir-repo/aztec_macros/src/transforms/mod.rs index 5a454c75148..2a6fef7647f 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/mod.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/mod.rs @@ -1,4 +1,5 @@ pub mod compute_note_hash_and_nullifier; +pub mod contract_interface; pub mod events; pub mod functions; pub mod note_interface; 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 0514155824e..4b72759a5db 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -528,12 +528,12 @@ fn generate_note_properties_fn_source( .join(", "); format!( " - pub fn properties() -> {}Properties {{ - {}Properties {{ - {} + pub fn properties() -> {0}Properties {{ + {0}Properties {{ + {1} }} }}", - note_type, note_type, note_property_selectors + note_type, note_property_selectors ) .to_string() } @@ -623,7 +623,7 @@ pub fn inject_note_exports( crate_id: &CrateId, context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { - if let Some((module_id, file_id)) = get_contract_module_data(context, crate_id) { + if let Some((_, module_id, file_id)) = get_contract_module_data(context, crate_id) { let notes = fetch_notes(context); for (_, note) in notes { diff --git a/noir/noir-repo/aztec_macros/src/transforms/storage.rs b/noir/noir-repo/aztec_macros/src/transforms/storage.rs index 0bfb39cbc71..9135be32443 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/storage.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/storage.rs @@ -265,13 +265,13 @@ pub fn assign_storage_slots( context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { let traits: Vec<_> = collect_traits(context); - if let Some((_, file_id)) = get_contract_module_data(context, crate_id) { + if let Some((_, _, file_id)) = get_contract_module_data(context, crate_id) { let maybe_storage_struct = - collect_crate_structs(crate_id, context).iter().find_map(|&(_, struct_id)| { - let r#struct = context.def_interner.get_struct(struct_id); - let attributes = context.def_interner.struct_attributes(&struct_id); + collect_crate_structs(crate_id, context).iter().find_map(|struct_id| { + let r#struct = context.def_interner.get_struct(*struct_id); + let attributes = context.def_interner.struct_attributes(struct_id); if attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(storage)")) - && r#struct.borrow().id.krate().is_root() + && r#struct.borrow().id.krate() == *crate_id { Some(r#struct) } else { @@ -290,7 +290,11 @@ pub fn assign_storage_slots( let expr = context.def_interner.expression(&statement.unwrap().expression); match expr { HirExpression::Constructor(hir_constructor_expression) => { - Some(hir_constructor_expression) + if hir_constructor_expression.r#type.borrow().id.krate() == *crate_id { + Some(hir_constructor_expression) + } else { + None + } } _ => None, } diff --git a/noir/noir-repo/aztec_macros/src/utils/constants.rs b/noir/noir-repo/aztec_macros/src/utils/constants.rs index 464cd10e2c7..848cca0477d 100644 --- a/noir/noir-repo/aztec_macros/src/utils/constants.rs +++ b/noir/noir-repo/aztec_macros/src/utils/constants.rs @@ -1,3 +1,4 @@ pub const FUNCTION_TREE_HEIGHT: u32 = 5; pub const MAX_CONTRACT_PRIVATE_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); pub const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; +pub const SELECTOR_PLACEHOLDER: &str = "SELECTOR_PLACEHOLDER"; diff --git a/noir/noir-repo/aztec_macros/src/utils/errors.rs b/noir/noir-repo/aztec_macros/src/utils/errors.rs index 52ee5587559..4c5411dfe0f 100644 --- a/noir/noir-repo/aztec_macros/src/utils/errors.rs +++ b/noir/noir-repo/aztec_macros/src/utils/errors.rs @@ -11,10 +11,12 @@ pub enum AztecMacroError { UnsupportedFunctionReturnType { span: Span, typ: UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, + CouldNotImplementComputeNoteHashAndNullifier { secondary_message: Option }, CouldNotImplementNoteInterface { span: Option, secondary_message: Option }, MultipleStorageDefinitions { span: Option }, CouldNotExportStorageLayout { span: Option, secondary_message: Option }, CouldNotExportFunctionAbi { span: Option, secondary_message: Option }, + CouldNotGenerateContractInterface { secondary_message: Option }, EventError { span: Span, message: String }, UnsupportedAttributes { span: Span, secondary_message: Option }, } @@ -52,6 +54,11 @@ impl From for MacroError { secondary_message, span: None, }, + AztecMacroError::CouldNotImplementComputeNoteHashAndNullifier { secondary_message } => MacroError { + primary_message: "Could not implement compute_note_hash_and_nullifier automatically, please provide an implementation".to_string(), + secondary_message, + span: None, + }, AztecMacroError::CouldNotImplementNoteInterface { span, secondary_message } => MacroError { primary_message: "Could not implement automatic methods for note, please provide an implementation of the NoteInterface trait".to_string(), secondary_message, @@ -72,6 +79,11 @@ impl From for MacroError { secondary_message, span, }, + AztecMacroError::CouldNotGenerateContractInterface { secondary_message } => MacroError { + primary_message: "Could not generate contract interface".to_string(), + secondary_message, + span: None + }, AztecMacroError::EventError { span, message } => MacroError { primary_message: message, secondary_message: None, diff --git a/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs b/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs index c4414e6419b..ae895d2075c 100644 --- a/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs @@ -7,43 +7,32 @@ use noirc_frontend::{ resolution::{path_resolver::StandardPathResolver, resolver::Resolver}, type_check::type_check_func, }, - macros_api::{FileId, HirContext, ModuleDefId, StructId}, + macros_api::{FileId, HirContext, MacroError, ModuleDefId, StructId}, node_interner::{FuncId, TraitId}, ItemVisibility, LetStatement, NoirFunction, Shared, Signedness, StructType, Type, }; use super::ast_utils::is_custom_attribute; -pub fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec<(String, StructId)> { +pub fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec { context .def_map(crate_id) - .expect("ICE: Missing crate in def_map") - .modules() - .iter() - .flat_map(|(_, module)| { - module.type_definitions().filter_map(move |typ| { - if let ModuleDefId::TypeId(struct_id) = typ { - let module_id = struct_id.module_id(); - let path = - context.fully_qualified_struct_path(context.root_crate_id(), struct_id); - let path = if path.contains("::") { - let prefix = if &module_id.krate == context.root_crate_id() { - "crate" + .map(|def_map| { + def_map + .modules() + .iter() + .flat_map(|(_, module)| { + module.type_definitions().filter_map(move |typ| { + if let ModuleDefId::TypeId(struct_id) = typ { + Some(struct_id) } else { - "dep" - }; - format!("{}::{}", prefix, path) - } else { - path - }; - - Some((path, struct_id)) - } else { - None - } - }) + None + } + }) + }) + .collect() }) - .collect() + .unwrap_or_default() } pub fn collect_crate_functions(crate_id: &CrateId, context: &HirContext) -> Vec { @@ -96,22 +85,48 @@ pub fn signature_of_type(typ: &Type) -> String { let fields = vecmap(types, signature_of_type); format!("({})", fields.join(",")) } + Type::String(len_typ) => { + if let Type::Constant(len) = **len_typ { + format!("str<{len}>") + } else { + unimplemented!( + "Cannot generate signature for string with length type {:?}", + len_typ + ) + } + } + Type::MutableReference(typ) => signature_of_type(typ), _ => unimplemented!("Cannot generate signature for type {:?}", typ), } } -// Fetches the name of all structs tagged as #[aztec(note)] in a given crate +// Fetches the name of all structs tagged as #[aztec(note)] in a given crate, avoiding +// contract dependencies that are just there for their interfaces. pub fn fetch_crate_notes( context: &HirContext, crate_id: &CrateId, ) -> Vec<(String, Shared)> { collect_crate_structs(crate_id, context) .iter() - .filter_map(|(path, struct_id)| { + .filter_map(|struct_id| { let r#struct = context.def_interner.get_struct(*struct_id); let attributes = context.def_interner.struct_attributes(struct_id); if attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(note)")) { - Some((path.clone(), r#struct)) + let module_id = struct_id.module_id(); + + fully_qualified_note_path(context, *struct_id).map(|path| { + let path = if path.contains("::") { + let prefix = if &module_id.krate == context.root_crate_id() { + "crate" + } else { + "dep" + }; + format!("{}::{}", prefix, path) + } else { + path + }; + (path.clone(), r#struct) + }) } else { None } @@ -127,23 +142,24 @@ pub fn fetch_notes(context: &HirContext) -> Vec<(String, Shared)> { pub fn get_contract_module_data( context: &mut HirContext, crate_id: &CrateId, -) -> Option<(LocalModuleId, FileId)> { +) -> Option<(String, LocalModuleId, FileId)> { + let def_map = context.def_map(crate_id).expect("ICE: Missing crate in def_map"); // We first fetch modules in this crate which correspond to contracts, along with their file id. - let contract_module_file_ids: Vec<(LocalModuleId, FileId)> = context - .def_map(crate_id) - .expect("ICE: Missing crate in def_map") + let contract_module_file_ids: Vec<(String, LocalModuleId, FileId)> = def_map .modules() .iter() .filter(|(_, module)| module.is_contract) - .map(|(idx, module)| (LocalModuleId(idx), module.location.file)) + .map(|(idx, module)| { + (def_map.get_module_path(idx, module.parent), LocalModuleId(idx), module.location.file) + }) .collect(); - // If the current crate does not contain a contract module we simply skip it. More than 1 contract in a crate is forbidden by the compiler + // If the current crate does not contain a contract module we simply skip it. if contract_module_file_ids.is_empty() { return None; } - Some(contract_module_file_ids[0]) + Some(contract_module_file_ids[0].clone()) } pub fn inject_fn( @@ -153,7 +169,7 @@ pub fn inject_fn( location: Location, module_id: LocalModuleId, file_id: FileId, -) { +) -> Result<(), MacroError> { let func_id = context.def_interner.push_empty_fn(); context.def_interner.push_function( func_id, @@ -164,12 +180,11 @@ pub fn inject_fn( context.def_map_mut(crate_id).unwrap().modules_mut()[module_id.0] .declare_function(func.name_ident().clone(), ItemVisibility::Public, func_id) - .unwrap_or_else(|_| { - panic!( - "Failed to declare autogenerated {} function, likely due to a duplicate definition", - func.name() - ) - }); + .map_err(|err| MacroError { + primary_message: format!("Failed to declare autogenerated {} function", func.name()), + secondary_message: Some(format!("Duplicate definition found {}", err.0)), + span: None, + })?; let def_maps = &mut context.def_maps; @@ -183,7 +198,17 @@ pub fn inject_fn( context.def_interner.push_fn_meta(meta, func_id); context.def_interner.update_fn(func_id, hir_func); - type_check_func(&mut context.def_interner, func_id); + let errors = type_check_func(&mut context.def_interner, func_id); + + if !errors.is_empty() { + return Err(MacroError { + primary_message: "Failed to type check autogenerated function".to_owned(), + secondary_message: Some(errors.iter().map(|err| err.to_string()).collect::()), + span: None, + }); + } + + Ok(()) } pub fn inject_global( @@ -224,3 +249,63 @@ pub fn inject_global( let statement_id = context.def_interner.get_global(global_id).let_statement; context.def_interner.replace_statement(statement_id, hir_stmt); } + +pub fn fully_qualified_note_path(context: &HirContext, note_id: StructId) -> Option { + let module_id = note_id.module_id(); + let child_id = module_id.local_id.0; + let def_map = + context.def_map(&module_id.krate).expect("The local crate should be analyzed already"); + + let module = context.module(module_id); + + let module_path = def_map.get_module_path_with_separator(child_id, module.parent, "::"); + + if &module_id.krate == context.root_crate_id() { + Some(module_path) + } else { + find_non_contract_dependencies_bfs(context, context.root_crate_id(), &module_id.krate) + .map(|crates| crates.join("::") + "::" + &module_path) + } +} + +fn filter_contract_modules(context: &HirContext, crate_id: &CrateId) -> bool { + if let Some(def_map) = context.def_map(crate_id) { + !def_map.modules().iter().any(|(_, module)| module.is_contract) + } else { + true + } +} + +fn find_non_contract_dependencies_bfs( + context: &HirContext, + crate_id: &CrateId, + target_crate_id: &CrateId, +) -> Option> { + context.crate_graph[crate_id] + .dependencies + .iter() + .filter(|dep| filter_contract_modules(context, &dep.crate_id)) + .find_map(|dep| { + if &dep.crate_id == target_crate_id { + Some(vec![dep.name.to_string()]) + } else { + None + } + }) + .or_else(|| { + context.crate_graph[crate_id] + .dependencies + .iter() + .filter(|dep| filter_contract_modules(context, &dep.crate_id)) + .find_map(|dep| { + if let Some(mut path) = + find_non_contract_dependencies_bfs(context, &dep.crate_id, target_crate_id) + { + path.insert(0, dep.name.to_string()); + Some(path) + } else { + None + } + }) + }) +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs index 727a6596df1..29be7524659 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs @@ -157,7 +157,8 @@ impl Context<'_, '_> { } } - /// Recursively walks down the crate dependency graph from crate_id until we reach requested crate + /// Tries to find the requested crate in the current one's dependencies, + /// otherwise walks down the crate dependency graph from crate_id until we reach it. /// This is needed in case a library (lib1) re-export a structure defined in another library (lib2) /// In that case, we will get [lib1,lib2] when looking for a struct defined in lib2, /// re-exported by lib1 and used by the main crate. @@ -167,16 +168,26 @@ impl Context<'_, '_> { crate_id: &CrateId, target_crate_id: &CrateId, ) -> Option> { - for dep in &self.crate_graph[crate_id].dependencies { - if &dep.crate_id == target_crate_id { - return Some(vec![dep.name.to_string()]); - } - if let Some(mut path) = self.find_dependencies(&dep.crate_id, target_crate_id) { - path.insert(0, dep.name.to_string()); - return Some(path); - } - } - None + self.crate_graph[crate_id] + .dependencies + .iter() + .find_map(|dep| { + if &dep.crate_id == target_crate_id { + Some(vec![dep.name.to_string()]) + } else { + None + } + }) + .or_else(|| { + self.crate_graph[crate_id].dependencies.iter().find_map(|dep| { + if let Some(mut path) = self.find_dependencies(&dep.crate_id, target_crate_id) { + path.insert(0, dep.name.to_string()); + Some(path) + } else { + None + } + }) + }) } pub fn function_meta(&self, func_id: &FuncId) -> &FuncMeta { @@ -241,7 +252,7 @@ impl Context<'_, '_> { .get_all_contracts(&self.def_interner) } - fn module(&self, module_id: def_map::ModuleId) -> &def_map::ModuleData { + pub fn module(&self, module_id: def_map::ModuleId) -> &def_map::ModuleData { module_id.module(&self.def_maps) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs index ffd760d6d7f..c9eef286bbd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -223,10 +223,10 @@ pub enum TraitImplKind { /// /// Additionally, types can define specialized impls with methods of the same name /// as long as these specialized impls do not overlap. E.g. `impl Struct` and `impl Struct` -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] pub struct Methods { - direct: Vec, - trait_impl_methods: Vec, + pub direct: Vec, + pub trait_impl_methods: Vec, } /// All the information from a function that is filled out during definition collection rather than @@ -921,6 +921,24 @@ impl NodeInterner { self.structs[&id].clone() } + pub fn get_struct_methods(&self, id: StructId) -> Vec { + self.struct_methods + .keys() + .filter_map(|(key_id, name)| { + if key_id == &id { + Some( + self.struct_methods + .get(&(*key_id, name.clone())) + .expect("get_struct_methods given invalid StructId") + .clone(), + ) + } else { + None + } + }) + .collect() + } + pub fn get_trait(&self, id: TraitId) -> &Trait { &self.traits[&id] } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index c4f0a8d67ba..e4d308fbb6b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -66,7 +66,7 @@ mod test { // Allocate a default Module for the root, giving it a ModuleId let mut modules: Arena = Arena::default(); let location = Location::new(Default::default(), root_file_id); - let root = modules.insert(ModuleData::new(None, location, false)); + let root = modules.insert(ModuleData::new(None, None, location, false)); let def_map = CrateDefMap { root: LocalModuleId(root), diff --git a/yarn-project/end-to-end/src/e2e_delegate_calls.test.ts b/yarn-project/end-to-end/src/e2e_delegate_calls/delegate.test.ts similarity index 54% rename from yarn-project/end-to-end/src/e2e_delegate_calls.test.ts rename to yarn-project/end-to-end/src/e2e_delegate_calls/delegate.test.ts index 45fb4a22e1d..03246dcff0f 100644 --- a/yarn-project/end-to-end/src/e2e_delegate_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_delegate_calls/delegate.test.ts @@ -1,34 +1,25 @@ -import { type Wallet } from '@aztec/aztec.js'; -import { DelegatedOnContract, DelegatorContract } from '@aztec/noir-contracts.js'; - -import { setup } from './fixtures/utils.js'; +import { DelegateCallsTest } from './delegate_calls_test.js'; describe('e2e_delegate_calls', () => { - let wallet: Wallet; - let delegatorContract: DelegatorContract; - let delegatedOnContract: DelegatedOnContract; - let teardown: () => Promise; - - beforeEach(async () => { - ({ teardown, wallet } = await setup()); - }, 100_000); - - afterEach(() => teardown()); + const t = new DelegateCallsTest('delegate_calls'); + let { delegatorContract, delegatedOnContract, wallet } = t; + + beforeAll(async () => { + await t.applyBaseSnapshots(); + await t.setup(); + // Have to destructure again to ensure we have latest refs. + ({ delegatorContract, delegatedOnContract, wallet } = t); + }); - beforeEach(async () => { - delegatorContract = await DelegatorContract.deploy(wallet).send().deployed(); - delegatedOnContract = await DelegatedOnContract.deploy(wallet).send().deployed(); - }, 100_000); + afterAll(async () => { + await t.teardown(); + }); describe('delegates on another contract', () => { it("runs another contract's private function on delegator's storage", async () => { const sentValue = 42n; await delegatorContract.methods - .private_delegate_set_value( - delegatedOnContract.address, - delegatedOnContract.methods.private_set_value.selector, - [sentValue, wallet.getCompleteAddress().address], - ) + .private_delegate_set_value(delegatedOnContract.address, sentValue, wallet.getCompleteAddress().address) .send() .wait(); @@ -46,14 +37,7 @@ describe('e2e_delegate_calls', () => { it("runs another contract's enqueued public function on delegator's storage", async () => { const sentValue = 42n; - await delegatorContract.methods - .enqueued_delegate_set_value( - delegatedOnContract.address, - delegatedOnContract.methods.public_set_value.selector, - [sentValue], - ) - .send() - .wait(); + await delegatorContract.methods.enqueued_delegate_set_value(delegatedOnContract.address, sentValue).send().wait(); const delegatorValue = await delegatorContract.methods.view_public_value().simulate(); const delegatedOnValue = await delegatedOnContract.methods.view_public_value().simulate(); @@ -64,12 +48,7 @@ describe('e2e_delegate_calls', () => { it("runs another contract's public function on delegator's storage", async () => { const sentValue = 42n; - await delegatorContract.methods - .public_delegate_set_value(delegatedOnContract.address, delegatedOnContract.methods.public_set_value.selector, [ - sentValue, - ]) - .send() - .wait(); + await delegatorContract.methods.public_delegate_set_value(delegatedOnContract.address, sentValue).send().wait(); const delegatorValue = await delegatorContract.methods.view_public_value().simulate(); const delegatedOnValue = await delegatedOnContract.methods.view_public_value().simulate(); diff --git a/yarn-project/end-to-end/src/e2e_delegate_calls/delegate_calls_test.ts b/yarn-project/end-to-end/src/e2e_delegate_calls/delegate_calls_test.ts new file mode 100644 index 00000000000..457408a05f7 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_delegate_calls/delegate_calls_test.ts @@ -0,0 +1,72 @@ +import { getSchnorrAccount } from '@aztec/accounts/schnorr'; +import { type AccountWallet, type DebugLogger, createDebugLogger } from '@aztec/aztec.js'; +import { DelegatedOnContract, DelegatorContract } from '@aztec/noir-contracts.js'; + +import { SnapshotManager, type SubsystemsContext, addAccounts } from '../fixtures/snapshot_manager.js'; + +const { E2E_DATA_PATH: dataPath } = process.env; + +export class DelegateCallsTest { + private snapshotManager: SnapshotManager; + logger: DebugLogger; + wallet!: AccountWallet; + delegatorContract!: DelegatorContract; + delegatedOnContract!: DelegatedOnContract; + + constructor(testName: string) { + this.logger = createDebugLogger(`aztec:e2e_delegate_calls:${testName}`); + this.snapshotManager = new SnapshotManager(`e2e_delegate_calls/${testName}`, dataPath); + } + + /** + * Adds two state shifts to snapshot manager. + * 1. Add 3 accounts. + * 2. Publicly deploy accounts, deploy token contract and a "bad account". + */ + async applyBaseSnapshots() { + await this.snapshotManager.snapshot('accounts', addAccounts(1, this.logger), async ({ accountKeys }, { pxe }) => { + const accountManager = getSchnorrAccount(pxe, accountKeys[0][0], accountKeys[0][1], 1); + this.wallet = await accountManager.getWallet(); + this.logger.verbose(`Wallet address: ${this.wallet.getAddress()}`); + }); + + await this.snapshotManager.snapshot( + 'e2e_delegate_calls', + async () => { + this.logger.verbose(`Deploying DelegatorContract...`); + this.delegatorContract = await DelegatorContract.deploy(this.wallet).send().deployed(); + this.logger.verbose(`Delegator deployed to ${this.delegatorContract.address}`); + + this.logger.verbose(`Deploying DelegatedOnContract...`); + this.delegatedOnContract = await DelegatedOnContract.deploy(this.wallet).send().deployed(); + + return { + delegatorContractAddress: this.delegatorContract.address, + delegatedOnContractAddress: this.delegatedOnContract.address, + }; + }, + async ({ delegatorContractAddress, delegatedOnContractAddress }) => { + // Restore the token contract state. + this.delegatorContract = await DelegatorContract.at(delegatorContractAddress, this.wallet); + this.logger.verbose(`Delegator contract address: ${this.delegatorContract.address}`); + + this.delegatedOnContract = await DelegatedOnContract.at(delegatedOnContractAddress, this.wallet); + this.logger.verbose(`DelegatedOn address: ${this.delegatedOnContract.address}`); + }, + ); + } + + async setup() { + await this.snapshotManager.setup(); + } + + snapshot = ( + name: string, + apply: (context: SubsystemsContext) => Promise, + restore: (snapshotData: T, context: SubsystemsContext) => Promise = () => Promise.resolve(), + ): Promise => this.snapshotManager.snapshot(name, apply, restore); + + async teardown() { + await this.snapshotManager.teardown(); + } +} diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index 102e10069d3..0310b4bbfd1 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -169,7 +169,7 @@ describe('e2e_nested_contract', () => { it('calls a method with multiple arguments', async () => { logger.info(`Calling main on importer contract`); - await importerContract.methods.main(testContract.address).send().wait(); + await importerContract.methods.main_contract(testContract.address).send().wait(); }, 30_000); it('calls a method no arguments', async () => { diff --git a/yarn-project/noir-compiler/package.json b/yarn-project/noir-compiler/package.json index 110a881ff8f..c35f9e851d5 100644 --- a/yarn-project/noir-compiler/package.json +++ b/yarn-project/noir-compiler/package.json @@ -58,6 +58,7 @@ "fs-extra": "^11.1.1", "lodash.camelcase": "^4.3.0", "lodash.capitalize": "^4.2.1", + "lodash.uniqby": "^4.7.0", "memfs": "^4.6.0", "pako": "^2.1.0", "semver": "^7.5.4", @@ -70,6 +71,7 @@ "@types/jest": "^29.5.0", "@types/lodash.camelcase": "^4.3.7", "@types/lodash.capitalize": "^4.2.7", + "@types/lodash.uniqby": "^4.7.9", "@types/node": "^18.7.23", "@types/pako": "^2.0.0", "@types/semver": "^7.5.4", diff --git a/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts b/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts index 02fa9eb6c0b..a3330b8368f 100644 --- a/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts +++ b/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts @@ -12,15 +12,10 @@ export function addCodegenCommanderAction(program: Command, _: LogFn = () => {}) .command('codegen') .argument('', 'Path to the Noir ABI or project dir.') .option('-o, --outdir ', 'Output folder for the generated code.') - .option('--ts', 'Generate TypeScript wrapper.') - .option('--nr', 'Generate Noir interface.') .option('--force', 'Force code generation even when the contract has not changed.') .description('Validates and generates an Aztec Contract ABI from Noir ABI.') - .action(async (noirAbiPath: string, { outdir, ts, nr, force }) => { - if (ts && nr) { - throw new Error('--ts and --nr are mutually exclusive.'); - } + .action(async (noirAbiPath: string, { outdir, force }) => { const { generateCode } = await import('./codegen.js'); - generateCode(outdir || dirname(noirAbiPath), noirAbiPath, { ts, nr, force }); + generateCode(outdir || dirname(noirAbiPath), noirAbiPath, { force }); }); } diff --git a/yarn-project/noir-compiler/src/cli/codegen.ts b/yarn-project/noir-compiler/src/cli/codegen.ts index 47aaeaa390b..858109a982b 100644 --- a/yarn-project/noir-compiler/src/cli/codegen.ts +++ b/yarn-project/noir-compiler/src/cli/codegen.ts @@ -5,14 +5,13 @@ import crypto from 'crypto'; import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs'; import path from 'path'; -import { generateNoirContractInterface } from '../contract-interface-gen/noir.js'; import { generateTypescriptContractInterface } from '../contract-interface-gen/typescript.js'; const cacheFilePath = './codegenCache.json'; let cache: Record = {}; /** Generate code options */ -type GenerateCodeOptions = { /** Typescript */ ts?: boolean; /** Noir */ nr?: boolean; force?: boolean }; +type GenerateCodeOptions = { force?: boolean }; /** * Generates Noir interface or Typescript interface for a folder or single file from a Noir compilation artifact. @@ -49,26 +48,18 @@ function generateFromNoirAbi(outputPath: string, noirAbiPath: string, opts: Gene const contract = JSON.parse(readFileSync(noirAbiPath, 'utf8')); const aztecAbi = loadContractArtifact(contract); - const { nr, ts } = opts; mkdirSync(outputPath, { recursive: true }); - if (nr) { - const noirContract = generateNoirContractInterface(aztecAbi); - writeFileSync(`${outputPath}/${aztecAbi.name}.nr`, noirContract); - return; + let relativeArtifactPath = path.relative(outputPath, noirAbiPath); + if (relativeArtifactPath === path.basename(noirAbiPath)) { + // Prepend ./ for local import if the folder is the same + relativeArtifactPath = `./${relativeArtifactPath}`; } - if (ts) { - let relativeArtifactPath = path.relative(outputPath, noirAbiPath); - if (relativeArtifactPath === path.basename(noirAbiPath)) { - // Prepend ./ for local import if the folder is the same - relativeArtifactPath = `./${relativeArtifactPath}`; - } + const tsWrapper = generateTypescriptContractInterface(aztecAbi, relativeArtifactPath); + writeFileSync(`${outputPath}/${aztecAbi.name}.ts`, tsWrapper); - const tsWrapper = generateTypescriptContractInterface(aztecAbi, relativeArtifactPath); - writeFileSync(`${outputPath}/${aztecAbi.name}.ts`, tsWrapper); - } updateCache(contractName, currentHash); } diff --git a/yarn-project/noir-compiler/src/contract-interface-gen/noir.ts b/yarn-project/noir-compiler/src/contract-interface-gen/noir.ts deleted file mode 100644 index 52fd0788973..00000000000 --- a/yarn-project/noir-compiler/src/contract-interface-gen/noir.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { - type ABIParameter, - type ABIVariable, - type ContractArtifact, - type FunctionArtifact, - FunctionSelector, - FunctionType, - type StructType, -} from '@aztec/foundation/abi'; -import { times } from '@aztec/foundation/collection'; - -import camelCase from 'lodash.camelcase'; -import capitalize from 'lodash.capitalize'; - -/** - * Returns whether this function type corresponds to a private call. - * @param functionType - The function type. - * @returns Whether this function type corresponds to a private call. - */ -function isPrivateCall(functionType: FunctionType) { - return functionType === FunctionType.SECRET; -} - -/** - * Generates a call to a private function using the context. - * @param selector - The selector of a function. - * @param functionType - Type of the function. - * @returns A code string. - */ -function generateCallStatement( - selector: FunctionSelector, - functionType: FunctionType, - callingContext: 'private' | 'public', -) { - const callMethod = isPrivateCall(functionType) ? 'call_private_function' : 'call_public_function'; - const args = [ - 'self.address', - `FunctionSelector::from_field(${selector.toString()})`, - 'serialized_args', - ...(callingContext === 'private' ? [] : ['GasOpts::default()']), - ]; - return ` - context.${callMethod}(${args.join(', ')})`; -} - -/** - * Formats a string as pascal case. - * @param str - A string. - * @returns A capitalized camelcase string. - */ -function toPascalCase(str: string) { - const camel = camelCase(str); - return camel[0].toUpperCase() + camel.slice(1); -} - -/** - * Returns a struct name given a list of fragments. - * @param fragments - Fragments. - * @returns The concatenation of the capitalized fragments. - */ -function getStructName(...fragments: string[]) { - return fragments.map(toPascalCase).join('') + 'Struct'; -} - -/** - * Returns a Noir type name for the given ABI variable. - * @param param - ABI variable to translate to a Noir type name. - * @param parentNames - Function name or parent structs or arrays to use for struct qualified names. - * @returns A valid Noir basic type name or a name for a struct. - */ -function getTypeName(param: ABIVariable, ...parentNames: string[]): string { - const type = param.type; - switch (type.kind) { - case 'field': - return 'Field'; - case 'boolean': - return 'bool'; - case 'integer': - return `${type.sign === 'signed' ? 'i' : 'u'}${type.width}`; - case 'string': - throw new Error(`Strings not supported yet`); - case 'array': - return `[${getTypeName({ name: param.name, type: type.type }, ...parentNames)};${type.length}]`; - case 'struct': - return getStructName(param.name, ...parentNames); - default: - throw new Error(`Unknown type ${type}`); - } -} - -/** - * Generates a parameter string. - * @param param - ABI parameter. - * @param functionData - Parent function. - * @returns A Noir string with the param name and type to be used in a function call. - */ -function generateParameter(param: ABIParameter, functionData: FunctionArtifact) { - const typename = getTypeName(param, functionData.name); - return `${param.name}: ${typename}`; -} - -/** - * Collects all parameters for a given function and flattens them according to how they should be serialized. - * @param parameters - Parameters for a function. - * @returns List of parameters flattened to basic data types. - */ -function collectParametersForSerialization(parameters: ABIVariable[]) { - const flattened: string[] = []; - for (const parameter of parameters) { - const { name } = parameter; - if (parameter.type.kind === 'array') { - const nestedType = parameter.type.type; - const nested = times(parameter.type.length, i => - collectParametersForSerialization([{ name: `${name}[${i}]`, type: nestedType }]), - ); - flattened.push(...nested.flat()); - } else if (parameter.type.kind === 'struct') { - const nested = parameter.type.fields.map(field => - collectParametersForSerialization([{ name: `${name}.${field.name}`, type: field.type }]), - ); - flattened.push(...nested.flat()); - } else if (parameter.type.kind === 'string') { - throw new Error(`String not yet supported`); - } else if (parameter.type.kind === 'field') { - flattened.push(name); - } else { - flattened.push(`${name} as Field`); - } - } - return flattened; -} - -/** - * Generates Noir code for serializing the parameters into an array of fields. - * @param parameters - Parameters to serialize. - * @returns The serialization code. - */ -function generateSerialization(parameters: ABIParameter[]) { - const flattened = collectParametersForSerialization(parameters); - const declaration = ` let mut serialized_args = [0; ${flattened.length}];`; - const lines = flattened.map((param, i) => ` serialized_args[${i}] = ${param};`); - return [declaration, ...lines].join('\n'); -} - -/** - * Generate a function interface for a particular function of the Aztec.nr Contract being processed. This function will be a method of the ContractInterface struct being created here. - * @param functionData - Data relating to the function, which can be used to generate a callable Aztec.nr Function. - * @param kind - Whether this interface will be used from private or public functions. - * @returns A code string. - */ -function generateFunctionInterface(functionData: FunctionArtifact, kind: 'private' | 'public') { - const { name, parameters } = functionData; - const selector = FunctionSelector.fromNameAndParameters(name, parameters); - const serialization = generateSerialization(parameters); - const contextType = kind === 'private' ? '&mut PrivateContext' : '&mut PublicContext'; - const callStatement = generateCallStatement(selector, functionData.functionType, kind); - const allParams = ['self', `context: ${contextType}`, ...parameters.map(p => generateParameter(p, functionData))]; - const isPrivate = isPrivateCall(functionData.functionType); - const isSync = (isPrivate && kind === 'private') || (!isPrivate && kind === 'public'); - // TODO: When return typing data is available in the artifact, we can instead codegen the concrete return type for public and private. - const generics = !isPrivate && isSync ? `` : ``; - const retType = isPrivate ? `-> PackedReturns` : isSync ? `-> FunctionReturns ` : ``; - - return ` - pub fn ${name}${generics}( - ${allParams.join(',\n ')} - ) ${retType}{ -${serialization} -${callStatement} - } - `; -} - -/** - * Generates static imports. - * @returns A string of code which will be needed in every contract interface, regardless of the contract. - */ -function generateStaticImports() { - return `use dep::std; -use dep::aztec::context::{ PrivateContext, PublicContext, PackedReturns, FunctionReturns, gas::GasOpts }; -use dep::aztec::protocol_types::{ - address::AztecAddress, - abis::function_selector::FunctionSelector, -};`; -} - -/** - * Generates the name of the contract struct, based on whether it's for private or public usage. - * @param contractName - Name of the contract. - * @param kind - Whether this interface will be used from private or public functions. - * @returns A name. - */ -function generateContractStructName(contractName: string, kind: 'private' | 'public') { - return `${contractName}${capitalize(kind)}ContextInterface`; -} - -/** - * Generate the main focus of this code generator: the contract interface struct. - * @param contractName - the name of the contract, as matches the original source file. - * @param kind - Whether this interface will be used from private or public functions. - * @returns Code. - */ -function generateContractInterfaceStruct(contractName: string, kind: 'private' | 'public') { - return `// Interface for calling ${contractName} functions from a ${kind} context -struct ${generateContractStructName(contractName, kind)} { - address: AztecAddress, -} -`; -} - -/** - * Generates the implementation of the contract interface struct. - * @param contractName - The name of the contract, as matches the original source file. - * @param kind - Whether this interface will be used from private or public functions. - * @param functions - An array of strings, where each string is valid Noir code describing the function interface of one of the contract's functions (as generated via `generateFunctionInterface` above). - * @returns Code. - */ -function generateContractInterfaceImpl(contractName: string, kind: 'private' | 'public', functions: string[]) { - return `impl ${generateContractStructName(contractName, kind)} { - pub fn at(address: AztecAddress) -> Self { - Self { - address, - } - } - ${functions.join('\n')} -} -`; -} - -/** Represents a struct along its parent names to derive a fully qualified name. */ -type StructInfo = ABIVariable & { /** Parent name */ parentNames: string[] }; - -/** - * Generates a Noir struct. - * @param struct - Struct info. - * @returns Code representing the struct. - */ -function generateStruct(struct: StructInfo) { - const fields = (struct.type as StructType).fields.map( - field => ` ${field.name}: ${getTypeName(field, struct.name, ...struct.parentNames)},`, - ); - - return ` -struct ${getStructName(struct.name, ...struct.parentNames)} { -${fields.join('\n')} -}`; -} - -/** - * Collects all structs across all parameters. - * @param params - Parameters to look for structs, either structs themselves or nested. - * @param parentNames - Parent names to derive fully qualified names when needed. - * @returns A list of struct infos. - */ -function collectStructs(params: ABIVariable[], parentNames: string[]): StructInfo[] { - const structs: StructInfo[] = []; - for (const param of params) { - if (param.type.kind === 'struct') { - const struct = { ...param, parentNames }; - structs.push(struct, ...collectStructs(param.type.fields, [param.name, ...parentNames])); - } else if (param.type.kind === 'array') { - structs.push(...collectStructs([{ name: param.name, type: param.type.type }], [...parentNames])); - } - } - return structs; -} - -/** - * Generates the struct definition and implementation for a contract interface. - * @param abiName - Name of the contract. - * @param kind - Whether this interface will be used from private or public functions. - * @param methods - Contract methods to generate (private ones will be excluded if kind is public) - * @returns Code. - */ -function generateContractStruct(abiName: string, kind: 'private' | 'public', methods: FunctionArtifact[]) { - const contractStruct: string = generateContractInterfaceStruct(abiName, kind); - const applicableMethods = methods.filter(m => kind === 'private' || !isPrivateCall(m.functionType)); - const functionInterfaces = applicableMethods.map(m => generateFunctionInterface(m, kind)); - const contractImpl: string = generateContractInterfaceImpl(abiName, kind, functionInterfaces); - - return ` -${contractStruct} -${contractImpl} - `; -} - -/** - * Generates the Noir code to represent an interface for calling a contract. - * @param artifact - The compiled Aztec.nr artifact. - * @returns The corresponding ts code. - */ -export function generateNoirContractInterface(artifact: ContractArtifact) { - // We don't allow calling internal fns or unconstrained fns from other contracts - const methods = artifact.functions.filter(f => !f.isInternal && f.functionType !== FunctionType.UNCONSTRAINED); - const paramStructs = methods.flatMap(m => collectStructs(m.parameters, [m.name])).map(generateStruct); - const privateContractStruct = generateContractStruct(artifact.name, 'private', methods); - const publicContractStruct = generateContractStruct(artifact.name, 'public', methods); - - return `/* Autogenerated file, do not edit! */ - -${generateStaticImports()} -${paramStructs.join('\n')} - -${privateContractStruct} - -${publicContractStruct} -`; -} diff --git a/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts b/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts index 0ade1726ec8..04f1af32ed4 100644 --- a/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts +++ b/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts @@ -14,6 +14,8 @@ import { isWrappedFieldStruct, } from '@aztec/foundation/abi'; +import uniqBy from 'lodash.uniqby'; + /** * Returns the corresponding typescript type for a given Noir type. * @param type - The input Noir type. @@ -224,7 +226,9 @@ function generateStorageLayoutGetter(input: ContractArtifact) { * @param input - The contract artifact. */ function generateNotesGetter(input: ContractArtifact) { - const notes = input.outputs.globals.notes ? (input.outputs.globals.notes as TupleValue[]) : []; + const notes = input.outputs.globals.notes + ? uniqBy(input.outputs.globals.notes as TupleValue[], n => (n.fields[1] as BasicValue<'string', string>).value) + : []; const notesUnionType = notes.map(n => `'${(n.fields[1] as BasicValue<'string', string>).value}'`).join(' | '); const noteMetadata = notes diff --git a/yarn-project/noir-compiler/src/index.ts b/yarn-project/noir-compiler/src/index.ts index a28e4f09303..c6c4a84e441 100644 --- a/yarn-project/noir-compiler/src/index.ts +++ b/yarn-project/noir-compiler/src/index.ts @@ -1,2 +1 @@ export { generateTypescriptContractInterface } from './contract-interface-gen/typescript.js'; -export { generateNoirContractInterface } from './contract-interface-gen/noir.js'; diff --git a/yarn-project/noir-contracts.js/scripts/generate-noir-interfaces.sh b/yarn-project/noir-contracts.js/scripts/generate-noir-interfaces.sh deleted file mode 100755 index 249d168242f..00000000000 --- a/yarn-project/noir-contracts.js/scripts/generate-noir-interfaces.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -CONTRACTS=( - test_contract-Test -) - -NOIR_CONTRACTS_DIR="../../noir-projects/noir-contracts" - -for contract in $CONTRACTS; do - echo "Generating Noir interface for $contract" - OUT_DIR="$NOIR_CONTRACTS_DIR/contracts/${contract%%-*}/src" - node --no-warnings ../noir-compiler/dest/cli.js codegen -o $OUT_DIR --nr --force $NOIR_CONTRACTS_DIR/target/$contract.json - mv $OUT_DIR/${contract#*-}.nr $OUT_DIR/interface.nr -done - -echo "Done updating Noir interfaces" \ No newline at end of file diff --git a/yarn-project/noir-contracts.js/scripts/generate-types.sh b/yarn-project/noir-contracts.js/scripts/generate-types.sh index 87a13d3459d..6e98b09125b 100755 --- a/yarn-project/noir-contracts.js/scripts/generate-types.sh +++ b/yarn-project/noir-contracts.js/scripts/generate-types.sh @@ -28,7 +28,7 @@ for ABI in $(find ../../noir-projects/noir-contracts/target -maxdepth 1 -type f done # Generate types for the contracts -node --no-warnings ../noir-compiler/dest/cli.js codegen -o $OUT_DIR --ts artifacts +node --no-warnings ../noir-compiler/dest/cli.js codegen -o $OUT_DIR artifacts # Append exports for each generated TypeScript file to index.ts find "$OUT_DIR" -maxdepth 1 -type f -name '*.ts' ! -name 'index.ts' | while read -r TS_FILE; do diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index c9223ae8f1d..cccbb59c2bc 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -21,8 +21,13 @@ export class Oracle { return toACVMField(val); } - async packArguments(args: ACVMField[]): Promise { - const packed = await this.typedOracle.packArguments(args.map(fromACVMField)); + async packArgumentsArray(args: ACVMField[]): Promise { + const packed = await this.typedOracle.packArgumentsArray(args.map(fromACVMField)); + return toACVMField(packed); + } + + async packArguments(_length: ACVMField[], values: ACVMField[]): Promise { + const packed = await this.typedOracle.packArgumentsArray(values.map(fromACVMField)); return toACVMField(packed); } diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 4f245732275..e52548cb5c2 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -85,8 +85,8 @@ export abstract class TypedOracle { return Fr.random(); } - packArguments(_args: Fr[]): Promise { - throw new OracleMethodNotAvailableError('packArguments'); + packArgumentsArray(_args: Fr[]): Promise { + throw new OracleMethodNotAvailableError('packArgumentsArray'); } packReturns(_returns: Fr[]): Promise { diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 0c045247407..08aeebed187 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -171,10 +171,10 @@ export class ClientExecutionContext extends ViewDataOracle { } /** - * Pack the given arguments. + * Pack the given array of arguments. * @param args - Arguments to pack */ - public override packArguments(args: Fr[]): Promise { + public override packArgumentsArray(args: Fr[]): Promise { return Promise.resolve(this.packedValuesCache.pack(args)); } diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 716232104a6..512060e9948 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -491,7 +491,7 @@ describe('Private Execution test suite', () => { it('test function should be callable through autogenerated interface', async () => { const testAddress = AztecAddress.random(); - const parentArtifact = getFunctionArtifact(ImportTestContractArtifact, 'main'); + const parentArtifact = getFunctionArtifact(ImportTestContractArtifact, 'main_contract'); const testCodeGenSelector = FunctionSelector.fromNameAndParameters( testCodeGenArtifact.name, testCodeGenArtifact.parameters, diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index e0b0922c934..686b7e3ba05 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -90,10 +90,10 @@ export class PublicExecutionContext extends TypedOracle { } /** - * Pack the given arguments. + * Pack the given array of arguments. * @param args - Arguments to pack */ - public override packArguments(args: Fr[]): Promise { + public override packArgumentsArray(args: Fr[]): Promise { return Promise.resolve(this.packedValuesCache.pack(args)); } diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 23adca3a1b6..6fb7bf150b5 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -607,6 +607,7 @@ __metadata: "@types/jest": ^29.5.0 "@types/lodash.camelcase": ^4.3.7 "@types/lodash.capitalize": ^4.2.7 + "@types/lodash.uniqby": ^4.7.9 "@types/node": ^18.7.23 "@types/pako": ^2.0.0 "@types/semver": ^7.5.4 @@ -616,6 +617,7 @@ __metadata: jest: ^29.5.0 lodash.camelcase: ^4.3.0 lodash.capitalize: ^4.2.1 + lodash.uniqby: ^4.7.0 memfs: ^4.6.0 pako: ^2.1.0 semver: ^7.5.4 @@ -3634,6 +3636,15 @@ __metadata: languageName: node linkType: hard +"@types/lodash.uniqby@npm:^4.7.9": + version: 4.7.9 + resolution: "@types/lodash.uniqby@npm:4.7.9" + dependencies: + "@types/lodash": "*" + checksum: 24cc8af36e0d4c52b7294c7ba7d814c89ce2c8118d94350bbed21031fef850fa1a280bfd2b30a47e0b5f7aa6ac649a36a5089aa76bc23787963a5ee6443f631e + languageName: node + linkType: hard + "@types/lodash@npm:*": version: 4.14.202 resolution: "@types/lodash@npm:4.14.202" @@ -9844,6 +9855,13 @@ __metadata: languageName: node linkType: hard +"lodash.uniqby@npm:^4.7.0": + version: 4.7.0 + resolution: "lodash.uniqby@npm:4.7.0" + checksum: 659264545a95726d1493123345aad8cbf56e17810fa9a0b029852c6d42bc80517696af09d99b23bef1845d10d95e01b8b4a1da578f22aeba7a30d3e0022a4938 + languageName: node + linkType: hard + "lodash@npm:^4.17.21, lodash@npm:^4.17.4": version: 4.17.21 resolution: "lodash@npm:4.17.21"