Skip to content

Commit

Permalink
feat: delegate calls (#4586)
Browse files Browse the repository at this point in the history
Implemented delegatecall ETH-style with contract instances, pending
discussion regarding contract classes.

Fixed usage of `storageContractAddress` in many places and creation of
CallRequests with proper context information.

Better tests and docs are coming! Trying to get at least the
functionality merged before the offsite.
  • Loading branch information
Thunkar authored and AztecBot committed Mar 19, 2024
1 parent d00fb6d commit 847f087
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 48 deletions.
2 changes: 2 additions & 0 deletions authwit/src/entrypoint/app.nr
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,15 @@ impl AppPayload {
call.target_address,
call.function_selector,
call.args_hash,
false,
false
);
} else {
let _result = context.call_private_function_with_packed_args(
call.target_address,
call.function_selector,
call.args_hash,
false,
false
);
}
Expand Down
2 changes: 2 additions & 0 deletions authwit/src/entrypoint/fee.nr
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ impl FeePayload {
call.target_address,
call.function_selector,
call.args_hash,
false,
false
);
} else {
let _result = context.call_private_function_with_packed_args(
call.target_address,
call.function_selector,
call.args_hash,
false,
false
);
}
Expand Down
122 changes: 89 additions & 33 deletions aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -251,49 +251,70 @@ impl PrivateContext {
) -> [Field; RETURN_VALUES_LENGTH] {
let args_hash = hash_args(args);
assert(args_hash == arguments::pack_arguments(args));
self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, false)
self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, false, false)
}

pub fn call_private_function_static<ARGS_COUNT>(
pub fn static_call_private_function<ARGS_COUNT>(
&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));
self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, true)
self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, true, false)
}

pub fn delegate_call_private_function<ARGS_COUNT>(
&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));
self.call_private_function_with_packed_args(contract_address, function_selector, args_hash, false, true)
}

pub fn call_private_function_no_args(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
self.call_private_function_with_packed_args(contract_address, function_selector, 0, false)
self.call_private_function_with_packed_args(contract_address, function_selector, 0, false, false)
}

pub fn static_call_private_function_no_args(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
self.call_private_function_with_packed_args(contract_address, function_selector, 0, true, false)
}

pub fn call_private_function_no_args_static(
pub fn delegate_call_private_function_no_args<ARGS_COUNT>(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
self.call_private_function_with_packed_args(contract_address, function_selector, 0, true)
self.call_private_function_with_packed_args(contract_address, function_selector, 0, false, true)
}

fn call_private_function_with_packed_args(
pub fn call_private_function_with_packed_args(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args_hash: Field,
is_static_call: bool
is_static_call: bool,
is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {
let item = call_private_function_internal(
contract_address,
function_selector,
args_hash,
self.side_effect_counter,
is_static_call
is_static_call,
is_delegate_call
);

assert_eq(item.public_inputs.call_context.start_side_effect_counter, self.side_effect_counter);
Expand All @@ -305,16 +326,23 @@ impl PrivateContext {
assert(args_hash == item.public_inputs.args_hash);

// Assert that the call context of the enqueued call generated by the oracle matches our request.
// We are issuing a regular call which is not delegate, static, or deployment. We also constrain
// the msg_sender in the nested call to be equal to our address, and the execution context address
// for the nested call to be equal to the address we actually called.
assert(item.public_inputs.call_context.is_delegate_call == false);
assert(item.public_inputs.call_context.is_delegate_call == is_delegate_call);
assert(item.public_inputs.call_context.is_static_call == is_static_call);
assert(item.public_inputs.call_context.is_contract_deployment == false);
assert(
item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address)
);
assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address));

if (is_delegate_call) {
// For delegate calls, we also constrain the execution context address for the nested call to be equal to our address.
assert(
item.public_inputs.call_context.storage_contract_address.eq(self.inputs.call_context.storage_contract_address)
);
assert(item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.msg_sender));
} else {
// For non-delegate calls, we also constrain the execution context address for the nested call to be equal to the address we called.
assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address));
assert(
item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address)
);
}

self.private_call_stack_hashes.push(item.hash());

Expand All @@ -329,49 +357,70 @@ impl PrivateContext {
) {
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)
self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, false, false)
}

pub fn call_public_function_static<ARGS_COUNT>(
pub fn static_call_public_function<ARGS_COUNT>(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) {
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)
self.call_public_function_with_packed_args(contract_address, function_selector, args_hash, true, false)
}

pub fn delegate_call_public_function<ARGS_COUNT>(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args: [Field; ARGS_COUNT]
) {
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)
}

pub fn call_public_function_no_args(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) {
self.call_public_function_with_packed_args(contract_address, function_selector, 0, false)
self.call_public_function_with_packed_args(contract_address, function_selector, 0, false, false)
}

pub fn static_call_public_function_no_args(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) {
self.call_public_function_with_packed_args(contract_address, function_selector, 0, true, false)
}

