Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: TXE single execution env #9183

Merged
merged 23 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2900390
removed direct noir calls
Thunkar Oct 11, 2024
67fd3fa
more cleanup
Thunkar Oct 11, 2024
725f2e6
more cleanup
Thunkar Oct 11, 2024
0640f2a
Merge branch 'master' of github.com:AztecProtocol/aztec-packages into…
Thunkar Oct 11, 2024
eec0c40
Merge branch 'master' of github.com:AztecProtocol/aztec-packages into…
Thunkar Oct 14, 2024
f0e9846
fixes
Thunkar Oct 15, 2024
9d0d9f0
Merge branch 'master' of github.com:AztecProtocol/aztec-packages into…
Thunkar Oct 15, 2024
e581fb8
fmt and fixes
Thunkar Oct 15, 2024
aa5c50b
docs
Thunkar Oct 15, 2024
20e91a2
unify simulation error handling
Thunkar Oct 16, 2024
8ed69e6
fmt
Thunkar Oct 16, 2024
2027fba
comments from review
Thunkar Oct 17, 2024
70b1319
Merge branch 'master' into gj/txe_single_execution_env
Thunkar Oct 17, 2024
22da773
removed dep
Thunkar Oct 17, 2024
9e84eca
Merge branch 'gj/txe_single_execution_env' of github.com:AztecProtoco…
Thunkar Oct 17, 2024
e030e09
Merge branch 'master' of github.com:AztecProtocol/aztec-packages into…
Thunkar Oct 18, 2024
4c2778a
Merge branch 'master' of github.com:AztecProtocol/aztec-packages into…
Thunkar Oct 18, 2024
d174d0c
Update yarn-project/pxe/src/pxe_service/error_enriching.ts
Thunkar Oct 23, 2024
1439b09
Merge branch 'master' of github.com:AztecProtocol/aztec-packages into…
Thunkar Oct 23, 2024
75fda87
fix
Thunkar Oct 23, 2024
268ff71
fix
Thunkar Oct 23, 2024
095f2af
Merge branch 'master' of github.com:AztecProtocol/aztec-packages into…
Thunkar Oct 23, 2024
070edb9
fixes
Thunkar Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ let my_contract_instance = deployer.without_initializer();
```

:::warning
It is not always necessary to deploy a contract in order to test it, but sometimes it's inevitable (when testing functions that depend on the contract being initialized, or contracts that call others for example) **It is important to keep them up to date**, as TXE cannot recompile them on changes. Think of it as regenerating the bytecode and ABI so it becomes accessible externally.
It is always necessary to deploy a contract in order to test it. **It is important to keep them up to date**, as TXE cannot recompile them on changes. Think of it as regenerating the bytecode and ABI so it becomes accessible externally.
:::

### Calling functions
Expand Down Expand Up @@ -210,7 +210,7 @@ For example:

You can also use the `assert_public_call_fails` or `assert_private_call_fails` methods on the `TestEnvironment` to check that a call fails.

#include_code assert_public_fail /noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_public.nr rust
#include_code assert_public_fail /noir-projects/noir-contracts/contracts/token_contract/src/test/access_control.nr rust

### Logging

Expand Down
12 changes: 12 additions & 0 deletions docs/docs/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ keywords: [sandbox, aztec, notes, migration, updating, upgrading]

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

## 0.X.X
### [TXE] Single execution environment
Thanks to recent advancements in Brillig TXE performs every single call as if it was a nested call, spawning a new ACVM or AVM simulator without performance loss.
This ensures every single test runs in a consistent environment and allows for clearer test syntax:

```diff
-let my_call_interface = MyContract::at(address).my_function(args);
-env.call_private(my_contract_interface)
+MyContract::at(address).my_function(args).call(&mut env.private());
```
This implies every contract has to be deployed before it can be tested (via `env.deploy` or `env.deploy_self`) and of course it has to be recompiled if its code was changed before TXE can use the modified bytecode.

## 0.58.0
### [l1-contracts] Inbox's MessageSent event emits global tree index
Earlier `MessageSent` event in Inbox emitted a subtree index (index of the message in the subtree of the l2Block). But the nodes and Aztec.nr expects the index in the global L1_TO_L2_MESSAGES_TREE. So to make it easier to parse this, Inbox now emits this global index.
Expand Down
10 changes: 3 additions & 7 deletions noir-projects/aztec-nr/authwit/src/cheatcodes.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ use dep::aztec::{

use crate::auth::{compute_inner_authwit_hash, compute_authwit_message_hash, set_authorized};

pub fn add_private_authwit_from_call_interface<C, let M: u32, T, P, Env>(
on_behalf_of: AztecAddress,
caller: AztecAddress,
call_interface: C
) where C: CallInterface<M, T, P, Env> {
pub fn add_private_authwit_from_call_interface<C, let M: u32>(on_behalf_of: AztecAddress, caller: AztecAddress, call_interface: C) where C: CallInterface<M> {
let target = call_interface.get_contract_address();
let inputs = cheatcodes::get_private_context_inputs(get_block_number());
let chain_id = inputs.tx_context.chain_id;
Expand All @@ -22,11 +18,11 @@ pub fn add_private_authwit_from_call_interface<C, let M: u32, T, P, Env>(
cheatcodes::add_authwit(on_behalf_of, message_hash);
}

pub fn add_public_authwit_from_call_interface<C, let M: u32, T, P, Env>(
pub fn add_public_authwit_from_call_interface<C, let M: u32>(
on_behalf_of: AztecAddress,
caller: AztecAddress,
call_interface: C
) where C: CallInterface<M, T, P, Env> {
) where C: CallInterface<M> {
let current_contract = get_contract_address();
cheatcodes::set_contract_address(on_behalf_of);
let target = call_interface.get_contract_address();
Expand Down
105 changes: 33 additions & 72 deletions noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use dep::protocol_types::{
abis::{function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs},
address::AztecAddress, traits::Deserialize
};
use dep::protocol_types::{abis::{function_selector::FunctionSelector}, address::AztecAddress, traits::Deserialize};

use crate::context::{
private_context::PrivateContext, public_context::PublicContext, gas::GasOpts,
Expand All @@ -11,9 +8,7 @@ use crate::context::{
use crate::oracle::arguments::pack_arguments;
use crate::hash::hash_args;

pub trait CallInterface<let N: u32, T, P, Env> {
fn get_original(self) -> fn[Env](T) -> P;

pub trait CallInterface<let N: u32> {
fn get_args(self) -> [Field] {
self.args
}
Expand All @@ -35,23 +30,17 @@ pub trait CallInterface<let N: u32, T, P, Env> {
}
}

impl<let N: u32, T, P, Env> CallInterface<N, PrivateContextInputs, PrivateCircuitPublicInputs, Env> for PrivateCallInterface<N, T, Env> {
fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs {
self.original
}
}

pub struct PrivateCallInterface<let N: u32, T, Env> {
pub struct PrivateCallInterface<let N: u32, T> {
target_contract: AztecAddress,
selector: FunctionSelector,
name: str<N>,
args_hash: Field,
args: [Field],
original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs,
return_type: T,
is_static: bool
}

impl<let N: u32, T, Env> PrivateCallInterface<N, T, Env> {
impl<let N: u32, T> PrivateCallInterface<N, T> {
pub fn call<let M: u32>(self, context: &mut PrivateContext) -> T where T: Deserialize<M> {
pack_arguments(self.args);
let returns = context.call_private_function_with_packed_args(
Expand All @@ -78,23 +67,19 @@ impl<let N: u32, T, Env> PrivateCallInterface<N, T, Env> {
}
}

impl<let N: u32, T, P, Env> CallInterface<N, PrivateContextInputs, PrivateCircuitPublicInputs, Env> for PrivateVoidCallInterface<N, Env> {
fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs {
self.original
}
}
impl<let N: u32> CallInterface<N> for PrivateVoidCallInterface<N> {}

pub struct PrivateVoidCallInterface<let N: u32, Env> {
pub struct PrivateVoidCallInterface<let N: u32> {
target_contract: AztecAddress,
selector: FunctionSelector,
name: str<N>,
args_hash: Field,
args: [Field],
original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs,
return_type: (),
is_static: bool
}

impl<let N: u32, Env> PrivateVoidCallInterface<N, Env> {
impl<let N: u32> PrivateVoidCallInterface<N> {
pub fn call(self, context: &mut PrivateContext) {
pack_arguments(self.args);
context.call_private_function_with_packed_args(
Expand All @@ -117,70 +102,58 @@ impl<let N: u32, Env> PrivateVoidCallInterface<N, Env> {
}
}

impl<let N: u32, T, P, Env> CallInterface<N, PrivateContextInputs, PrivateCircuitPublicInputs, Env> for PrivateStaticCallInterface<N, T, Env> {
fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs {
self.original
}
}
impl<let N: u32, T> CallInterface<N> for PrivateStaticCallInterface<N, T> {}

pub struct PrivateStaticCallInterface<let N: u32, T, Env> {
pub struct PrivateStaticCallInterface<let N: u32, T> {
target_contract: AztecAddress,
selector: FunctionSelector,
name: str<N>,
args_hash: Field,
args: [Field],
original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs,
return_type: T,
is_static: bool
}

impl<let N: u32, T, Env> PrivateStaticCallInterface<N, T, Env> {
impl<let N: u32, T> PrivateStaticCallInterface<N, T> {
pub fn view<let M: u32>(self, context: &mut PrivateContext) -> T where T: Deserialize<M> {
pack_arguments(self.args);
let returns = context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false);
returns.unpack_into()
}
}

impl<let N: u32, T, P, Env> CallInterface<N, PrivateContextInputs, PrivateCircuitPublicInputs, Env> for PrivateStaticVoidCallInterface<N, Env> {
fn get_original(self) -> fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs {
self.original
}
}
impl<let N: u32> CallInterface<N> for PrivateStaticVoidCallInterface<N> {}

pub struct PrivateStaticVoidCallInterface<let N: u32, Env> {
pub struct PrivateStaticVoidCallInterface<let N: u32> {
target_contract: AztecAddress,
selector: FunctionSelector,
name: str<N>,
args_hash: Field,
args: [Field],
original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs,
return_type: (),
is_static: bool
}

impl<let N: u32, Env> PrivateStaticVoidCallInterface<N, Env> {
impl<let N: u32> PrivateStaticVoidCallInterface<N> {
pub fn view(self, context: &mut PrivateContext) {
pack_arguments(self.args);
context.call_private_function_with_packed_args(self.target_contract, self.selector, self.args_hash, true, false).assert_empty();
}
}

impl<let N: u32, T, P, Env> CallInterface<N, (), T, Env> for PublicCallInterface<N, T, Env> {
fn get_original(self) -> fn[Env](()) -> T {
self.original
}
}
impl<let N: u32, T> CallInterface<N> for PublicCallInterface<N, T> {}

pub struct PublicCallInterface<let N: u32, T, Env> {
pub struct PublicCallInterface<let N: u32, T> {
target_contract: AztecAddress,
selector: FunctionSelector,
name: str<N>,
args: [Field],
gas_opts: GasOpts,
original: fn[Env](()) -> T,
return_type: T,
is_static: bool
}

impl<let N: u32, T, Env> PublicCallInterface<N, T, Env> {
impl<let N: u32, T> PublicCallInterface<N, T> {
pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self {
self.gas_opts = gas_opts;
self
Expand Down Expand Up @@ -238,23 +211,19 @@ impl<let N: u32, T, Env> PublicCallInterface<N, T, Env> {
}
}

impl<let N: u32, T, P, Env> CallInterface<N, (), (), Env> for PublicVoidCallInterface<N, Env> {
fn get_original(self) -> fn[Env](()) -> () {
self.original
}
}
impl<let N: u32> CallInterface<N> for PublicVoidCallInterface<N> {}

pub struct PublicVoidCallInterface<let N: u32, Env> {
pub struct PublicVoidCallInterface<let N: u32> {
target_contract: AztecAddress,
nventuro marked this conversation as resolved.
Show resolved Hide resolved
selector: FunctionSelector,
name: str<N>,
args: [Field],
original: fn[Env](()) -> (),
return_type: (),
is_static: bool,
gas_opts: GasOpts
}

impl<let N: u32, Env> PublicVoidCallInterface<N, Env> {
impl<let N: u32> PublicVoidCallInterface<N> {
pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self {
self.gas_opts = gas_opts;
self
Expand Down Expand Up @@ -312,23 +281,19 @@ impl<let N: u32, Env> PublicVoidCallInterface<N, Env> {
}
}

impl<let N: u32, T, P, Env> CallInterface<N, (), T, Env> for PublicStaticCallInterface<N, T, Env> {
fn get_original(self) -> fn[Env](()) -> T {
self.original
}
}
impl<let N: u32, T> CallInterface<N> for PublicStaticCallInterface<N, T> {}

pub struct PublicStaticCallInterface<let N: u32, T, Env> {
pub struct PublicStaticCallInterface<let N: u32, T> {
target_contract: AztecAddress,
selector: FunctionSelector,
name: str<N>,
args: [Field],
original: fn[Env](()) -> T,
return_type: T,
is_static: bool,
gas_opts: GasOpts
}

impl<let N: u32, T, Env> PublicStaticCallInterface<N, T, Env> {
impl<let N: u32, T> PublicStaticCallInterface<N, T> {
pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self {
self.gas_opts = gas_opts;
self
Expand All @@ -353,23 +318,19 @@ impl<let N: u32, T, Env> PublicStaticCallInterface<N, T, Env> {
}
}

impl<let N: u32, T, P, Env> CallInterface<N, (), (), Env> for PublicStaticVoidCallInterface<N, Env> {
fn get_original(self) -> fn[Env](()) -> () {
self.original
}
}
impl<let N: u32> CallInterface<N> for PublicStaticVoidCallInterface<N> {}

pub struct PublicStaticVoidCallInterface<let N: u32, Env> {
pub struct PublicStaticVoidCallInterface<let N: u32> {
target_contract: AztecAddress,
selector: FunctionSelector,
name: str<N>,
args: [Field],
original: fn[Env](()) -> (),
return_type: (),
is_static: bool,
gas_opts: GasOpts
}

impl<let N: u32, Env> PublicStaticVoidCallInterface<N, Env> {
impl<let N: u32> PublicStaticVoidCallInterface<N> {
pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self {
self.gas_opts = gas_opts;
self
Expand Down
46 changes: 3 additions & 43 deletions noir-projects/aztec-nr/aztec/src/macros/functions/interfaces.nr
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,10 @@ pub comptime fn stub_fn(f: FunctionDefinition) -> Quoted {

let fn_name_len: u32 = unquote!(quote { $fn_name_str.as_bytes().len()});

let arg_types_list: Quoted = fn_parameters.map(|(_, typ): (_, Type)| quote { $typ }).join(quote {,});
let arg_types = if fn_parameters.len() == 1 {
// Extra colon to avoid it being interpreted as a parenthesized expression instead of a tuple
quote { ($arg_types_list,) }
} else {
quote { ($arg_types_list) }
};

let call_interface_generics = if is_void {
quote { $fn_name_len, $arg_types }
quote { $fn_name_len }
Thunkar marked this conversation as resolved.
Show resolved Hide resolved
} else {
quote { $fn_name_len, $fn_return_type, $arg_types }
quote { $fn_name_len, $fn_return_type }
};

let call_interface_name = f"dep::aztec::context::call_interfaces::{fn_visibility_capitalized}{is_static_call_capitalized}{is_void_capitalized}CallInterface".quoted_contents();
Expand All @@ -128,38 +120,6 @@ pub comptime fn stub_fn(f: FunctionDefinition) -> Quoted {
quote {}
};

let input_type = if is_fn_private(f) {
quote { crate::context::inputs::PrivateContextInputs }.as_type()
} else {
quote { () }.as_type()
};

let return_type_hint = if is_fn_private(f) {
quote { protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs }.as_type()
} else {
fn_return_type
};

let mut parameter_names_list = fn_parameters.map(|(name, _): (Quoted, _)| name);
let parameter_names = if is_fn_private(f) {
&[quote {inputs}].append(parameter_names_list).join(quote{,})
} else {
parameter_names_list.join(quote {,})
};
let original = if is_fn_private(f) {
quote {
| inputs: $input_type | -> $return_type_hint {
$fn_name($parameter_names)
}
}
} else {
quote {
| _: $input_type | -> $return_type_hint {
unsafe { $fn_name($parameter_names) }
}
}
};

let args_hash = if fn_visibility == quote { private } {
quote { $args_hash_name, }
} else {
Expand All @@ -176,7 +136,7 @@ pub comptime fn stub_fn(f: FunctionDefinition) -> Quoted {
name: $fn_name_str,
$args_hash
args: $args_acc_name,
original: $original,
return_type: std::mem::zeroed(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this

Copy link
Contributor Author

@Thunkar Thunkar Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call interfaces were sort of broken before. Return value types were unbound and that led to weird situations such as:

#[private]
fn my_fn() -> u8 {...}

...

#[test]
fn test_my_fn() {
  let result = MyContract::at(somewhere).my_fn().call(&mut env.private());
  assert(result == 8 // this is a Field, so result is cast to Field);
}

return_value in the call interfaces is essentially rust's PhantomData, so we are able to properly type the return value of the call interface (both in TXE and actual contracts)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I understood this, and did some reading around on PhantomData, but then tried to write down a comment explaining why zeroed needs to be there and failed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's similar to when we needed array hints in methods for length...we need a return_type variable with the correct Type, but we don't know what the actual value is (because we haven't called the function yet)

is_static: $is_static_call,
$gas_opts
}
Expand Down
3 changes: 2 additions & 1 deletion noir-projects/aztec-nr/aztec/src/macros/functions/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use super::utils::{
use protocol_types::meta::flatten_to_fields;

use interfaces::{create_fn_abi_export, register_stub, stub_fn};
use super::utils::fn_requires_authwit;

// Functions can have multiple attributes applied to them, e.g. a single function can have #[public], #[view] and
// #[internal]. However. the order in which this will be evaluated is unknown, which makes combining them tricky.
Expand Down Expand Up @@ -102,7 +103,7 @@ pub comptime fn private(f: FunctionDefinition) -> Quoted {

let mut body = f.body().as_block().unwrap();

// The original params are hashed and passed to the `context` object, so that the kernel can verify we're received
// The original params are hashed and passed to the `context` object, so that the kernel can verify we've received
// the correct values.

// TODO: Optimize args_hasher for small number of arguments
Expand Down
Loading
Loading