diff --git a/circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp b/circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp index 364c92bab2d0..1ee832d871e3 100644 --- a/circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp +++ b/circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp @@ -140,7 +140,7 @@ TEST(abi_tests, compute_function_leaf) { // Construct FunctionLeafPreimage with some randomized fields FunctionLeafPreimage preimage = FunctionLeafPreimage{ - .function_selector = NT::fr::random_element(), + .function_selector = engine.get_random_uint32(), .is_private = static_cast(engine.get_random_uint8() & 1), .vk_hash = NT::fr::random_element(), .acir_hash = NT::fr::random_element(), diff --git a/circuits/cpp/src/aztec3/circuits/abis/function_leaf_preimage.hpp b/circuits/cpp/src/aztec3/circuits/abis/function_leaf_preimage.hpp index 1963518c7730..04498db2a7fb 100644 --- a/circuits/cpp/src/aztec3/circuits/abis/function_leaf_preimage.hpp +++ b/circuits/cpp/src/aztec3/circuits/abis/function_leaf_preimage.hpp @@ -29,8 +29,9 @@ template 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; diff --git a/circuits/cpp/src/aztec3/circuits/hash.hpp b/circuits/cpp/src/aztec3/circuits/hash.hpp index c5a722c6d8b6..fcfde09f1745 100644 --- a/circuits/cpp/src/aztec3/circuits/hash.hpp +++ b/circuits/cpp/src/aztec3/circuits/hash.hpp @@ -126,7 +126,7 @@ typename NCT::fr root_from_sibling_path(typename NCT::fr const& leaf, */ template 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, diff --git a/yarn-project/aztec-rpc/src/contract_tree/contract_tree.ts b/yarn-project/aztec-rpc/src/contract_tree/contract_tree.ts index 49d717a860fe..c09bed156cc6 100644 --- a/yarn-project/aztec-rpc/src/contract_tree/contract_tree.ts +++ b/yarn-project/aztec-rpc/src/contract_tree/contract_tree.ts @@ -3,6 +3,7 @@ import { CONTRACT_TREE_HEIGHT, FUNCTION_TREE_HEIGHT, FunctionData, + FunctionLeafPreimage, MembershipWitness, NewContractData, computeFunctionTree, @@ -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; diff --git a/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap b/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap index 21707a772620..5ec7c15ebb82 100644 --- a/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap +++ b/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap @@ -44,7 +44,7 @@ AztecAddress { exports[`abis wasm bindings computes a function leaf 1`] = ` Fr { - "value": 16255853943620434246084547000263971056928483888227499632732320436242372959484n, + "value": 4561502337125734544623838483038987112287298230187358554089398390117787410340n, } `; diff --git a/yarn-project/circuits.js/src/abis/abis.test.ts b/yarn-project/circuits.js/src/abis/abis.test.ts index 1cd0325d1e94..7498466746c3 100644 --- a/yarn-project/circuits.js/src/abis/abis.test.ts +++ b/yarn-project/circuits.js/src/abis/abis.test.ts @@ -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'; @@ -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(); diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index e428aa69f108..b3e0f652a9cc 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -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'; @@ -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[]) { diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/function_leaf_preimage.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/function_leaf_preimage.test.ts.snap index 9c0600d4b4dc..6b82b90ec0de 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/function_leaf_preimage.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/function_leaf_preimage.test.ts.snap @@ -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 +acir_hash: 0x0 " `; diff --git a/yarn-project/circuits.js/src/structs/function_leaf_preimage.ts b/yarn-project/circuits.js/src/structs/function_leaf_preimage.ts index 00898d772741..85d6caafcbe4 100644 --- a/yarn-project/circuits.js/src/structs/function_leaf_preimage.ts +++ b/yarn-project/circuits.js/src/structs/function_leaf_preimage.ts @@ -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.`, + ); } } @@ -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); } diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index de6540632900..ab751bd425f5 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -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';