Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 11 additions & 5 deletions docs/docs/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Aztec is in full-speed development. Literally every version breaks compatibility

## TBD

### Changes to public calling convention

Contracts that include public functions (that is, marked with `#[public]`), are required to have a function `public_dispatch(selector: Field)` which acts as an entry point. This will be soon the only public function registered/deployed in contracts. The calling convention is updated so that external calls are made to this function.

If you are writing your contracts using Aztec-nr, there is nothing you need to change. The `public_dispatch` function is automatically generated by the `#[aztec]` macro.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@critesjosh can you create a github issue for devrel to add this to macros docs?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yes will do


### [Aztec.nr] Renamed `unsafe_rand` to `random`

Since this is an `unconstrained` function, callers are already supposed to include an `unsafe` block, so this function has been renamed for reduced verbosity.
Expand Down Expand Up @@ -64,8 +70,8 @@ contract XYZ {

- numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m, owner));
+ numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner_ivpk_m, owner));

}
```

## 0.56.0

Expand Down Expand Up @@ -208,6 +214,7 @@ export LOG_LEVEL="debug"
- assert(verification == true);
- true
+ std::ecdsa_secp256k1::verify_signature(public_key.x, public_key.y, signature, hashed_message)
```

## 0.49.0

Expand Down Expand Up @@ -406,17 +413,16 @@ struct WithdrawalProcessed {

### [Aztec.nr] rename `encode_and_encrypt_with_keys` to `encode_and_encrypt_note_with_keys`

````diff
```diff
contract XYZ {
- use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_with_keys;
+ use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note_with_keys;
....

- numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m));
+ numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m));

}

```

### [Aztec.nr] changes to `NoteInterface`

Expand Down Expand Up @@ -469,7 +475,7 @@ These changes were done because having the note hash exposed allowed us to not h
+ (note_hash_for_nullify, nullifier)
+ }
+ }
````
```

### [Aztec.nr] `note_getter` returns `BoundedVec`

Expand Down
1 change: 1 addition & 0 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ library Constants {
uint256 internal constant GENESIS_ARCHIVE_ROOT =
8142738430000951296386584486068033372964809139261822027365426310856631083550;
uint256 internal constant FEE_JUICE_INITIAL_MINT = 20000000000;
uint256 internal constant PUBLIC_DISPATCH_SELECTOR = 3578010381;
uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 20000;
uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000;
uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 3000;
Expand Down
25 changes: 22 additions & 3 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use dep::protocol_types::{
MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,
MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL,
MAX_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_ENCRYPTED_LOGS_PER_CALL, MAX_UNENCRYPTED_LOGS_PER_CALL,
MAX_NOTE_ENCRYPTED_LOGS_PER_CALL
MAX_NOTE_ENCRYPTED_LOGS_PER_CALL, PUBLIC_DISPATCH_SELECTOR
},
header::Header, messaging::l2_to_l1_message::L2ToL1Message, traits::Empty
};
Expand Down Expand Up @@ -468,7 +468,13 @@ impl PrivateContext {
let counter = self.next_counter();

let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;
enqueue_public_function_call_internal(
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this.
// WARNING: This is insecure and should be temporary!
// The oracle repacks the arguments and returns a new args_hash.
// new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function.
Comment thread
fcarreiro marked this conversation as resolved.
// We don't validate or compute it in the circuit because a) it's harder to do with slices, and
// b) this is only temporary.
let args_hash = enqueue_public_function_call_internal(
contract_address,
function_selector,
args_hash,
Expand All @@ -477,6 +483,10 @@ impl PrivateContext {
is_delegate_call
);

// Public calls are rerouted through the dispatch function.
let function_selector = comptime {
FunctionSelector::from_field(PUBLIC_DISPATCH_SELECTOR)
};
let call_context = self.generate_call_context(
contract_address,
function_selector,
Expand Down Expand Up @@ -510,7 +520,13 @@ impl PrivateContext {
let counter = self.next_counter();

let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;
set_public_teardown_function_call_internal(
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Fix this.
// WARNING: This is insecure and should be temporary!
// The oracle repacks the arguments and returns a new args_hash.
// new_args = [selector, ...old_args], so as to make it suitable to call the public dispatch function.
// We don't validate or compute it in the circuit because a) it's harder to do with slices, and
// b) this is only temporary.
let args_hash = set_public_teardown_function_call_internal(
contract_address,
function_selector,
args_hash,
Expand All @@ -519,6 +535,9 @@ impl PrivateContext {
is_delegate_call
);

let function_selector = comptime {
FunctionSelector::from_field(PUBLIC_DISPATCH_SELECTOR)
};
let call_context = self.generate_call_context(
contract_address,
function_selector,
Expand Down
24 changes: 15 additions & 9 deletions noir-projects/aztec-nr/aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::hash::{compute_secret_hash, compute_message_hash, compute_message_nullifier};
use dep::protocol_types::address::{AztecAddress, EthAddress};
use dep::protocol_types::constants::MAX_FIELD_VALUE;
use dep::protocol_types::constants::{MAX_FIELD_VALUE, PUBLIC_DISPATCH_SELECTOR};
use dep::protocol_types::traits::{Serialize, Deserialize, Empty};
use dep::protocol_types::abis::function_selector::FunctionSelector;
use crate::context::gas::GasOpts;
Expand Down Expand Up @@ -70,11 +70,12 @@ impl PublicContext {
args: [Field],
gas_opts: GasOpts
) -> FunctionReturns<RETURNS_COUNT> {
let args = &[function_selector.to_field()].append(args);
let results = call(
gas_for_call(gas_opts),
contract_address,
args,
function_selector.to_field()
PUBLIC_DISPATCH_SELECTOR
);
let data_to_return: [Field; RETURNS_COUNT] = results.0;
let success: u8 = results.1;
Expand All @@ -90,11 +91,12 @@ impl PublicContext {
args: [Field],
gas_opts: GasOpts
) -> FunctionReturns<RETURNS_COUNT> {
let args = &[function_selector.to_field()].append(args);
let (data_to_return, success): ([Field; RETURNS_COUNT], u8) = call_static(
gas_for_call(gas_opts),
contract_address,
args,
function_selector.to_field()
PUBLIC_DISPATCH_SELECTOR
);

assert(success == 1, "Nested static call failed!");
Expand Down Expand Up @@ -127,7 +129,9 @@ impl PublicContext {
sender()
}
fn selector(_self: Self) -> FunctionSelector {
FunctionSelector::from_u32(function_selector())
// The selector is the first element of the calldata when calling a public function through dispatch.
let raw_selector: [Field; 1] = calldata_copy(0, 1);
FunctionSelector::from_field(raw_selector[0])
}
fn get_args_hash(mut self) -> Field {
if !self.args_hash.is_some() {
Expand Down Expand Up @@ -215,9 +219,10 @@ unconstrained fn sender() -> AztecAddress {
unconstrained fn portal() -> EthAddress {
portal_opcode()
}
unconstrained fn function_selector() -> u32 {
function_selector_opcode()
}
// UNUSED: Remove.
// unconstrained fn function_selector() -> u32 {
// function_selector_opcode()
// }
Comment thread
dbanks12 marked this conversation as resolved.
unconstrained fn transaction_fee() -> Field {
transaction_fee_opcode()
}
Expand Down Expand Up @@ -321,8 +326,9 @@ unconstrained fn sender_opcode() -> AztecAddress {}
#[oracle(avmOpcodePortal)]
unconstrained fn portal_opcode() -> EthAddress {}

#[oracle(avmOpcodeFunctionSelector)]
unconstrained fn function_selector_opcode() -> u32 {}
// UNUSED: Remove.
// #[oracle(avmOpcodeFunctionSelector)]
// unconstrained fn function_selector_opcode() -> u32 {}
Comment thread
dbanks12 marked this conversation as resolved.

#[oracle(avmOpcodeTransactionFee)]
unconstrained fn transaction_fee_opcode() -> Field {}
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 @@ -247,7 +247,8 @@ comptime fn transform_public(f: FunctionDefinition) -> Quoted {

// Unlike in the private case, in public the `context` does not need to receive the hash of the original params.
let context_creation = quote { let mut context = dep::aztec::context::public_context::PublicContext::new(|| {
let serialized_args : [Field; $args_len] = dep::aztec::context::public_context::calldata_copy(0, $args_len);
// We start from 1 because we skip the selector for the dispatch function.
let serialized_args : [Field; $args_len] = dep::aztec::context::public_context::calldata_copy(1, $args_len);
dep::aztec::hash::hash_args_array(serialized_args)
}); };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ unconstrained fn enqueue_public_function_call_oracle(
_side_effect_counter: u32,
_is_static_call: bool,
_is_delegate_call: bool
) {}
) -> Field {}

unconstrained pub fn enqueue_public_function_call_internal(
contract_address: AztecAddress,
Expand All @@ -17,15 +17,15 @@ unconstrained pub fn enqueue_public_function_call_internal(
side_effect_counter: u32,
is_static_call: bool,
is_delegate_call: bool
) {
) -> Field {
enqueue_public_function_call_oracle(
contract_address,
function_selector,
args_hash,
side_effect_counter,
is_static_call,
is_delegate_call
);
)
}

#[oracle(setPublicTeardownFunctionCall)]
Expand All @@ -36,7 +36,7 @@ unconstrained fn set_public_teardown_function_call_oracle(
_side_effect_counter: u32,
_is_static_call: bool,
_is_delegate_call: bool
) {}
) -> Field {}

unconstrained pub fn set_public_teardown_function_call_internal(
contract_address: AztecAddress,
Expand All @@ -45,15 +45,15 @@ unconstrained pub fn set_public_teardown_function_call_internal(
side_effect_counter: u32,
is_static_call: bool,
is_delegate_call: bool
) {
) -> Field {
set_public_teardown_function_call_oracle(
contract_address,
function_selector,
args_hash,
side_effect_counter,
is_static_call,
is_delegate_call
);
)
}

pub fn notify_set_min_revertible_side_effect_counter(counter: u32) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::hash::hash_args;

use crate::note::{note_header::NoteHeader, note_interface::{NoteInterface, NullifiableNote}};
use crate::oracle::{execution::{get_block_number, get_contract_address}, notes::notify_created_note};
use protocol_types::constants::PUBLIC_DISPATCH_SELECTOR;

pub struct TestEnvironment {}

Expand Down Expand Up @@ -142,9 +143,11 @@ impl TestEnvironment {
let original_contract_address = get_contract_address();
let original_fn_selector = cheatcodes::get_function_selector();
let target_address = call_interface.get_contract_address();
let fn_selector = call_interface.get_selector();
let calldata = call_interface.get_args();

// Public functions are routed through the dispatch function.
let fn_selector = FunctionSelector::from_field(PUBLIC_DISPATCH_SELECTOR);
let calldata = &[call_interface.get_selector().to_field()].append(calldata);
cheatcodes::set_fn_selector(fn_selector);
cheatcodes::set_contract_address(target_address);
cheatcodes::set_msg_sender(original_contract_address);
Expand Down
7 changes: 5 additions & 2 deletions noir-projects/aztec-nr/aztec/src/test/helpers/utils.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use dep::protocol_types::{
traits::{Deserialize, Serialize}, address::AztecAddress,
abis::{private_circuit_public_inputs::PrivateCircuitPublicInputs},
abis::{function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs},
contract_instance::ContractInstance
};

Expand All @@ -10,6 +10,7 @@ use crate::test::helpers::cheatcodes;
use crate::keys::public_keys::{PUBLIC_KEYS_LENGTH, PublicKeys};

use crate::oracle::{execution::{get_block_number, get_contract_address}};
use protocol_types::constants::PUBLIC_DISPATCH_SELECTOR;

unconstrained pub fn apply_side_effects_private(contract_address: AztecAddress, public_inputs: PrivateCircuitPublicInputs) {
let mut nullifiers = &[];
Expand Down Expand Up @@ -83,7 +84,9 @@ impl<let N: u32, let M: u32> Deployer<N, M> {
let original_fn_selector = cheatcodes::get_function_selector();
let calldata = call_interface.get_args();

cheatcodes::set_fn_selector(call_interface.get_selector());
let fn_selector = FunctionSelector::from_field(PUBLIC_DISPATCH_SELECTOR);
let calldata = &[call_interface.get_selector().to_field()].append(calldata);
cheatcodes::set_fn_selector(fn_selector);
cheatcodes::set_contract_address(instance.to_address());
cheatcodes::set_msg_sender(original_contract_address);
cheatcodes::set_is_static_call(call_interface.get_is_static());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,11 @@ contract AvmTest {
AvmTest::at(nestedAddress).new_nullifier(nullifier + 1).call(&mut context);
}

#[public]
fn nested_call_to_assert_same(arg_a: Field, arg_b: Field) -> pub Field {
AvmTest::at(context.this_address()).assert_same(arg_a, arg_b).call(&mut context)
}

/**
* Enqueue a public call from private
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ global GENESIS_ARCHIVE_ROOT: Field = 0x1200a06aae1368abe36530b585bd7a4d2ba4de503
// The following and the value in `deploy_l1_contracts` must match. We should not have the code both places, but
// we are running into circular dependency issues. #3342
global FEE_JUICE_INITIAL_MINT: Field = 20000000000;
// Last 4 bytes of the Poseidon2 hash of 'public_dispatch(Field)'.
global PUBLIC_DISPATCH_SELECTOR: Field = 0xd5441b0d;

// CONTRACT CLASS CONSTANTS
global MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS: u32 = 20000;
Expand Down
1 change: 1 addition & 0 deletions yarn-project/circuits.js/src/constants.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export const AZTEC_EPOCH_DURATION = 48;
export const AZTEC_TARGET_COMMITTEE_SIZE = 48;
export const GENESIS_ARCHIVE_ROOT = 8142738430000951296386584486068033372964809139261822027365426310856631083550n;
export const FEE_JUICE_INITIAL_MINT = 20000000000;
export const PUBLIC_DISPATCH_SELECTOR = 3578010381;
export const MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 20000;
export const MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000;
export const MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 3000;
Expand Down
8 changes: 5 additions & 3 deletions yarn-project/end-to-end/src/e2e_ordering.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,16 @@ describe('e2e_ordering', () => {
});

// The enqueued public calls are in the expected order based on the argument they set (stack is reversed!)
expect(enqueuedPublicCalls.map(c => c.args[0].toBigInt())).toEqual([...expectedOrder].reverse());
// args[1] is used instead of args[0] because public functions are routed through the public dispatch
// function and args[0] is the target function selector.
expect(enqueuedPublicCalls.map(c => c.args[1].toBigInt())).toEqual([...expectedOrder].reverse());

// Logs are emitted in the expected order
await expectLogsFromLastBlockToBe(expectedOrder);

// The final value of the child is the last one set
const value = await pxe.getPublicStorageAt(child.address, new Fr(1));
expect(value.value).toBe(expectedOrder[1]); // final state should match last value set
expect(value.toBigInt()).toBe(expectedOrder[1]); // final state should match last value set
},
);
});
Expand All @@ -106,7 +108,7 @@ describe('e2e_ordering', () => {
await child.methods[method]().send().wait();

const value = await pxe.getPublicStorageAt(child.address, new Fr(1));
expect(value.value).toBe(expectedOrder[expectedOrder.length - 1]); // final state should match last value set
expect(value.toBigInt()).toBe(expectedOrder[expectedOrder.length - 1]); // final state should match last value set
});

it.each([
Expand Down
9 changes: 6 additions & 3 deletions yarn-project/sequencer-client/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,16 +189,19 @@ function getDefaultAllowedSetupFunctions(): AllowedElement[] {
// needed for claiming on the same tx as a spend
{
address: FeeJuiceAddress,
selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'),
// We can't restrict the selector because public functions get routed via dispatch.
// selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'),
},
// needed for private transfers via FPC
{
classId: getContractClassFromArtifact(TokenContractArtifact).id,
selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'),
// We can't restrict the selector because public functions get routed via dispatch.
// selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'),
},
{
classId: getContractClassFromArtifact(FPCContract.artifact).id,
selector: FunctionSelector.fromSignature('prepare_fee((Field),Field,(Field),Field)'),
// We can't restrict the selector because public functions get routed via dispatch.
// selector: FunctionSelector.fromSignature('prepare_fee((Field),Field,(Field),Field)'),
},
];
}
Expand Down
Loading