Skip to content

Commit

Permalink
refactor: use capsule for emitting unconstrained and private functions
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed Jul 3, 2024
1 parent 75a1d4a commit 3a76ea8
Show file tree
Hide file tree
Showing 13 changed files with 82 additions and 44 deletions.
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ library Constants {
uint256 internal constant DEPLOYER_CONTRACT_ADDRESS =
19511485909966796736993840362353440247573331327062358513665772226446629198132;
uint256 internal constant REGISTERER_CONTRACT_ADDRESS =
21791696151759019003097706094037044371210776294983020497737005968946992649239;
13402924717071282069537366635406026232165444473509746327951838324587448220160;
uint256 internal constant GAS_TOKEN_ADDRESS =
3159976153131520272419617514531889581796079438158800470341967144801191524489;
uint256 internal constant AZTEC_ADDRESS_LENGTH = 1;
Expand Down
8 changes: 4 additions & 4 deletions noir-projects/aztec-nr/aztec/src/hash.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use dep::protocol_types::{
address::{AztecAddress, EthAddress},
constants::{
GENERATOR_INDEX__SECRET_HASH, GENERATOR_INDEX__MESSAGE_NULLIFIER, ARGS_HASH_CHUNK_COUNT,
GENERATOR_INDEX__FUNCTION_ARGS, ARGS_HASH_CHUNK_LENGTH
GENERATOR_INDEX__FUNCTION_ARGS, ARGS_HASH_CHUNK_LENGTH, MAX_ARGS_LENGTH
},
traits::Hash, hash::{pedersen_hash, compute_siloed_nullifier, sha256_to_field}
};
Expand Down Expand Up @@ -112,7 +112,7 @@ pub fn hash_args(args: [Field]) -> Field {
if args.len() == 0 {
0
} else {
assert(args.len() < ARGS_HASH_CHUNK_COUNT * ARGS_HASH_CHUNK_LENGTH);
assert(args.len() < MAX_ARGS_LENGTH);
let mut chunks_hashes = [0; ARGS_HASH_CHUNK_COUNT];
let mut current_chunk_values = [0; ARGS_HASH_CHUNK_LENGTH];

Expand All @@ -138,11 +138,11 @@ pub fn hash_args(args: [Field]) -> Field {
#[test]
fn compute_var_args_hash() {
let mut input = ArgsHasher::new();
for i in 0..800 {
for i in 0..MAX_ARGS_LENGTH {
input.add(i as Field);
}
let hash = input.hash();
assert(hash == 0x05a1023fef839ac88731f49ae983e172c1b600a3c8f3393ad0ac25d819ac0f0f);
assert(hash == 0x11e40f2a780822f7971803048c9a2100579de352e7dadd99981760964da65b57);
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ use dep::aztec::protocol_types::{
traits::Serialize
};

struct InnerPrivateFunction {
selector: FunctionSelector,
metadata_hash: Field,
vk_hash: Field,
}

impl Serialize<3> for InnerPrivateFunction {
fn serialize(self: Self) -> [Field; 3] {
[self.selector.to_field(), self.metadata_hash, self.vk_hash]
}
}

struct PrivateFunction {
selector: FunctionSelector,
metadata_hash: Field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ use dep::aztec::protocol_types::{
traits::Serialize
};

struct InnerUnconstrainedFunction {
selector: FunctionSelector,
metadata_hash: Field,
}

impl Serialize<2> for InnerUnconstrainedFunction {
fn serialize(self: Self) -> [Field; 2] {
[ self.selector.to_field(), self.metadata_hash]
}
}

struct UnconstrainedFunction {
selector: FunctionSelector,
metadata_hash: Field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ contract ContractClassRegisterer {
contract_class_id::ContractClassId,
constants::{
ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, FUNCTION_TREE_HEIGHT,
MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS,
MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS,
MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE
},
traits::Serialize, abis::log_hash::LogHash
Expand All @@ -16,8 +18,8 @@ contract ContractClassRegisterer {

use crate::events::{
class_registered::ContractClassRegistered,
private_function_broadcasted::{ClassPrivateFunctionBroadcasted, PrivateFunction},
unconstrained_function_broadcasted::{ClassUnconstrainedFunctionBroadcasted, UnconstrainedFunction}
private_function_broadcasted::{ClassPrivateFunctionBroadcasted, PrivateFunction, InnerPrivateFunction},
unconstrained_function_broadcasted::{ClassUnconstrainedFunctionBroadcasted, UnconstrainedFunction, InnerUnconstrainedFunction}
};

// docs:start:import_pop_capsule
Expand Down Expand Up @@ -66,8 +68,10 @@ contract ContractClassRegisterer {
private_function_tree_leaf_index: Field,
artifact_function_tree_sibling_path: [Field; ARTIFACT_FUNCTION_TREE_MAX_HEIGHT],
artifact_function_tree_leaf_index: Field,
function_data: PrivateFunction
function_data: InnerPrivateFunction
) {
let private_bytecode: [Field; MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS] = pop_capsule();

let event = ClassPrivateFunctionBroadcasted {
contract_class_id,
artifact_metadata_hash,
Expand All @@ -76,7 +80,12 @@ contract ContractClassRegisterer {
private_function_tree_leaf_index,
artifact_function_tree_sibling_path,
artifact_function_tree_leaf_index,
function: function_data
function: PrivateFunction {
selector: function_data.selector,
metadata_hash: function_data.metadata_hash,
vk_hash: function_data.vk_hash,
bytecode: private_bytecode
}
};
dep::aztec::oracle::debug_log::debug_log_format(
"ClassPrivateFunctionBroadcasted: {}",
Expand All @@ -99,15 +108,20 @@ contract ContractClassRegisterer {
private_functions_artifact_tree_root: Field,
artifact_function_tree_sibling_path: [Field; ARTIFACT_FUNCTION_TREE_MAX_HEIGHT],
artifact_function_tree_leaf_index: Field,
function_data: UnconstrainedFunction
function_data: InnerUnconstrainedFunction
) {
let unconstrained_bytecode: [Field; MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS] = pop_capsule();
let event = ClassUnconstrainedFunctionBroadcasted {
contract_class_id,
artifact_metadata_hash,
private_functions_artifact_tree_root,
artifact_function_tree_sibling_path,
artifact_function_tree_leaf_index,
function: function_data
function: UnconstrainedFunction {
selector: function_data.selector,
metadata_hash: function_data.metadata_hash,
bytecode: unconstrained_bytecode
}
};
dep::aztec::oracle::debug_log::debug_log_format(
"ClassUnconstrainedFunctionBroadcasted: {}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ global FIXED_DA_GAS: u32 = 512;
global CANONICAL_KEY_REGISTRY_ADDRESS = 0x04c2d010f88e8c238882fbbcbce5c81fdc1dc8ece85e8dbf3f602b4d81ec0351;
global CANONICAL_AUTH_REGISTRY_ADDRESS = 0x27ffa4fb3da8a80b6365315f9798c887474854c71c0720e1c5236861288ce147;
global DEPLOYER_CONTRACT_ADDRESS = 0x2b231c13768709b1ba51c1f86275b47e38dfac16e3d7f242cb578d92a4e2d934;
global REGISTERER_CONTRACT_ADDRESS = 0x302da9b6000a76691341b250565ca5c67723261fa99af1435ffe5178ccb21417;
global REGISTERER_CONTRACT_ADDRESS = 0x1da1c95bfa44d2d94cda61564e0b28a3515f0b2ad4bd8d30d86572f02e2fba00;
global GAS_TOKEN_ADDRESS = 0x06fc7badd50bb8ee32439b52e8874b5a16ddd2aa1d5647ec46b2a0f51356f889;

// LENGTH OF STRUCTS SERIALIZED TO FIELDS
Expand Down
36 changes: 21 additions & 15 deletions yarn-project/aztec.js/src/deployment/broadcast_function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import { getRegistererContract } from './protocol_contracts.js';
* @param selector - Selector of the function to be broadcast.
* @returns A ContractFunctionInteraction object that can be used to send the transaction.
*/
export function broadcastPrivateFunction(
export async function broadcastPrivateFunction(
wallet: Wallet,
artifact: ContractArtifact,
selector: FunctionSelector,
): ContractFunctionInteraction {
): Promise<ContractFunctionInteraction> {
const contractClass = getContractClassFromArtifact(artifact);
const privateFunctionArtifact = artifact.functions.find(fn => selector.equals(fn));
if (!privateFunctionArtifact) {
Expand All @@ -50,17 +50,21 @@ export function broadcastPrivateFunction(
MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS,
);

await wallet.addCapsule(bytecode);

const registerer = getRegistererContract(wallet);
return registerer.methods.broadcast_private_function(
contractClass.id,
artifactMetadataHash,
unconstrainedFunctionsArtifactTreeRoot,
privateFunctionTreeSiblingPath,
privateFunctionTreeLeafIndex,
padArrayEnd(artifactTreeSiblingPath, Fr.ZERO, ARTIFACT_FUNCTION_TREE_MAX_HEIGHT),
artifactTreeLeafIndex,
// eslint-disable-next-line camelcase
{ selector, metadata_hash: functionMetadataHash, bytecode, vk_hash: vkHash },
return Promise.resolve(
registerer.methods.broadcast_private_function(
contractClass.id,
artifactMetadataHash,
unconstrainedFunctionsArtifactTreeRoot,
privateFunctionTreeSiblingPath,
privateFunctionTreeLeafIndex,
padArrayEnd(artifactTreeSiblingPath, Fr.ZERO, ARTIFACT_FUNCTION_TREE_MAX_HEIGHT),
artifactTreeLeafIndex,
// eslint-disable-next-line camelcase
{ selector, metadata_hash: functionMetadataHash, vk_hash: vkHash },
),
);
}

Expand All @@ -73,11 +77,11 @@ export function broadcastPrivateFunction(
* @param selector - Selector of the function to be broadcast.
* @returns A ContractFunctionInteraction object that can be used to send the transaction.
*/
export function broadcastUnconstrainedFunction(
export async function broadcastUnconstrainedFunction(
wallet: Wallet,
artifact: ContractArtifact,
selector: FunctionSelector,
): ContractFunctionInteraction {
): Promise<ContractFunctionInteraction> {
const contractClass = getContractClassFromArtifact(artifact);
const functionArtifactIndex = artifact.functions.findIndex(
fn => fn.functionType === FunctionType.UNCONSTRAINED && selector.equals(fn),
Expand All @@ -97,6 +101,8 @@ export function broadcastUnconstrainedFunction(

const bytecode = bufferAsFields(functionArtifact.bytecode, MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS);

await wallet.addCapsule(bytecode);

const registerer = getRegistererContract(wallet);
return registerer.methods.broadcast_unconstrained_function(
contractClass.id,
Expand All @@ -105,6 +111,6 @@ export function broadcastUnconstrainedFunction(
padArrayEnd(artifactTreeSiblingPath, Fr.ZERO, ARTIFACT_FUNCTION_TREE_MAX_HEIGHT),
artifactTreeLeafIndex,
// eslint-disable-next-line camelcase
{ selector, metadata_hash: functionMetadataHash, bytecode },
{ selector, metadata_hash: functionMetadataHash },
);
}
2 changes: 1 addition & 1 deletion yarn-project/circuits.js/src/constants.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const CANONICAL_AUTH_REGISTRY_ADDRESS =
18091885756106795278141309801070173692350235742979924147720536894670507925831n;
export const DEPLOYER_CONTRACT_ADDRESS = 19511485909966796736993840362353440247573331327062358513665772226446629198132n;
export const REGISTERER_CONTRACT_ADDRESS =
21791696151759019003097706094037044371210776294983020497737005968946992649239n;
13402924717071282069537366635406026232165444473509746327951838324587448220160n;
export const GAS_TOKEN_ADDRESS = 3159976153131520272419617514531889581796079438158800470341967144801191524489n;
export const AZTEC_ADDRESS_LENGTH = 1;
export const GAS_FEES_LENGTH = 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`ContractAddress computeContractAddressFromInstance 1`] = `"0x2a192ee63791ad5e219b63db872bf54ba245afbc2c1287f4ba036b8f58fad740"`;

exports[`ContractAddress computeInitializationHash 1`] = `Fr<0x109865e4b959adba34b722e72a69baaf9ee78e31bb1042318f0d91006ed86780>`;
exports[`ContractAddress computeInitializationHash 1`] = `Fr<0x06fdfa55764301f37a1e8f2f7aef196245747858bf8cba8806c30f53ea6c07df>`;

exports[`ContractAddress computePartialAddress 1`] = `Fr<0x1923a6246e305720b6aaf751fde0342613e93c82e455c3831e28375c16dd40d8>`;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`hash Var args hash matches noir 1`] = `Fr<0x05a1023fef839ac88731f49ae983e172c1b600a3c8f3393ad0ac25d819ac0f0f>`;
exports[`hash Var args hash matches noir 1`] = `Fr<0x11e40f2a780822f7971803048c9a2100579de352e7dadd99981760964da65b57>`;

exports[`hash compute secret message hash 1`] = `Fr<0x0dc06f2167e2cd19adf738d1f38469d7f8bff1e26b029816e8230bcd6ab6332e>`;

Expand All @@ -18,6 +18,6 @@ exports[`hash computes unique note hash 1`] = `Fr<0x1cbdcecec4fe92f6638eb6a8dade

exports[`hash hashes empty function args 1`] = `Fr<0x0000000000000000000000000000000000000000000000000000000000000000>`;

exports[`hash hashes function args 1`] = `Fr<0x1a76e9750a1493d95ce48be1fa31831fc370d7e68f563fe5c781c6f58e1f1eac>`;
exports[`hash hashes function args 1`] = `Fr<0x0f4da5b77d47f61ea495c0ae504c300bfe608d1f233013c20f34ac7c030d8c14>`;

exports[`hash hashes many function args 1`] = `Fr<0x21e37f4da2762d4daff39c2337d812b9c7b9c46aa9fafdf666d7d7fd9935d124>`;
exports[`hash hashes many function args 1`] = `Fr<0x21fe126bab6cf132a34ef16f23bb6e6a97551d91a2408cd186443e465dc66606>`;
4 changes: 2 additions & 2 deletions yarn-project/circuits.js/src/hash/hash.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { times } from '@aztec/foundation/collection';
import { setupCustomSnapshotSerializers } from '@aztec/foundation/testing';

import { AztecAddress, Fr } from '../index.js';
import { AztecAddress, Fr, MAX_ARGS_LENGTH } from '../index.js';
import { makeAztecAddress } from '../tests/factories.js';
import {
computeNoteHashNonce,
Expand Down Expand Up @@ -82,7 +82,7 @@ describe('hash', () => {
});

it('Var args hash matches noir', () => {
const args = times(800, i => new Fr(i));
const args = times(MAX_ARGS_LENGTH, i => new Fr(i));
const res = computeVarArgsHash(args);
expect(res).toMatchSnapshot();

Expand Down
11 changes: 3 additions & 8 deletions yarn-project/circuits.js/src/hash/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import { type AztecAddress } from '@aztec/foundation/aztec-address';
import { padArrayEnd } from '@aztec/foundation/collection';
import { pedersenHash, pedersenHashBuffer } from '@aztec/foundation/crypto';
import { Fr } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';
import { numToUInt8, numToUInt16BE, numToUInt32BE } from '@aztec/foundation/serialize';

import chunk from 'lodash.chunk';

import { ARGS_HASH_CHUNK_COUNT, ARGS_HASH_CHUNK_LENGTH, GeneratorIndex } from '../constants.gen.js';
import { ARGS_HASH_CHUNK_COUNT, ARGS_HASH_CHUNK_LENGTH, GeneratorIndex, MAX_ARGS_LENGTH } from '../constants.gen.js';
import { VerificationKey } from '../structs/index.js';

/**
Expand Down Expand Up @@ -123,12 +122,8 @@ export function computeVarArgsHash(args: Fr[]) {
if (args.length === 0) {
return Fr.ZERO;
}
const maxLen = ARGS_HASH_CHUNK_LENGTH * ARGS_HASH_CHUNK_COUNT;
if (args.length > maxLen) {
// TODO(@spalladino): This should throw instead of warning. And we should implement
// the same check on the Noir side, which is currently missing.
args = args.slice(0, maxLen);
createDebugLogger('aztec:circuits:abis').warn(`Hashing ${args.length} args exceeds max of ${maxLen}`);
if (args.length > MAX_ARGS_LENGTH) {
throw new Error(`Hashing ${args.length} args exceeds max of ${MAX_ARGS_LENGTH}`);
}

let chunksHashes = chunk(args, ARGS_HASH_CHUNK_LENGTH).map(c => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('e2e_deploy_contract contract class registration', () => {
}
const selector = FunctionSelector.fromNameAndParameters(constructorArtifact.name, constructorArtifact.parameters);

const tx = await broadcastPrivateFunction(wallet, artifact, selector).send().wait();
const tx = await (await broadcastPrivateFunction(wallet, artifact, selector)).send().wait();
const logs = await pxe.getUnencryptedLogs({ txHash: tx.txHash });
const logData = logs.logs[0].log.data;
writeTestData('yarn-project/circuits.js/fixtures/PrivateFunctionBroadcastedEventData.hex', logData);
Expand All @@ -84,7 +84,7 @@ describe('e2e_deploy_contract contract class registration', () => {
it('broadcasts an unconstrained function', async () => {
const functionArtifact = artifact.functions.find(fn => fn.functionType === FunctionType.UNCONSTRAINED)!;
const selector = FunctionSelector.fromNameAndParameters(functionArtifact);
const tx = await broadcastUnconstrainedFunction(wallet, artifact, selector).send().wait();
const tx = await (await broadcastUnconstrainedFunction(wallet, artifact, selector)).send().wait();
const logs = await pxe.getUnencryptedLogs({ txHash: tx.txHash });
const logData = logs.logs[0].log.data;
writeTestData('yarn-project/circuits.js/fixtures/UnconstrainedFunctionBroadcastedEventData.hex', logData);
Expand Down

0 comments on commit 3a76ea8

Please sign in to comment.