pub fn call_public_function_no_args_static(
pub fn delegate_call_public_function_no_args(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) {
self.call_public_function_with_packed_args(contract_address, function_selector, 0, true)
self.call_public_function_with_packed_args(contract_address, function_selector, 0, false, true)
}

pub fn call_public_function_with_packed_args(
&mut self,
contract_address: AztecAddress,
function_selector: FunctionSelector,
args_hash: Field,
is_static_call: bool
is_static_call: bool,
is_delegate_call: bool
) {
let fields = enqueue_public_function_call_internal(
contract_address,
function_selector,
args_hash,
self.side_effect_counter,
is_static_call
is_static_call,
is_delegate_call
);

let mut reader = Reader::new(fields);
Expand Down Expand Up @@ -410,16 +459,23 @@ impl PrivateContext {
assert(args_hash == item.public_inputs.args_hash);

// Assert that the call context of the enqueued call generated by the oracle matches our request.
// We are issuing a regular call which is not delegate, static, or deployment. We also constrain
// the msg_sender in the nested call to be equal to our address, and the execution context address
// for the nested call to be equal to the address we actually called.
assert(item.public_inputs.call_context.is_delegate_call == false);
assert(item.public_inputs.call_context.is_delegate_call == is_delegate_call);
assert(item.public_inputs.call_context.is_static_call == is_static_call);
assert(item.public_inputs.call_context.is_contract_deployment == false);
assert(
item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address)
);
assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address));

if (is_delegate_call) {
// For delegate calls, we also constrain the execution context address for the nested call to be equal to our address.
assert(
item.public_inputs.call_context.storage_contract_address.eq(self.inputs.call_context.storage_contract_address)
);
assert(item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.msg_sender));
} else {
// For non-delegate calls, we also constrain the execution context address for the nested call to be equal to the address we called.
assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address));
assert(
item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address)
);
}

self.public_call_stack_hashes.push(item.hash());
}
Expand Down
31 changes: 25 additions & 6 deletions aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -194,33 +194,52 @@ impl PublicContext {
) -> [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)
call_public_function_internal(contract_address, function_selector, args_hash, false, false)
}

pub fn call_public_function_static<ARGS_COUNT>(
pub fn static_call_public_function<ARGS_COUNT>(
_self: 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)
call_public_function_internal(contract_address, function_selector, args_hash, true, false)
}

pub fn delegate_call_public_function<ARGS_COUNT>(
_self: 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)
}

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

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

pub fn call_public_function_no_args_static(
pub fn delegate_call_public_function_no_args(
_self: Self,
contract_address: AztecAddress,
function_selector: FunctionSelector
) -> [Field; RETURN_VALUES_LENGTH] {
call_public_function_internal(contract_address, function_selector, 0, true)
call_public_function_internal(contract_address, function_selector, 0, false, true)
}
}
9 changes: 6 additions & 3 deletions aztec/src/oracle/call_private_function.nr
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@ fn call_private_function_oracle(
_function_selector: FunctionSelector,
_args_hash: Field,
_start_side_effect_counter: u32,
_is_static_call: bool
_is_static_call: bool,
_is_delegate_call: bool
) -> [Field; PRIVATE_CALL_STACK_ITEM_LENGTH] {}

unconstrained pub fn call_private_function_internal(
contract_address: AztecAddress,
function_selector: FunctionSelector,
args_hash: Field,
start_side_effect_counter: u32,
is_static_call: bool
is_static_call: bool,
is_delegate_call: bool
) -> PrivateCallStackItem {
let fields = call_private_function_oracle(
contract_address,
function_selector,
args_hash,
start_side_effect_counter,
is_static_call
is_static_call,
is_delegate_call
);

PrivateCallStackItem::deserialize(fields)
Expand Down
9 changes: 6 additions & 3 deletions aztec/src/oracle/enqueue_public_function_call.nr
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,24 @@ fn enqueue_public_function_call_oracle(
_function_selector: FunctionSelector,
_args_hash: Field,
_side_effect_counter: u32,
_is_static_call: bool
_is_static_call: bool,
_is_delegate_call: bool
) -> [Field; ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_SIZE] {}

unconstrained pub fn enqueue_public_function_call_internal(
contract_address: AztecAddress,
function_selector: FunctionSelector,
args_hash: Field,
side_effect_counter: u32,
is_static_call: bool
is_static_call: bool,
is_delegate_call: bool
) -> [Field; ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_SIZE] {
enqueue_public_function_call_oracle(
contract_address,
function_selector,
args_hash,
side_effect_counter,
is_static_call
is_static_call,
is_delegate_call
)
}
14 changes: 11 additions & 3 deletions aztec/src/oracle/public_call.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,22 @@ fn call_public_function_oracle(
_contract_address: AztecAddress,
_function_selector: FunctionSelector,
_args_hash: Field,
_is_static_call: bool
_is_static_call: bool,
_is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {}

unconstrained pub fn call_public_function_internal(
contract_address: AztecAddress,
function_selector: FunctionSelector,
args_hash: Field,
is_static_call: bool
is_static_call: bool,
is_delegate_call: bool
) -> [Field; RETURN_VALUES_LENGTH] {
call_public_function_oracle(contract_address, function_selector, args_hash, is_static_call)
call_public_function_oracle(
contract_address,
function_selector,
args_hash,
is_static_call,
is_delegate_call
)
}

0 comments on commit 847f087

Please sign in to comment.