Skip to content

Commit

Permalink
refactor: Optimize private call stack item hash for gate count (#7285)
Browse files Browse the repository at this point in the history
Fixes #7092.

In particular notice the costs around entrypoints 👀 50% reduction,
cutting away 1 million constraints.
  • Loading branch information
LHerskind authored Jul 5, 2024
1 parent 7342873 commit 783d9b6
Show file tree
Hide file tree
Showing 21 changed files with 248 additions and 52 deletions.
12 changes: 6 additions & 6 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,17 @@ library Constants {
uint256 internal constant NULLIFIER_LENGTH = 3;
uint256 internal constant SCOPED_NULLIFIER_LENGTH = 4;
uint256 internal constant CALLER_CONTEXT_LENGTH = 3;
uint256 internal constant PRIVATE_CALL_REQUEST_LENGTH = 6;
uint256 internal constant SCOPED_PRIVATE_CALL_REQUEST_LENGTH = 7;
uint256 internal constant PRIVATE_CALL_REQUEST_LENGTH = 16;
uint256 internal constant SCOPED_PRIVATE_CALL_REQUEST_LENGTH = 17;
uint256 internal constant ROLLUP_VALIDATION_REQUESTS_LENGTH = 2;
uint256 internal constant STATE_REFERENCE_LENGTH = 8;
uint256 internal constant TX_CONTEXT_LENGTH = 9;
uint256 internal constant TX_REQUEST_LENGTH = 13;
uint256 internal constant TOTAL_FEES_LENGTH = 1;
uint256 internal constant HEADER_LENGTH = 23;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 393;
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 433;
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 482;
uint256 internal constant PRIVATE_CALL_STACK_ITEM_LENGTH = 396;
uint256 internal constant PRIVATE_CALL_STACK_ITEM_LENGTH = 436;
uint256 internal constant PUBLIC_CONTEXT_INPUTS_LENGTH = 41;
uint256 internal constant AGGREGATION_OBJECT_LENGTH = 16;
uint256 internal constant SCOPED_READ_REQUEST_LEN = 3;
Expand All @@ -166,8 +166,8 @@ library Constants {
uint256 internal constant COMBINED_ACCUMULATED_DATA_LENGTH = 333;
uint256 internal constant COMBINED_CONSTANT_DATA_LENGTH = 40;
uint256 internal constant CALL_REQUEST_LENGTH = 7;
uint256 internal constant PRIVATE_ACCUMULATED_DATA_LENGTH = 1152;
uint256 internal constant PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2227;
uint256 internal constant PRIVATE_ACCUMULATED_DATA_LENGTH = 1232;
uint256 internal constant PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2307;
uint256 internal constant PUBLIC_ACCUMULATED_DATA_LENGTH = 983;
uint256 internal constant PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 3258;
uint256 internal constant KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 383;
Expand Down
11 changes: 10 additions & 1 deletion noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,16 @@ impl PrivateContext {
caller_context.storage_contract_address = self.inputs.call_context.storage_contract_address;
}
self.private_call_requests.push(
PrivateCallRequest { hash: item.hash(), caller_context, start_side_effect_counter, end_side_effect_counter }
PrivateCallRequest {
target: item.contract_address,
call_context: item.public_inputs.call_context,
function_data: item.function_data,
args_hash: item.public_inputs.args_hash,
returns_hash: item.public_inputs.returns_hash,
caller_context,
start_side_effect_counter,
end_side_effect_counter
}
);

PackedReturns::new(item.public_inputs.returns_hash)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ impl PrivateCallDataValidator {
let caller_contract_address = scoped_call_request.contract_address;
let request = scoped_call_request.call_request;

assert_eq(
request.hash, call_stack_item.hash(), "calculated private_call_hash does not match provided private_call_hash at the top of the call stack"
assert(
request.matches_stack_item(call_stack_item), "calculated private_call_hash does not match provided private_call_hash at the top of the call stack"
);

let call_context = call_stack_item.public_inputs.call_context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,7 @@ mod tests {
pub fn execute(&mut self) -> PrivateKernelCircuitPublicInputs {
let private_call = self.private_call.to_private_call_data();
// Update the previous_kernel's private_call_stack with the current call_stack_item.
let hash = private_call.call_stack_item.hash();
let is_delegate_call = private_call.call_stack_item.public_inputs.call_context.is_delegate_call;
self.previous_kernel.add_private_call_request(hash, is_delegate_call);
self.previous_kernel.add_private_call_from_call_stack_item(private_call.call_stack_item);
let previous_kernel = self.previous_kernel.to_private_kernel_data();

let kernel = PrivateKernelInnerCircuitPrivateInputs { previous_kernel, private_call };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn validate_against_call_request_mismatch_hash_fails() {

let mut request = builder.private_call.build_private_call_request();
// Tweak the hash to be a different value.
request.call_request.hash += 1;
request.call_request.args_hash += 1;

builder.validate_against_call_request(request);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl PrivateKernelCircuitOutputValidatorBuilder {
// Append one private call request for the current call.
let num_private_call_requests = self.previous_kernel.private_call_requests.len();
previous_kernel.end.private_call_stack[num_private_call_requests] = ScopedPrivateCallRequest::empty();
previous_kernel.end.private_call_stack[num_private_call_requests].call_request.hash = 98765432;
previous_kernel.end.private_call_stack[num_private_call_requests].call_request.args_hash = 98765432;

let previous_kernel_array_lengths = PrivateKernelCircuitPublicInputsArrayLengths::new(previous_kernel);
let private_call = self.private_call.to_private_call_data();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl PrivateKernelCircuitPublicInputsComposerBuilder {
let mut previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs();
let num_private_call_requests = self.previous_kernel.private_call_requests.len();
previous_kernel.end.private_call_stack[num_private_call_requests] = ScopedPrivateCallRequest::empty();
previous_kernel.end.private_call_stack[num_private_call_requests].call_request.hash = 98765432;
previous_kernel.end.private_call_stack[num_private_call_requests].call_request.args_hash = 98765432;

let private_call = self.private_call.to_private_call_data();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
use crate::{
abis::{caller_context::CallerContext, side_effect::{Ordered, RangeOrdered, Scoped}},
abis::{
private_call_stack_item::PrivateCallStackItem, call_context::CallContext,
function_data::FunctionData, caller_context::CallerContext,
side_effect::{Ordered, RangeOrdered, Scoped}
},
address::AztecAddress, constants::{PRIVATE_CALL_REQUEST_LENGTH, SCOPED_PRIVATE_CALL_REQUEST_LENGTH},
traits::{Empty, Serialize, Deserialize}, utils::reader::Reader
};

struct PrivateCallRequest {
hash: Field,
target: AztecAddress,
call_context: CallContext,
function_data: FunctionData,
args_hash: Field,
returns_hash: Field,
caller_context: CallerContext,
start_side_effect_counter: u32,
end_side_effect_counter: u32,
Expand All @@ -28,7 +36,11 @@ impl RangeOrdered for PrivateCallRequest {

impl Eq for PrivateCallRequest {
fn eq(self, other: PrivateCallRequest) -> bool {
(self.hash == other.hash)
(self.target == other.target)
& (self.call_context == other.call_context)
& (self.function_data == other.function_data)
& (self.args_hash == other.args_hash)
& (self.returns_hash == other.returns_hash)
& (self.caller_context == other.caller_context)
& (self.start_side_effect_counter == other.start_side_effect_counter)
& (self.end_side_effect_counter == other.end_side_effect_counter)
Expand All @@ -38,7 +50,11 @@ impl Eq for PrivateCallRequest {
impl Empty for PrivateCallRequest {
fn empty() -> Self {
PrivateCallRequest {
hash: 0,
target: AztecAddress::empty(),
call_context: CallContext::empty(),
function_data: FunctionData::empty(),
args_hash: 0,
returns_hash: 0,
caller_context: CallerContext::empty(),
start_side_effect_counter: 0,
end_side_effect_counter: 0,
Expand All @@ -50,7 +66,11 @@ impl Serialize<PRIVATE_CALL_REQUEST_LENGTH> for PrivateCallRequest {
fn serialize(self) -> [Field; PRIVATE_CALL_REQUEST_LENGTH] {
let mut fields: BoundedVec<Field, PRIVATE_CALL_REQUEST_LENGTH> = BoundedVec::new();

fields.push(self.hash);
fields.push(self.target.to_field());
fields.extend_from_array(self.call_context.serialize());
fields.extend_from_array(self.function_data.serialize());
fields.push(self.args_hash);
fields.push(self.returns_hash);
fields.extend_from_array(self.caller_context.serialize());
fields.push(self.start_side_effect_counter as Field);
fields.push(self.end_side_effect_counter as Field);
Expand All @@ -65,7 +85,11 @@ impl Deserialize<PRIVATE_CALL_REQUEST_LENGTH> for PrivateCallRequest {
fn deserialize(fields: [Field; PRIVATE_CALL_REQUEST_LENGTH]) -> PrivateCallRequest {
let mut reader = Reader::new(fields);
let item = PrivateCallRequest {
hash: reader.read(),
target: reader.read_struct(AztecAddress::deserialize),
call_context: reader.read_struct(CallContext::deserialize),
function_data: reader.read_struct(FunctionData::deserialize),
args_hash: reader.read(),
returns_hash: reader.read(),
caller_context: reader.read_struct(CallerContext::deserialize),
start_side_effect_counter: reader.read_u32(),
end_side_effect_counter: reader.read_u32(),
Expand All @@ -79,6 +103,18 @@ impl PrivateCallRequest {
pub fn scope(self, contract_address: AztecAddress) -> ScopedPrivateCallRequest {
ScopedPrivateCallRequest { call_request: self, contract_address }
}

pub fn matches_stack_item(self, stack_item: PrivateCallStackItem) -> bool {
(self.target == stack_item.contract_address)
& (self.call_context == stack_item.public_inputs.call_context)
& (self.function_data == stack_item.function_data)
& (self.args_hash == stack_item.public_inputs.args_hash)
& (self.returns_hash == stack_item.public_inputs.returns_hash)
& (self.start_side_effect_counter
== stack_item.public_inputs.start_side_effect_counter)
& (self.end_side_effect_counter
== stack_item.public_inputs.end_side_effect_counter)
}
}

struct ScopedPrivateCallRequest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,6 @@ fn empty_hash() {
let hash = item.hash();

// Value from private_call_stack_item.test.ts "computes empty item hash" test
let test_data_empty_hash = 0x157022d579f892f06461fb895cdf5550b24329e15e7a41df14f9dad582fa1bc5;
let test_data_empty_hash = 0x2dacef1b7e5d66445049df33ac3edad6689d86c83c1f11bb2bdaa4748309fd9f;
assert_eq(hash, test_data_empty_hash);
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,6 @@ fn empty_hash() {
let inputs = PrivateCircuitPublicInputs::empty();
let hash = inputs.hash();
// Value from private_circuit_public_inputs.test.ts "computes empty item hash" test
let test_data_empty_hash = 0x1eb5048b5bdcea5ba66519ecd1cbdb9e18fd957d52830b2bcb309f4ce9bcfbd3;
let test_data_empty_hash = 0x25748c9f85233dc2faf9eb73b1d2772d0fcb101075845a4f507baa837dc24f6b;
assert_eq(hash, test_data_empty_hash);
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ global SCOPED_NOTE_HASH_LENGTH = NOTE_HASH_LENGTH + 1;
global NULLIFIER_LENGTH = 3;
global SCOPED_NULLIFIER_LENGTH = NULLIFIER_LENGTH + 1;
global CALLER_CONTEXT_LENGTH = 2 * AZTEC_ADDRESS_LENGTH + 1;
global PRIVATE_CALL_REQUEST_LENGTH = 3 + CALLER_CONTEXT_LENGTH;
global PRIVATE_CALL_REQUEST_LENGTH = AZTEC_ADDRESS_LENGTH + CALL_CONTEXT_LENGTH + FUNCTION_DATA_LENGTH + 4 + CALLER_CONTEXT_LENGTH;
global SCOPED_PRIVATE_CALL_REQUEST_LENGTH = PRIVATE_CALL_REQUEST_LENGTH + AZTEC_ADDRESS_LENGTH;
global ROLLUP_VALIDATION_REQUESTS_LENGTH = MAX_BLOCK_NUMBER_LENGTH;
global STATE_REFERENCE_LENGTH: u32 = APPEND_ONLY_TREE_SNAPSHOT_LENGTH + PARTIAL_STATE_REFERENCE_LENGTH;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ impl FixtureBuilder {
}

pub fn build_private_call_request(self) -> ScopedPrivateCallRequest {
let hash = self.to_private_call_stack_item().hash();
let item = self.to_private_call_stack_item();
let is_delegate_call = self.is_delegate_call;
let mut caller_context = CallerContext::empty();
caller_context.is_static_call = self.is_static_call;
Expand All @@ -226,7 +226,11 @@ impl FixtureBuilder {
caller_context.storage_contract_address = self.storage_contract_address;
};
PrivateCallRequest {
hash,
target: item.contract_address,
call_context: item.public_inputs.call_context,
function_data: item.function_data,
args_hash: item.public_inputs.args_hash,
returns_hash: item.public_inputs.returns_hash,
caller_context,
start_side_effect_counter: self.counter_start,
end_side_effect_counter: self.counter
Expand Down Expand Up @@ -784,7 +788,33 @@ impl FixtureBuilder {
self.unencrypted_log_preimages_length = preimages_length;
}

pub fn add_private_call_request(&mut self, hash: Field, is_delegate_call: bool) {
pub fn add_private_call_from_call_stack_item(&mut self, item: PrivateCallStackItem) {
let mut caller_context = CallerContext::empty();
let is_delegate_call = item.public_inputs.call_context.is_delegate_call;
caller_context.is_static_call = self.is_static_call;
if is_delegate_call {
caller_context.msg_sender = self.msg_sender;
caller_context.storage_contract_address = self.storage_contract_address;
}
let start_counter = item.public_inputs.start_side_effect_counter;
let end_counter = item.public_inputs.end_side_effect_counter;
self.counter = end_counter + 1;

self.private_call_requests.push(
PrivateCallRequest {
target: item.contract_address,
call_context: item.public_inputs.call_context,
function_data: item.function_data,
args_hash: item.public_inputs.args_hash,
returns_hash: item.public_inputs.returns_hash,
caller_context,
start_side_effect_counter: start_counter,
end_side_effect_counter: end_counter
}.scope(self.contract_address)
);
}

pub fn add_private_call_request(&mut self, args_hash: Field, is_delegate_call: bool) {
let mut caller_context = CallerContext::empty();
caller_context.is_static_call = self.is_static_call;
if is_delegate_call {
Expand All @@ -794,8 +824,23 @@ impl FixtureBuilder {
let start_counter = self.next_counter();
let end_counter = start_counter + 10;
self.counter = end_counter + 1;

let target = AztecAddress::empty();
let call_context = CallContext::empty();
let function_data = FunctionData::empty();
let returns_hash = 0;

self.private_call_requests.push(
PrivateCallRequest { hash, caller_context, start_side_effect_counter: start_counter, end_side_effect_counter: end_counter }.scope(self.contract_address)
PrivateCallRequest {
target,
call_context,
function_data,
args_hash,
returns_hash,
caller_context,
start_side_effect_counter: start_counter,
end_side_effect_counter: end_counter
}.scope(self.contract_address)
);
}

Expand Down
12 changes: 6 additions & 6 deletions yarn-project/circuits.js/src/constants.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,17 @@ export const SCOPED_NOTE_HASH_LENGTH = 4;
export const NULLIFIER_LENGTH = 3;
export const SCOPED_NULLIFIER_LENGTH = 4;
export const CALLER_CONTEXT_LENGTH = 3;
export const PRIVATE_CALL_REQUEST_LENGTH = 6;
export const SCOPED_PRIVATE_CALL_REQUEST_LENGTH = 7;
export const PRIVATE_CALL_REQUEST_LENGTH = 16;
export const SCOPED_PRIVATE_CALL_REQUEST_LENGTH = 17;
export const ROLLUP_VALIDATION_REQUESTS_LENGTH = 2;
export const STATE_REFERENCE_LENGTH = 8;
export const TX_CONTEXT_LENGTH = 9;
export const TX_REQUEST_LENGTH = 13;
export const TOTAL_FEES_LENGTH = 1;
export const HEADER_LENGTH = 23;
export const PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 393;
export const PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 433;
export const PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 482;
export const PRIVATE_CALL_STACK_ITEM_LENGTH = 396;
export const PRIVATE_CALL_STACK_ITEM_LENGTH = 436;
export const PUBLIC_CONTEXT_INPUTS_LENGTH = 41;
export const AGGREGATION_OBJECT_LENGTH = 16;
export const SCOPED_READ_REQUEST_LEN = 3;
Expand All @@ -150,8 +150,8 @@ export const PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3;
export const COMBINED_ACCUMULATED_DATA_LENGTH = 333;
export const COMBINED_CONSTANT_DATA_LENGTH = 40;
export const CALL_REQUEST_LENGTH = 7;
export const PRIVATE_ACCUMULATED_DATA_LENGTH = 1152;
export const PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2227;
export const PRIVATE_ACCUMULATED_DATA_LENGTH = 1232;
export const PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2307;
export const PUBLIC_ACCUMULATED_DATA_LENGTH = 983;
export const PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 3258;
export const KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 383;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`PrivateCallStackItem computes empty item hash 1`] = `Fr<0x157022d579f892f06461fb895cdf5550b24329e15e7a41df14f9dad582fa1bc5>`;
exports[`PrivateCallStackItem computes empty item hash 1`] = `Fr<0x2dacef1b7e5d66445049df33ac3edad6689d86c83c1f11bb2bdaa4748309fd9f>`;

exports[`PrivateCallStackItem computes hash 1`] = `Fr<0x029b1573da033e679c68a55e05987602c5e73419c4fdfdd6104e178a9a360834>`;
exports[`PrivateCallStackItem computes hash 1`] = `Fr<0x17171557d06226fae88f7e2b57e23acd8dd767160e5149965ca0f4bebbdedb06>`;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`PrivateCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x1eb5048b5bdcea5ba66519ecd1cbdb9e18fd957d52830b2bcb309f4ce9bcfbd3>`;
exports[`PrivateCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x25748c9f85233dc2faf9eb73b1d2772d0fcb101075845a4f507baa837dc24f6b>`;

exports[`PrivateCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x0b02f49b7283dacf553c5cfc0615c979fdd1cd146a1d4e71c78610345a711d22>`;
exports[`PrivateCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x0d8c2da0c15b53e26d2f612cec5d0ac71ce21fb6e1b00caf3e8aa5a8bb02ad65>`;
7 changes: 6 additions & 1 deletion yarn-project/circuits.js/src/structs/call_context.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { FunctionSelector } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { pedersenHash } from '@aztec/foundation/crypto';
import { Fr } from '@aztec/foundation/fields';
import { BufferReader, FieldReader, serializeToBuffer, serializeToFields } from '@aztec/foundation/serialize';
import { type FieldsOf } from '@aztec/foundation/types';

import { CALL_CONTEXT_LENGTH } from '../constants.gen.js';
import { CALL_CONTEXT_LENGTH, GeneratorIndex } from '../constants.gen.js';

/**
* Call context.
Expand Down Expand Up @@ -125,4 +126,8 @@ export class CallContext {
callContext.sideEffectCounter === this.sideEffectCounter
);
}

hash(): Fr {
return pedersenHash(this.toFields(), GeneratorIndex.CALL_CONTEXT);
}
}
9 changes: 9 additions & 0 deletions yarn-project/circuits.js/src/structs/function_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ export class FunctionData {
return this.selector.isEmpty();
}

/**
* Returns whether this instance is equal to another.
* @param other
* @returns
*/
equals(other: FunctionData): boolean {
return this.selector.equals(other.selector) && this.isPrivate === other.isPrivate;
}

/**
* Returns a new instance of FunctionData with zero function selector.
* @param args - Arguments to pass to the constructor.
Expand Down
Loading

0 comments on commit 783d9b6

Please sign in to comment.