Skip to content

Commit

Permalink
feat: track side effects in public (#5129)
Browse files Browse the repository at this point in the history
This PR adds start/end side effect counters to private & public context
inputs.

This solves #4958 by assigning the correct side effect counters to notes
created in setup/teardown so that when they're recombined into a single
array they can be sorted in order of creation. A few e2e tests were also
added to validate the change.

Next steps:
- track public storage reads and writes as side effects in Noir
(currently done in parallel, in TS)
- validate a call's side effects are in between its start and end
counters in the private and kernels
- when calling a public function, the public context should update its
`context.side_effect_counter` to the called function's
`end_side_effect_counter + 1` (just like the private context)
- rename `call_context.side_effect_counter` to just
`call_context.counter`

Fix #4958 
Fix #5185
  • Loading branch information
alexghr authored Mar 13, 2024
1 parent 6c70624 commit d666f6f
Show file tree
Hide file tree
Showing 70 changed files with 741 additions and 297 deletions.
6 changes: 3 additions & 3 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ library Constants {
uint256 internal constant NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = 4;
uint256 internal constant NULLIFIER_KEY_VALIDATION_REQUEST_CONTEXT_LENGTH = 5;
uint256 internal constant PARTIAL_STATE_REFERENCE_LENGTH = 6;
uint256 internal constant PRIVATE_CALL_STACK_ITEM_LENGTH = 214;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 209;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 200;
uint256 internal constant PRIVATE_CALL_STACK_ITEM_LENGTH = 215;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 210;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 202;
uint256 internal constant STATE_REFERENCE_LENGTH = 8;
uint256 internal constant TX_CONTEXT_DATA_LENGTH = 4;
uint256 internal constant TX_REQUEST_LENGTH = 10;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ struct PrivateContextInputs {
call_context : CallContext,
historical_header: Header,
private_global_variables: PrivateGlobalVariables,
start_side_effect_counter: u32,
}
// docs:end:private-context-inputs
// docs:end:private-context-inputs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ struct PublicContextInputs {
historical_header: Header,

public_global_variables: PublicGlobalVariables,

start_side_effect_counter: u32,
}
// docs:end:public-context-inputs
18 changes: 8 additions & 10 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl ContextInterface for PrivateContext {

// Returns the header of a block whose state is used during private execution (not the block the transaction is
// included in).
pub fn get_header(self) -> Header {
fn get_header(self) -> Header {
self.historical_header
}

Expand All @@ -117,7 +117,7 @@ impl ContextInterface for PrivateContext {

impl PrivateContext {
pub fn new(inputs: PrivateContextInputs, args_hash: Field) -> PrivateContext {
let side_effect_counter = inputs.call_context.start_side_effect_counter;
let side_effect_counter = inputs.start_side_effect_counter;
let mut min_revertible_side_effect_counter = 0;
if is_empty(inputs.call_context.msg_sender) {
min_revertible_side_effect_counter = side_effect_counter;
Expand Down Expand Up @@ -161,12 +161,6 @@ impl PrivateContext {
call_context: self.inputs.call_context,
args_hash: self.args_hash,
return_values: self.return_values.storage,
// TODO(fees): start this from 0 and test the following:
// - in the private circuit init that it gets set correctly
// - in the private circuit inner that it remains 0
// I've had to initialize the counter here so that it would work for contract deployments
// the above checks should be doable after we figure out fee payments for contract deployments
// TO(fees): there are no more contract deployments at the protocol level
min_revertible_side_effect_counter: self.min_revertible_side_effect_counter,
note_hash_read_requests: self.note_hash_read_requests.storage,
nullifier_read_requests: self.nullifier_read_requests.storage,
Expand All @@ -176,6 +170,7 @@ impl PrivateContext {
private_call_stack_hashes: self.private_call_stack_hashes.storage,
public_call_stack_hashes: self.public_call_stack_hashes.storage,
new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage,
start_side_effect_counter: self.inputs.start_side_effect_counter,
end_side_effect_counter: self.side_effect_counter,
encrypted_logs_hash,
unencrypted_logs_hash,
Expand Down Expand Up @@ -337,7 +332,8 @@ impl PrivateContext {
is_delegate_call
);

assert_eq(item.public_inputs.call_context.start_side_effect_counter, self.side_effect_counter);
assert_eq(item.public_inputs.call_context.side_effect_counter, self.side_effect_counter);
assert_eq(item.public_inputs.start_side_effect_counter, self.side_effect_counter);
self.side_effect_counter = item.public_inputs.end_side_effect_counter + 1;

assert(contract_address.eq(item.contract_address));
Expand Down Expand Up @@ -462,6 +458,8 @@ impl PrivateContext {
new_note_hashes: [SideEffect::empty(); MAX_NEW_NOTE_HASHES_PER_CALL],
new_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL],
new_l2_to_l1_msgs: [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL],
start_side_effect_counter: 0,
end_side_effect_counter: 0,
unencrypted_logs_hash: [0; NUM_FIELDS_PER_SHA256],
unencrypted_log_preimages_length: 0,
historical_header: Header::empty(),
Expand All @@ -475,7 +473,7 @@ impl PrivateContext {
assert(contract_address.eq(item.contract_address));
assert(function_selector.eq(item.function_data.selector));

assert_eq(item.public_inputs.call_context.start_side_effect_counter, self.side_effect_counter);
assert_eq(item.public_inputs.call_context.side_effect_counter, self.side_effect_counter);
// We increment the sideffect counter by one, to account for the call itself being a side effect.
self.side_effect_counter = self.side_effect_counter + 1;

Expand Down
50 changes: 37 additions & 13 deletions noir-projects/aztec-nr/aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl PublicContext {
pub fn new(inputs: PublicContextInputs, args_hash: Field) -> PublicContext {
PublicContext {
inputs,
side_effect_counter: inputs.call_context.start_side_effect_counter,
side_effect_counter: inputs.start_side_effect_counter,
args_hash,
return_values: BoundedVec::new(),
nullifier_read_requests: BoundedVec::new(),
Expand Down Expand Up @@ -158,6 +158,8 @@ impl PublicContext {
new_nullifiers: self.new_nullifiers.storage,
public_call_stack_hashes: self.public_call_stack_hashes.storage,
new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage,
start_side_effect_counter: self.inputs.start_side_effect_counter,
end_side_effect_counter: self.side_effect_counter,
unencrypted_logs_hash,
unencrypted_log_preimages_length,
historical_header: self.inputs.historical_header,
Expand Down Expand Up @@ -213,59 +215,81 @@ impl PublicContext {
}

pub fn call_public_function<ARGS_COUNT>(
_self: Self,
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) -> [Field; RETURN_VALUES_LENGTH] {
let args_hash = hash_args(args);
assert(args_hash == arguments::pack_arguments(args));
call_public_function_internal(contract_address, function_selector, args_hash, false, false)
self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, false, false)
}

pub fn static_call_public_function<ARGS_COUNT>(
_self: Self,
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) -> [Field; RETURN_VALUES_LENGTH] {
let args_hash = hash_args(args);
assert(args_hash == arguments::pack_arguments(args));
call_public_function_internal(contract_address, function_selector, args_hash, true, false)
self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, true, false)
}

pub fn delegate_call_public_function<ARGS_COUNT>(
_self: Self,
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) -> [Field; RETURN_VALUES_LENGTH] {
let args_hash = hash_args(args);
assert(args_hash == arguments::pack_arguments(args));
call_public_function_internal(contract_address, function_selector, args_hash, false, true)
self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, false, true)
}

pub fn call_public_function_no_args(
_self: Self,
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
call_public_function_internal(contract_address, function_selector, 0, false, false)
self.call_public_function_with_packed_args(contract_address, function_selector, 0, false, false)
}

pub fn static_call_public_function_no_args(
_self: Self,
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
call_public_function_internal(contract_address, function_selector, 0, true, false)
self.call_public_function_with_packed_args(contract_address, function_selector, 0, true, false)
}

pub fn delegate_call_public_function_no_args(
_self: Self,
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
call_public_function_internal(contract_address, function_selector, 0, false, true)
self.call_public_function_with_packed_args(contract_address, function_selector, 0, false, true)
}

pub fn call_public_function_with_packed_args(
self: &mut Self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args_hash: Field,
is_static_call: bool,
is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {
let side_effect_counter = self.side_effect_counter;
// TODO get next value from output of `call_public_function_internal`
self.side_effect_counter += 1;

call_public_function_internal(
contract_address,
function_selector,
args_hash,
side_effect_counter,
is_static_call,
is_delegate_call
)
}
}
3 changes: 3 additions & 0 deletions noir-projects/aztec-nr/aztec/src/oracle/public_call.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ fn call_public_function_oracle(
_contract_address: AztecAddress,
_function_selector: FunctionSelector,
_args_hash: Field,
_side_effect_counter: u32,
_is_static_call: bool,
_is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {}
Expand All @@ -13,13 +14,15 @@ unconstrained pub fn call_public_function_internal(
contract_address: AztecAddress,
function_selector: FunctionSelector,
args_hash: Field,
side_effect_counter: u32,
is_static_call: bool,
is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {
call_public_function_oracle(
contract_address,
function_selector,
args_hash,
side_effect_counter,
is_static_call,
is_delegate_call
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl Token {

pub fn transfer_public(
self: Self,
context: PublicContext,
context: &mut PublicContext,
from: AztecAddress,
to: AztecAddress,
amount: Field,
Expand All @@ -27,6 +27,21 @@ impl Token {
);
}

pub fn shield(
self: Self,
context: &mut PublicContext,
from: AztecAddress,
amount: Field,
secret_hash: Field,
nonce: Field
) {
let _ = context.call_public_function(
self.address,
FunctionSelector::from_signature("shield((Field),Field,Field,Field)"),
[from.to_field(), amount, secret_hash, nonce]
);
}

// Private
pub fn unshield(
self: Self,
Expand Down
22 changes: 17 additions & 5 deletions noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract FPC {
}

#[aztec(private)]
fn fee_entrypoint_private(amount: Field, asset: AztecAddress, nonce: Field) {
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(
Expand All @@ -32,8 +32,8 @@ contract FPC {

let _void = context.call_public_function(
context.this_address(),
FunctionSelector::from_signature("pay_fee((Field),Field,(Field))"),
[context.msg_sender().to_field(), amount, asset.to_field()]
FunctionSelector::from_signature("pay_fee_with_shielded_rebate(Field,(Field),Field)"),
[amount, asset.to_field(), secret_hash]
);
}

Expand All @@ -55,7 +55,7 @@ contract FPC {
#[aztec(public)]
#[aztec(internal)]
fn prepare_fee(from: AztecAddress, amount: Field, asset: AztecAddress, nonce: Field) {
let _res = Token::at(asset).transfer_public(context, from, context.this_address(), amount, nonce);
let _res = Token::at(asset).transfer_public(&mut context, from, context.this_address(), amount, nonce);
}

#[aztec(public)]
Expand All @@ -68,6 +68,18 @@ contract FPC {
)[0];

// Just do public refunds for the present
Token::at(asset).transfer_public(context, context.this_address(), refund_address, refund, 0)
Token::at(asset).transfer_public(&mut context, context.this_address(), refund_address, refund, 0)
}

#[aztec(public)]
#[aztec(internal)]
fn pay_fee_with_shielded_rebate(amount: Field, asset: AztecAddress, secretHash: Field) {
let refund = context.call_public_function(
storage.gas_token_address.read_public(),
FunctionSelector::from_signature("pay_fee(Field)"),
[amount]
)[0];

Token::at(asset).shield(&mut context, context.this_address(), refund, secretHash, 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ contract ImportTest {
#[aztec(public)]
fn pubCallOpenFn(target: AztecAddress) -> Field {
let test_contract_instance = TestPublicContextInterface::at(target);
let ret = test_contract_instance.create_nullifier_public(context, 1, 2);
let ret = test_contract_instance.create_nullifier_public(&mut context, 1, 2);

ret[0]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl PriceFeed {
Self { address }
}

pub fn get_price(self: Self, context: PublicContext) -> U128 {
pub fn get_price(self: Self, context: &mut PublicContext) -> U128 {
let return_values = context.call_public_function(
self.address,
FunctionSelector::from_signature("get_price(Field)"),
Expand All @@ -36,21 +36,21 @@ impl Token {

pub fn transfer_public(
self: Self,
context: PublicContext,
context: &mut PublicContext,
from: AztecAddress,
to: AztecAddress,
amount: Field,
nonce: Field
) {
context.call_public_function(
let _ = context.call_public_function(
self.address,
FunctionSelector::from_signature("transfer_public((Field),(Field),Field,Field)"),
[from.to_field(), to.to_field(), amount, nonce]
);
}

pub fn mint_public(self: Self, context: PublicContext, to: AztecAddress, amount: Field) {
context.call_public_function(
pub fn mint_public(self: Self, context: &mut PublicContext, to: AztecAddress, amount: Field) {
let _ = context.call_public_function(
self.address,
FunctionSelector::from_signature("mint_public((Field),Field)"),
[to.to_field(), amount]
Expand All @@ -59,12 +59,12 @@ impl Token {

pub fn burn_public(
self: Self,
context: PublicContext,
context: &mut PublicContext,
from: AztecAddress,
amount: Field,
nonce: Field
) {
context.call_public_function(
let _ = context.call_public_function(
self.address,
FunctionSelector::from_signature("burn_public((Field),Field,Field)"),
[from.to_field(), amount, nonce]
Expand Down Expand Up @@ -111,7 +111,7 @@ impl Lending {
Self { address }
}

pub fn update_accumulator(self: Self, context: PublicContext) -> Asset {
pub fn update_accumulator(self: Self, context: &mut PublicContext) -> Asset {
let return_values = context.call_public_function_no_args(
self.address,
FunctionSelector::from_signature("update_accumulator()")
Expand Down
Loading

0 comments on commit d666f6f

Please sign in to comment.