Skip to content

Commit

Permalink
refactor: ARGS_HASH constants 64 -> 16 (#7284)
Browse files Browse the repository at this point in the history
Fixes #7272.

Reduces the number of elements that make its way into the argshash. If
you really wanna bloat the number of inputs, go visit capsules, we don't
really want to pay for the option all the time when most simple will not
use it. Reducing both the args hash constants to 16 instead of 64,
allowing 256 instead of 4096 elements as inputs, should suffice most
cases still.
  • Loading branch information
LHerskind authored Jul 4, 2024
1 parent 0521033 commit c19029a
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 52 deletions.
8 changes: 4 additions & 4 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ library Constants {
uint256 internal constant L1_TO_L2_MSG_SUBTREE_HEIGHT = 4;
uint256 internal constant L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH = 12;
uint256 internal constant FUNCTION_SELECTOR_NUM_BYTES = 4;
uint256 internal constant ARGS_HASH_CHUNK_LENGTH = 64;
uint256 internal constant ARGS_HASH_CHUNK_COUNT = 64;
uint256 internal constant MAX_ARGS_LENGTH = 4096;
uint256 internal constant ARGS_HASH_CHUNK_LENGTH = 16;
uint256 internal constant ARGS_HASH_CHUNK_COUNT = 16;
uint256 internal constant MAX_ARGS_LENGTH = 256;
uint256 internal constant INITIALIZATION_SLOT_SEPARATOR = 1000000000;
uint256 internal constant INITIAL_L2_BLOCK_NUM = 1;
uint256 internal constant BLOB_SIZE_IN_BYTES = 126976;
Expand Down Expand Up @@ -110,7 +110,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 @@ -107,7 +107,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, "Args length exceeds maximum");
let mut chunks_hashes = [0; ARGS_HASH_CHUNK_COUNT];
let mut current_chunk_values = [0; ARGS_HASH_CHUNK_LENGTH];

Expand All @@ -133,11 +133,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 @@ -100,8 +100,8 @@ global L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH: u32 = 12;

// MISC CONSTANTS
global FUNCTION_SELECTOR_NUM_BYTES: Field = 4;
global ARGS_HASH_CHUNK_LENGTH: u32 = 64;
global ARGS_HASH_CHUNK_COUNT: u32 = 64;
global ARGS_HASH_CHUNK_LENGTH: u32 = 16;
global ARGS_HASH_CHUNK_COUNT: u32 = 16;
global MAX_ARGS_LENGTH: u32 = ARGS_HASH_CHUNK_COUNT * ARGS_HASH_CHUNK_LENGTH;
// The following is used in immutable state variables to compute an initialization slot whose value is used to
// determine whether a given variable has been initialized (by asserting that the value in the slot is 0).
Expand Down Expand Up @@ -160,7 +160,7 @@ global L2_GAS_PER_NULLIFIER = 64;
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 },
);
}
8 changes: 4 additions & 4 deletions yarn-project/circuits.js/src/constants.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ export const PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH = 34;
export const L1_TO_L2_MSG_SUBTREE_HEIGHT = 4;
export const L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH = 12;
export const FUNCTION_SELECTOR_NUM_BYTES = 4;
export const ARGS_HASH_CHUNK_LENGTH = 64;
export const ARGS_HASH_CHUNK_COUNT = 64;
export const MAX_ARGS_LENGTH = 4096;
export const ARGS_HASH_CHUNK_LENGTH = 16;
export const ARGS_HASH_CHUNK_COUNT = 16;
export const MAX_ARGS_LENGTH = 256;
export const INITIALIZATION_SLOT_SEPARATOR = 1000000000;
export const INITIAL_L2_BLOCK_NUM = 1;
export const BLOB_SIZE_IN_BYTES = 126976;
Expand Down Expand Up @@ -95,7 +95,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 c19029a

Please sign in to comment.