Skip to content
2 changes: 1 addition & 1 deletion circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ TEST(abi_tests, compute_function_leaf)
{
// Construct FunctionLeafPreimage with some randomized fields
FunctionLeafPreimage<NT> preimage = FunctionLeafPreimage<NT>{
.function_selector = NT::fr::random_element(),
.function_selector = engine.get_random_uint32(),
.is_private = static_cast<bool>(engine.get_random_uint8() & 1),
.vk_hash = NT::fr::random_element(),
.acir_hash = NT::fr::random_element(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ template <typename NCT> struct FunctionLeafPreimage {

typedef typename NCT::boolean boolean;
typedef typename NCT::fr fr;
typedef typename NCT::uint32 uint32;

fr function_selector = 0;
uint32 function_selector = 0;
boolean is_private = false;
fr vk_hash = 0;
fr acir_hash = 0;
Expand Down
2 changes: 1 addition & 1 deletion circuits/cpp/src/aztec3/circuits/hash.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ typename NCT::fr root_from_sibling_path(typename NCT::fr const& leaf,
*/
template <typename NCT>
typename NCT::fr function_tree_root_from_siblings(
typename NCT::fr const& function_selector,
typename NCT::uint32 const& function_selector,
typename NCT::boolean const& is_private,
typename NCT::fr const& vk_hash,
typename NCT::fr const& acir_hash,
Expand Down
11 changes: 7 additions & 4 deletions yarn-project/aztec-rpc/src/contract_tree/contract_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
CONTRACT_TREE_HEIGHT,
FUNCTION_TREE_HEIGHT,
FunctionData,
FunctionLeafPreimage,
MembershipWitness,
NewContractData,
computeFunctionTree,
Expand Down Expand Up @@ -49,11 +50,13 @@ async function generateFunctionLeaves(functions: ContractFunctionDao[], wasm: Ci
// All non-unconstrained functions have vks
const vkHash = await hashVKStr(f.verificationKey!, wasm);
const acirHash = keccak(Buffer.from(f.bytecode, 'hex'));
// TODO: selector is currently padded to 32 bytes in CBINDS, check this.
const fnLeaf = await computeFunctionLeaf(
wasm,
Buffer.concat([selector, Buffer.alloc(28, 0), Buffer.from([isPrivate ? 1 : 0]), vkHash, acirHash]),
const fnLeafPreimage = new FunctionLeafPreimage(
selector,
isPrivate,
Fr.fromBuffer(vkHash),
Fr.fromBuffer(acirHash),
);
const fnLeaf = await computeFunctionLeaf(wasm, fnLeafPreimage);
result.push(fnLeaf);
}
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ AztecAddress {

exports[`abis wasm bindings computes a function leaf 1`] = `
Fr {
"value": 16255853943620434246084547000263971056928483888227499632732320436242372959484n,
"value": 4561502337125734544623838483038987112287298230187358554089398390117787410340n,
}
`;

Expand Down
20 changes: 18 additions & 2 deletions yarn-project/circuits.js/src/abis/abis.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fr, FunctionData, NewContractData } from '../index.js';
import { Fr, FunctionData, FunctionLeafPreimage, NewContractData } from '../index.js';
import { makeEthAddress } from '../tests/factories.js';
import { makeAztecAddress, makeBytes, makeTxRequest, makeVerificationKey } from '../tests/factories.js';
import { CircuitsWasm } from '../wasm/circuits_wasm.js';
Expand Down Expand Up @@ -40,11 +40,27 @@ describe('abis wasm bindings', () => {
});

it('computes a function leaf', async () => {
const leaf = Buffer.alloc(32 + 1 + 32 + 32, 0);
const leaf = new FunctionLeafPreimage(Buffer.from([0, 0, 0, 123]), true, Fr.ZERO, Fr.ZERO);
const res = await computeFunctionLeaf(wasm, leaf);
expect(res).toMatchSnapshot();
});

it('compute function leaf should revert if buffer is over 4 bytes', () => {
expect(() => {
new FunctionLeafPreimage(Buffer.from([0, 0, 0, 0, 123]), true, Fr.ZERO, Fr.ZERO);
}).toThrow('Function selector must be 4 bytes long, got 5 bytes.');
});

it('function leaf toBuffer should revert if buffer is over 4 bytes ', () => {
const initBuffer = Buffer.from([0, 0, 0, 123]);
const largerBuffer = Buffer.from([0, 0, 0, 0, 123]);
expect(() => {
const leaf = new FunctionLeafPreimage(initBuffer, true, Fr.ZERO, Fr.ZERO);
leaf.functionSelector = largerBuffer;
leaf.toBuffer();
}).toThrow('Function selector must be 4 bytes long, got 5 bytes.');
});

it('computes function tree root', async () => {
const res = await computeFunctionTreeRoot(wasm, [new Fr(0n), new Fr(0n), new Fr(0n), new Fr(0n)]);
expect(res).toMatchSnapshot();
Expand Down
14 changes: 9 additions & 5 deletions yarn-project/circuits.js/src/abis/abis.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Buffer } from 'buffer';
import { AztecAddress, Fr } from '@aztec/foundation';
import { CircuitsWasm } from '../wasm/index.js';
import { FunctionData, FUNCTION_SELECTOR_NUM_BYTES, TxRequest, NewContractData } from '../index.js';
import {
FunctionData,
FUNCTION_SELECTOR_NUM_BYTES,
TxRequest,
NewContractData,
FunctionLeafPreimage,
} from '../index.js';
import { serializeToBuffer } from '../utils/serialize.js';
import { AsyncWasmWrapper, WasmWrapper } from '@aztec/foundation/wasm';

Expand Down Expand Up @@ -81,11 +87,9 @@ export async function hashVK(wasm: CircuitsWasm, vkBuf: Buffer) {
return await wasmAsyncCall(wasm, 'abis__hash_vk', { toBuffer: () => vkBuf }, 32);
}

export async function computeFunctionLeaf(wasm: CircuitsWasm, fnLeaf: Buffer) {
// Size must match circuits/cpp/src/aztec3/circuits/abis/function_leaf_preimage.hpp
if (fnLeaf.length !== 32 + 1 + 32 + 32) throw new Error(`Invalid length for function leaf`);
export async function computeFunctionLeaf(wasm: CircuitsWasm, fnLeaf: FunctionLeafPreimage) {
wasm.call('pedersen__init');
return Fr.fromBuffer(await wasmAsyncCall(wasm, 'abis__compute_function_leaf', { toBuffer: () => fnLeaf }, 32));
return Fr.fromBuffer(await wasmAsyncCall(wasm, 'abis__compute_function_leaf', fnLeaf, 32));
}

export async function computeFunctionTreeRoot(wasm: CircuitsWasm, fnLeafs: Fr[]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`basic FunctionLeafPreimage serialization serializes a trivial Function Leaf Preimage and prints it 1`] = `
"function_selector: 0x7b01000000000000000000000000000000000000000000000000000000
is_private: 0
"function_selector: 123
is_private: 1
vk_hash: 0x0
acir_hash: 0x31000000082f1200f8e21100000000000000000000
Comment thread
Maddiaa0 marked this conversation as resolved.
acir_hash: 0x0
"
`;
16 changes: 14 additions & 2 deletions yarn-project/circuits.js/src/structs/function_leaf_preimage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,20 @@ import { serializeToBuffer } from '../utils/serialize.js';
* @see abis/function_leaf_preimage.hpp
*/
export class FunctionLeafPreimage {
readonly FUNCTION_SELECTOR_LENGTH = 4;

constructor(public functionSelector: Buffer, public isPrivate: boolean, public vkHash: Fr, public acirHash: Fr) {
if (functionSelector.byteLength !== 4) {
throw new Error(`Function selector must be 4 bytes long, got ${functionSelector.byteLength} bytes.`);
this.assertFunctionSelectorLength(functionSelector);
}

/**
* Assert the function selector buffer length matches `FUNCTION_SELECTOR_LENGTH`
*/
private assertFunctionSelectorLength(functionSelector: Buffer) {
if (functionSelector.byteLength !== this.FUNCTION_SELECTOR_LENGTH) {
throw new Error(
`Function selector must be ${this.FUNCTION_SELECTOR_LENGTH} bytes long, got ${functionSelector.byteLength} bytes.`,
);
}
}

Expand All @@ -17,6 +28,7 @@ export class FunctionLeafPreimage {
* @returns The buffer.
*/
toBuffer(): Buffer {
this.assertFunctionSelectorLength(this.functionSelector);
return serializeToBuffer(this.functionSelector, this.isPrivate, this.vkHash, this.acirHash);
}

Expand Down
1 change: 1 addition & 0 deletions yarn-project/circuits.js/src/structs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export * from './shared.js';
export * from './tx.js';
export * from './verification_key.js';
export * from './private_call_stack_item.js';
export * from './function_leaf_preimage.js';
export { Fr, Fq, AztecAddress, EthAddress } from '@aztec/foundation';