From 1b5402b261a839a7be2a1ee4a2a6618423ae93d5 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Wed, 19 Apr 2023 19:51:23 +0000 Subject: [PATCH 1/8] use a plain old serialized vector of frs instead of doing manual serialization logic for leaf frs --- .../cpp/src/aztec3/circuits/abis/c_bind.cpp | 142 +++++++----------- .../cpp/src/aztec3/circuits/abis/c_bind.h | 2 - .../src/aztec3/circuits/abis/c_bind.test.cpp | 92 ++++++------ 3 files changed, 103 insertions(+), 133 deletions(-) diff --git a/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp b/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp index 58161210b13..6c556583f4c 100644 --- a/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp +++ b/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp @@ -23,7 +23,6 @@ #include #include #include -#include namespace { @@ -37,71 +36,29 @@ using aztec3::circuits::abis::private_kernel::NewContractData; using NT = aztec3::utils::types::NativeTypes; // Cbind helper functions + /** - * @brief Compute an imperfect merkle tree's root from leaves. + * @brief Fill in zero-leaves to get a full tree's bottom layer. * - * @details given a `uint8_t const*` buffer representing a merkle tree's leaves, - * compute the corresponding tree's root and return the serialized results - * in the `output` buffer. "Partial left tree" here means that the tree's leaves - * are filled strictly from left to right, but there may be empty leaves on the right - * end of the tree. + * @details Given the a vector of nonzero leaves starting at the left, + * append zeroleaves to that list until it represents a FULL set of leaves + * for a tree of the given height. + * **MODIFIES THE INPUT `leaves` REFERENCE!** * - * @tparam TREE_HEIGHT height of the tree used to determine max leaves and used when computing root - * @param leaves_buf a buffer of bytes representing the leaves of the tree, where each leaf is - * assumed to be a field and is interpreted using `NT::fr::serialize_from_buffer(leaf_ptr)` - * @param num_leaves the number of leaves in leaves_buf + * @tparam TREE_HEIGHT height of the tree used to determine max leaves + * @param leaves the nonzero leaves of the tree starting at the left * @param zero_leaf the leaf value to be used for any empty/unset leaves - * @returns a field (`NT::fr`) containing the computed merkle tree root */ -template -NT::fr compute_root_of_partial_left_tree(uint8_t const* leaves_buf, uint8_t num_leaves, NT::fr zero_leaf) -{ - const size_t max_leaves = 2 << (TREE_HEIGHT - 1); - // cant exceed max leaves - ASSERT(num_leaves <= max_leaves); - - // initialize the vector of leaves to a complete-tree-sized vector of zero-leaves - std::vector leaves(max_leaves, zero_leaf); - - // Iterate over the input buffer, extracting each leaf and serializing it from buffer to field - // Insert each leaf field into the vector - // If num_leaves < perfect tree, remaining leaves will be `zero_leaf` - for (size_t l = 0; l < num_leaves; l++) { - // each iteration skips to over some number of `fr`s to get to the // next leaf - uint8_t const* cur_leaf_ptr = leaves_buf + sizeof(NT::fr) * l; - NT::fr leaf = NT::fr::serialize_from_buffer(cur_leaf_ptr); - leaves[l] = leaf; - } - - // compute the root of this complete tree, return - return plonk::stdlib::merkle_tree::compute_tree_root_native(leaves); -} - -// TODO comment -// TODO code reuse possible with root func above -template -std::vector // array length is num nodes -compute_partial_left_tree(uint8_t const* leaves_buf, uint8_t num_leaves, NT::fr zero_leaf) +template void rightfill_with_zeroleaves(std::vector& leaves, NT::fr& zero_leaf) { - const size_t max_leaves = 2 << (TREE_HEIGHT - 1); - // cant exceed max leaves - ASSERT(num_leaves <= max_leaves); - - // initialize the vector of leaves to a complete-tree-sized vector of zero-leaves - std::vector leaves(max_leaves, zero_leaf); - - // Iterate over the input buffer, extracting each leaf and serializing it from buffer to field - // Insert each leaf field into the vector - // If num_leaves < perfect tree, remaining leaves will be `zero_leaf` - for (size_t l = 0; l < num_leaves; l++) { - // each iteration skips to over some number of `fr`s to get to the // next leaf - uint8_t const* cur_leaf_ptr = leaves_buf + sizeof(NT::fr) * l; - NT::fr leaf = NT::fr::serialize_from_buffer(cur_leaf_ptr); - leaves[l] = leaf; - } - - // compute the root of this complete tree, return - return plonk::stdlib::merkle_tree::compute_tree_native(leaves); + constexpr size_t max_leaves = 2 << (TREE_HEIGHT - 1); + // input cant exceed max leaves + // FIXME don't think asserts will show in wasm + ASSERT(leaves.size() <= max_leaves); + + // fill in input vector with zero-leaves + // to get a full bottom layer of the tree + leaves.insert(leaves.end(), max_leaves - leaves.size(), zero_leaf); } } // namespace @@ -238,39 +195,59 @@ WASM_EXPORT void abis__compute_function_leaf(uint8_t const* function_leaf_preima } /** - * @brief Compute a function tree root from its leaves. + * @brief Compute a function tree root from its nonzero leaves. * This is a WASM-export that can be called from Typescript. * - * @details given a `uint8_t const*` buffer representing a function tree's leaves, - * compute the corresponding tree's root and return the serialized results - * in the `output` buffer. + * @details given a serialized vector of nonzero function leaves, + * compute the corresponding tree's root and return the + * serialized results via `root_out` buffer. * - * @param function_leaves_buf a buffer of bytes representing the leaves of the function tree, - * where each leaf is assumed to be a serialized field - * @param num_leaves the number of leaves in leaves_buf - * @param output buffer that will contain the output. The serialized function tree root. + * @param function_leaves_in input buffer representing a serialized vector of + * nonzero function leaves where each leaf is an `fr` starting at the left of the tree + * @param root_out buffer that will contain the serialized function tree root `fr`. */ -WASM_EXPORT void abis__compute_function_tree_root(uint8_t const* function_leaves_buf, - uint8_t num_leaves, - uint8_t* output) +WASM_EXPORT void abis__compute_function_tree_root(uint8_t const* function_leaves_in, uint8_t* root_out) { + std::vector leaves; + // fill in nonzero leaves to start + read(function_leaves_in, leaves); + // fill in zero leaves to complete tree NT::fr zero_leaf = FunctionLeafPreimage().hash(); // hash of empty/0 preimage - NT::fr root = - compute_root_of_partial_left_tree(function_leaves_buf, num_leaves, zero_leaf); + rightfill_with_zeroleaves(leaves, zero_leaf); + + // compute the root of this complete tree, return + NT::fr root = plonk::stdlib::merkle_tree::compute_tree_root_native(leaves); // serialize and return root - NT::fr::serialize_to_buffer(root, output); + NT::fr::serialize_to_buffer(root, root_out); } -// TODO comment -WASM_EXPORT void abis__compute_function_tree(uint8_t const* function_leaves_buf, uint8_t num_leaves, uint8_t* output) +/** + * @brief Compute all of a function tree's nodes from its nonzero leaves. + * This is a WASM-export that can be called from Typescript. + * + * @details given a serialized vector of nonzero function leaves, + * compute ALL of the corresponding tree's nodes (including root) and return + * the serialized results via `tree_nodes_out` buffer. + * + * @param function_leaves_in input buffer representing a serialized vector of + * nonzero function leaves where each leaf is an `fr` starting at the left of the tree. + * @param tree_nodes_out buffer that will contain the serialized function tree. + * The 0th node is the bottom leftmost leaf. The last entry is the root. + */ +WASM_EXPORT void abis__compute_function_tree(uint8_t const* function_leaves_in, uint8_t* tree_nodes_out) { + std::vector leaves; + // fill in nonzero leaves to start + read(function_leaves_in, leaves); + // fill in zero leaves to complete tree NT::fr zero_leaf = FunctionLeafPreimage().hash(); // hash of empty/0 preimage - std::vector tree = - compute_partial_left_tree(function_leaves_buf, num_leaves, zero_leaf); + rightfill_with_zeroleaves(leaves, zero_leaf); + + std::vector tree = plonk::stdlib::merkle_tree::compute_tree_native(leaves); // serialize and return tree - write(output, tree); + write(tree_nodes_out, tree); } /** @@ -295,7 +272,6 @@ WASM_EXPORT void abis__hash_constructor(uint8_t const* function_data_buf, std::array args; NT::fr constructor_vk_hash; - using serialize::read; read(function_data_buf, function_data); read(args_buf, args); read(constructor_vk_hash_buf, constructor_vk_hash); @@ -331,7 +307,6 @@ WASM_EXPORT void abis__compute_contract_address(uint8_t const* deployer_address_ NT::fr function_tree_root; NT::fr constructor_hash; - using serialize::read; read(deployer_address_buf, deployer_address); read(contract_address_salt_buf, contract_address_salt); read(function_tree_root_buf, function_tree_root); @@ -447,11 +422,10 @@ WASM_EXPORT const char* abis__test_roundtrip_serialize_private_kernel_public_inp return as_string_output>(input, size); } - -WASM_EXPORT const char* abis__test_roundtrip_serialize_function_leaf_preimage(uint8_t const* function_leaf_preimage_buf, uint32_t* size) +WASM_EXPORT const char* abis__test_roundtrip_serialize_function_leaf_preimage(uint8_t const* function_leaf_preimage_buf, + uint32_t* size) { return as_string_output>(function_leaf_preimage_buf, size); } - } // extern "C" diff --git a/circuits/cpp/src/aztec3/circuits/abis/c_bind.h b/circuits/cpp/src/aztec3/circuits/abis/c_bind.h index 412db8fbd2a..894adb97bed 100644 --- a/circuits/cpp/src/aztec3/circuits/abis/c_bind.h +++ b/circuits/cpp/src/aztec3/circuits/abis/c_bind.h @@ -11,11 +11,9 @@ WASM_EXPORT void abis__compute_function_selector(char const* func_sig_cstr, uint WASM_EXPORT void abis__compute_function_leaf(uint8_t const* function_leaf_preimage_buf, uint8_t* output); WASM_EXPORT void abis__compute_function_tree_root(uint8_t const* function_leaves_buf, - uint8_t num_leaves, uint8_t* output); WASM_EXPORT void abis__compute_function_tree(uint8_t const* function_leaves_buf, - uint8_t num_leaves, uint8_t* output); WASM_EXPORT void abis__hash_vk(uint8_t const* vk_data_buf, uint8_t* output); 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 1ee832d871e..93ce3b8a35a 100644 --- a/circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp +++ b/circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp @@ -14,6 +14,13 @@ namespace { using NT = aztec3::utils::types::NativeTypes; using aztec3::circuits::abis::private_kernel::NewContractData; +// num_leaves = 2**h = 2<<(h-1) +// root layer does not count in height +constexpr size_t FUNCTION_TREE_NUM_LEAVES = 2 << (aztec3::FUNCTION_TREE_HEIGHT - 1); +// num_nodes = (2**(h+1))-1 = (2<().hash(); // hash of empty/0 preimage - // these frs will be used to compute the root directly (without cbind) - // all empty slots will have the zero-leaf to ensure full tree - std::vector leaves_frs(FUNCTION_TREE_NUM_LEAVES, zero_leaf); - // randomize number of non-zero leaves such that `0 < num_nonzero_leaves <= FUNCTION_TREE_NUM_LEAVES` uint8_t num_nonzero_leaves = engine.get_random_uint8() % (FUNCTION_TREE_NUM_LEAVES + 1); - // create a vector whose vec.data() can be cast to a single mega-buffer containing all non-zero leaves - // initialize the vector with its size so that a leaf's data can be copied in (via `seralize_to_buffer`) - // (uint256_t here means nothing; it is just used because it is the right size (32 uint8_ts)) - std::vector leaves(num_nonzero_leaves); // generate some random leaves - // insert them into the vector of leaf fields (for direct tree root computation) - // insert their serialized form into the vector of 32-bytes chunks/uint256_ts - // (to be cast to a single mega uint8_t* buffer and passed to cbind) + std::vector leaves_frs; for (size_t l = 0; l < num_nonzero_leaves; l++) { - NT::fr leaf = NT::fr::random_element(); - leaves_frs[l] = leaf; - NT::fr::serialize_to_buffer(leaf, reinterpret_cast(&leaves[l])); + leaves_frs.push_back(NT::fr::random_element()); } + // serilalize the leaves to a buffer to pass to cbind + std::vector leaves_bytes_vec; + write(leaves_bytes_vec, leaves_frs); // call cbind and get output (root) std::array output = { 0 }; - abis__compute_function_tree_root(reinterpret_cast(leaves.data()), num_nonzero_leaves, output.data()); + abis__compute_function_tree_root(leaves_bytes_vec.data(), output.data()); + NT::fr got_root = NT::fr::serialize_from_buffer(output.data()); // compare cbind results with direct computation - NT::fr got_root = NT::fr::serialize_from_buffer(output.data()); + + // add the zero leaves to the vector of fields and pass to barretenberg helper + NT::fr zero_leaf = FunctionLeafPreimage().hash(); // hash of empty/0 preimage + for (size_t l = num_nonzero_leaves; l < FUNCTION_TREE_NUM_LEAVES; l++) { + leaves_frs.push_back(zero_leaf); + } + // compare results EXPECT_EQ(got_root, plonk::stdlib::merkle_tree::compute_tree_root_native(leaves_frs)); } TEST(abi_tests, compute_function_tree) { - constexpr size_t FUNCTION_TREE_NUM_LEAVES = 2 << (aztec3::FUNCTION_TREE_HEIGHT - 1); // leaves = 2 ^ height - - NT::fr zero_leaf = FunctionLeafPreimage().hash(); // hash of empty/0 preimage - // these frs will be used to compute the tree directly (without cbind) - // all empty slots will have the zero-leaf to ensure full tree - std::vector leaves_frs(FUNCTION_TREE_NUM_LEAVES, zero_leaf); - // randomize number of non-zero leaves such that `0 < num_nonzero_leaves <= FUNCTION_TREE_NUM_LEAVES` uint8_t num_nonzero_leaves = engine.get_random_uint8() % (FUNCTION_TREE_NUM_LEAVES + 1); - // create a vector whose vec.data() can be cast to a single mega-buffer containing all non-zero leaves - // initialize the vector with its size so that a leaf's data can be copied in (via `seralize_to_buffer`) - // (uint256_t here means nothing; it is just used because it is the right size (32 uint8_ts)) - std::vector leaves(num_nonzero_leaves); // generate some random leaves - // insert them into the vector of leaf fields (for direct tree computation) - // insert their serialized form into the vector of 32-bytes chunks/uint256_ts - // (to be cast to a single mega uint8_t* buffer and passed to cbind) + std::vector leaves_frs; for (size_t l = 0; l < num_nonzero_leaves; l++) { - NT::fr leaf = NT::fr::random_element(); - leaves_frs[l] = leaf; - NT::fr::serialize_to_buffer(leaf, reinterpret_cast(&leaves[l])); + leaves_frs.push_back(NT::fr::random_element()); } + // serilalize the leaves to a buffer to pass to cbind + std::vector leaves_bytes_vec; + write(leaves_bytes_vec, leaves_frs); + + // setup output buffer + // it must fit a uint32_t (for the vector length) + // plus all of the nodes `frs` in the tree + constexpr auto size_output_buf = sizeof(uint32_t) + (sizeof(NT::fr) * FUNCTION_TREE_NUM_NODES); + std::array output = { 0 }; + + // call cbind and get output (full tree root) + abis__compute_function_tree(leaves_bytes_vec.data(), output.data()); + // deserialize output to vector of frs representing all nodes in tree + std::vector got_tree; + uint8_t const* output_ptr = output.data(); + read(output_ptr, got_tree); - // (2**h) - 1 - constexpr size_t num_nodes = (2 << aztec3::FUNCTION_TREE_HEIGHT) - 1; - - // call cbind and get output (root) - uint8_t* output = (uint8_t*)malloc(sizeof(NT::fr) * num_nodes); - abis__compute_function_tree(reinterpret_cast(leaves.data()), num_nonzero_leaves, output); - - using serialize::read; // compare cbind results with direct computation - std::vector got_tree; - uint8_t const* output_copy = output; - read(output_copy, got_tree); + // add the zero leaves to the vector of fields and pass to barretenberg helper + NT::fr zero_leaf = FunctionLeafPreimage().hash(); // hash of empty/0 preimage + for (size_t l = num_nonzero_leaves; l < FUNCTION_TREE_NUM_LEAVES; l++) { + leaves_frs.push_back(zero_leaf); + } + // compare results EXPECT_EQ(got_tree, plonk::stdlib::merkle_tree::compute_tree_native(leaves_frs)); } From 2779c42ce178ea63a71814918b4f049de5eac9a0 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Thu, 20 Apr 2023 20:33:53 +0000 Subject: [PATCH 2/8] fix function tree TS logic to match C++ fix --- yarn-project/circuits.js/src/abis/abis.ts | 15 ++++++++------- yarn-project/circuits.js/src/kernel/kernel.ts | 10 +++++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index b3e0f652a9c..c57740f5a87 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -8,7 +8,7 @@ import { NewContractData, FunctionLeafPreimage, } from '../index.js'; -import { serializeToBuffer } from '../utils/serialize.js'; +import { serializeToBuffer, serializeBufferArrayToVector } from '../utils/serialize.js'; import { AsyncWasmWrapper, WasmWrapper } from '@aztec/foundation/wasm'; export function wasmSyncCall( @@ -93,13 +93,14 @@ export async function computeFunctionLeaf(wasm: CircuitsWasm, fnLeaf: FunctionLe } export async function computeFunctionTreeRoot(wasm: CircuitsWasm, fnLeafs: Fr[]) { - const inputBuf = serializeToBuffer(fnLeafs); + const inputVector = serializeBufferArrayToVector(fnLeafs.map(fr => fr.toBuffer())); wasm.call('pedersen__init'); - const outputBuf = wasm.call('bbmalloc', 32); - const inputBufPtr = wasm.call('bbmalloc', inputBuf.length); - wasm.writeMemory(inputBufPtr, inputBuf); - await wasm.asyncCall('abis__compute_function_tree_root', inputBufPtr, fnLeafs.length, outputBuf); - return Fr.fromBuffer(Buffer.from(wasm.getMemorySlice(outputBuf, outputBuf + 32))); + const result = await wasmAsyncCall(wasm, + 'abis__compute_function_tree_root', + { toBuffer: () => inputVector }, + 32 + ); + return Fr.fromBuffer(result); } export async function hashConstructor( diff --git a/yarn-project/circuits.js/src/kernel/kernel.ts b/yarn-project/circuits.js/src/kernel/kernel.ts index 72d1de31763..b8da1b5dbec 100644 --- a/yarn-project/circuits.js/src/kernel/kernel.ts +++ b/yarn-project/circuits.js/src/kernel/kernel.ts @@ -8,7 +8,7 @@ import { PrivateKernelPublicInputs, SignedTxRequest, } from '../index.js'; -import { boolToBuffer, serializeToBuffer, uint8ArrayToNum } from '../utils/serialize.js'; +import { boolToBuffer, serializeBufferArrayToVector, uint8ArrayToNum } from '../utils/serialize.js'; import { CircuitsWasm } from '../wasm/index.js'; export async function getDummyPreviousKernelData(wasm: CircuitsWasm) { @@ -30,13 +30,13 @@ export async function computeFunctionTree(wasm: CircuitsWasm, leaves: Fr[]): Pro const outputBufSize = 2 ** (FUNCTION_TREE_HEIGHT + 1) * Fr.SIZE_IN_BYTES + 4; // Allocate memory for the input and output buffers, and populate input buffer - const inputBuf = serializeToBuffer(leaves); - const inputBufPtr = wasm.call('bbmalloc', inputBuf.length); + const inputVector = serializeBufferArrayToVector(leaves.map(fr => fr.toBuffer())); + const inputBufPtr = wasm.call('bbmalloc', inputVector.length); const outputBufPtr = wasm.call('bbmalloc', outputBufSize * 100); - wasm.writeMemory(inputBufPtr, inputBuf); + wasm.writeMemory(inputBufPtr, inputVector); // Run and read outputs - await wasm.asyncCall('abis__compute_function_tree', inputBufPtr, leaves.length, outputBufPtr); + await wasm.asyncCall('abis__compute_function_tree', inputBufPtr, outputBufPtr); const outputBuf = Buffer.from(wasm.getMemorySlice(outputBufPtr, outputBufPtr + outputBufSize)); const reader = new BufferReader(outputBuf); const output = reader.readVector(Fr); From c9573fbd06b85d57348fa65ea9397166f555c34f Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Thu, 20 Apr 2023 20:39:13 +0000 Subject: [PATCH 3/8] prettier abis.ts --- yarn-project/circuits.js/src/abis/abis.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index c57740f5a87..45cf665258f 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -95,11 +95,7 @@ export async function computeFunctionLeaf(wasm: CircuitsWasm, fnLeaf: FunctionLe export async function computeFunctionTreeRoot(wasm: CircuitsWasm, fnLeafs: Fr[]) { const inputVector = serializeBufferArrayToVector(fnLeafs.map(fr => fr.toBuffer())); wasm.call('pedersen__init'); - const result = await wasmAsyncCall(wasm, - 'abis__compute_function_tree_root', - { toBuffer: () => inputVector }, - 32 - ); + const result = await wasmAsyncCall(wasm, 'abis__compute_function_tree_root', { toBuffer: () => inputVector }, 32); return Fr.fromBuffer(result); } From 07776d02d12c96720bd16bd0312e4b4ccb378d62 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Thu, 20 Apr 2023 22:50:57 +0000 Subject: [PATCH 4/8] fill out constructor args static length array with 0s before calling cbind. enforce args length. update snapshots --- .../src/abis/__snapshots__/abis.test.ts.snap | 60 +++++++++---------- .../circuits.js/src/abis/abis.test.ts | 6 ++ yarn-project/circuits.js/src/abis/abis.ts | 9 ++- 3 files changed, 44 insertions(+), 31 deletions(-) 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 5ec7c15ebb8..a37c453c266 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 @@ -102,38 +102,38 @@ Fr { exports[`abis wasm bindings hash constructor info 1`] = ` { "data": [ - 33, - 79, - 48, - 128, - 228, - 0, - 77, - 227, - 224, - 187, - 234, - 166, - 162, - 215, - 250, - 187, - 135, - 156, - 67, - 135, - 234, - 67, - 223, - 239, + 6, + 8, + 191, + 65, + 136, + 96, + 198, + 140, + 203, + 137, + 102, 172, - 228, - 95, 105, - 107, - 101, - 207, - 241, + 89, + 77, + 15, + 15, + 196, + 42, + 213, + 11, + 179, + 9, + 133, + 16, + 68, + 18, + 76, + 208, + 143, + 149, + 123, ], "type": "Buffer", } diff --git a/yarn-project/circuits.js/src/abis/abis.test.ts b/yarn-project/circuits.js/src/abis/abis.test.ts index 7498466746c..da1870c09c7 100644 --- a/yarn-project/circuits.js/src/abis/abis.test.ts +++ b/yarn-project/circuits.js/src/abis/abis.test.ts @@ -73,6 +73,12 @@ describe('abis wasm bindings', () => { const res = await hashConstructor(wasm, functionData, args, vkHash); expect(res).toMatchSnapshot(); }); + it('hash constructor throws on >8 args', async () => { + const functionData = new FunctionData(Buffer.alloc(4), true, true); + const args = [new Fr(0n), new Fr(1n), new Fr(2n), new Fr(3n), new Fr(4n), new Fr(5n), new Fr(6n), new Fr(7n), new Fr(8n)]; + const vkHash = Buffer.alloc(32); + await expect(hashConstructor(wasm, functionData, args, vkHash)).rejects.toThrow(); + }); it('computes a contract address', async () => { const deployerAddr = makeAztecAddress(1); diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index b3e0f652a9c..b3d63ec4929 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -4,6 +4,7 @@ import { CircuitsWasm } from '../wasm/index.js'; import { FunctionData, FUNCTION_SELECTOR_NUM_BYTES, + ARGS_LENGTH, TxRequest, NewContractData, FunctionLeafPreimage, @@ -108,7 +109,13 @@ export async function hashConstructor( args: Fr[], constructorVKHash: Buffer, ) { - const inputVector = serializeToBuffer(args.map(fr => fr.toBuffer())); + if (args.length > ARGS_LENGTH) { + throw new Error(`Expected constructor args to have length <= ${ARGS_LENGTH}! Was: ${args.length}`); + } + const numEmptyArgs = ARGS_LENGTH - args.length; + const emptyArgs = Array.from({ length: numEmptyArgs}, () => new Fr(0n)); + const fullArgs = args.concat(emptyArgs); + const inputVector = serializeToBuffer(fullArgs.map(fr => fr.toBuffer())); wasm.call('pedersen__init'); const result = await inputBuffersToOutputBuffer( wasm, From 63bb3635fdaffadc91014bae44e914afa2af9c72 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Fri, 21 Apr 2023 00:09:26 +0000 Subject: [PATCH 5/8] format --- yarn-project/circuits.js/src/abis/abis.test.ts | 2 +- yarn-project/circuits.js/src/abis/abis.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/circuits.js/src/abis/abis.test.ts b/yarn-project/circuits.js/src/abis/abis.test.ts index f863e71fedb..969537d8f67 100644 --- a/yarn-project/circuits.js/src/abis/abis.test.ts +++ b/yarn-project/circuits.js/src/abis/abis.test.ts @@ -85,7 +85,7 @@ describe('abis wasm bindings', () => { it('hash constructor throws (>max args)', async () => { const functionData = new FunctionData(Buffer.alloc(4), true, true); - const args = Array.from({ length: ARGS_LENGTH+1 }, (v, i) => new Fr(BigInt(i))); + const args = Array.from({ length: ARGS_LENGTH + 1 }, (v, i) => new Fr(BigInt(i))); const vkHash = Buffer.alloc(32); await expect(hashConstructor(wasm, functionData, args, vkHash)).rejects.toThrow(); }); diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index b3d63ec4929..6f1a89a49bb 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -113,7 +113,7 @@ export async function hashConstructor( throw new Error(`Expected constructor args to have length <= ${ARGS_LENGTH}! Was: ${args.length}`); } const numEmptyArgs = ARGS_LENGTH - args.length; - const emptyArgs = Array.from({ length: numEmptyArgs}, () => new Fr(0n)); + const emptyArgs = Array.from({ length: numEmptyArgs }, () => new Fr(0n)); const fullArgs = args.concat(emptyArgs); const inputVector = serializeToBuffer(fullArgs.map(fr => fr.toBuffer())); wasm.call('pedersen__init'); From 41ffa394996b5f7130798a3b9471fba9b584315f Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Fri, 21 Apr 2023 00:16:52 +0000 Subject: [PATCH 6/8] undo C++ autoformatting From ec7cc9b472cebf5ddeee21444f117921ca67f3f3 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Fri, 21 Apr 2023 17:16:59 +0000 Subject: [PATCH 7/8] allow wasm call helpers to accept Buffer or object with toBuffer function --- yarn-project/circuits.js/src/abis/abis.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index abe61686e73..c19151fcdfe 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -15,10 +15,10 @@ import { AsyncWasmWrapper, WasmWrapper } from '@aztec/foundation/wasm'; export function wasmSyncCall( wasm: WasmWrapper, fnName: string, - input: { toBuffer: () => Buffer }, + input: Buffer | { toBuffer: () => Buffer }, expectedOutputLength: number, ): Buffer { - const inputData = input.toBuffer(); + const inputData: Buffer = input instanceof Buffer ? input : input.toBuffer(); const outputBuf = wasm.call('bbmalloc', expectedOutputLength); const inputBuf = wasm.call('bbmalloc', inputData.length); wasm.writeMemory(inputBuf, inputData); @@ -32,10 +32,10 @@ export function wasmSyncCall( export async function wasmAsyncCall( wasm: AsyncWasmWrapper, fnName: string, - input: { toBuffer: () => Buffer }, + input: Buffer | { toBuffer: () => Buffer }, expectedOutputLength: number, ): Promise { - const inputData = input.toBuffer(); + const inputData: Buffer = input instanceof Buffer ? input : input.toBuffer(); const outputBuf = wasm.call('bbmalloc', expectedOutputLength); const inputBuf = wasm.call('bbmalloc', inputData.length); wasm.writeMemory(inputBuf, inputData); From bcd82372de046982cc25f2f8b8638bdb4ebfc563 Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Fri, 21 Apr 2023 17:25:13 +0000 Subject: [PATCH 8/8] wasm call helpers accept `Buffer \| { toBuffer: () => Buffer }` instead of only the object with toBuffer. --- yarn-project/circuits.js/src/abis/abis.ts | 8 ++++---- .../circuits.js/src/rollup/rollup_wasm_wrapper.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index c19151fcdfe..8d432236b3f 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -78,14 +78,14 @@ export async function computeFunctionSelector(wasm: CircuitsWasm, funcSig: strin return await wasmAsyncCall( wasm, 'abis__compute_function_selector', - { toBuffer: () => Buffer.from(funcSig) }, + Buffer.from(funcSig), FUNCTION_SELECTOR_NUM_BYTES, ); } export async function hashVK(wasm: CircuitsWasm, vkBuf: Buffer) { wasm.call('pedersen__init'); - return await wasmAsyncCall(wasm, 'abis__hash_vk', { toBuffer: () => vkBuf }, 32); + return await wasmAsyncCall(wasm, 'abis__hash_vk', vkBuf, 32); } export async function computeFunctionLeaf(wasm: CircuitsWasm, fnLeaf: FunctionLeafPreimage) { @@ -96,7 +96,7 @@ export async function computeFunctionLeaf(wasm: CircuitsWasm, fnLeaf: FunctionLe export async function computeFunctionTreeRoot(wasm: CircuitsWasm, fnLeafs: Fr[]) { const inputVector = serializeBufferArrayToVector(fnLeafs.map(fr => fr.toBuffer())); wasm.call('pedersen__init'); - const result = await wasmAsyncCall(wasm, 'abis__compute_function_tree_root', { toBuffer: () => inputVector }, 32); + const result = await wasmAsyncCall(wasm, 'abis__compute_function_tree_root', inputVector, 32); return Fr.fromBuffer(result); } @@ -143,6 +143,6 @@ export async function computeContractAddress( export function computeContractLeaf(wasm: WasmWrapper, cd: NewContractData) { wasm.call('pedersen__init'); - const value = wasmSyncCall(wasm, 'abis__compute_contract_leaf', { toBuffer: () => cd.toBuffer() }, 32); + const value = wasmSyncCall(wasm, 'abis__compute_contract_leaf', cd, 32); return Fr.fromBuffer(value); } diff --git a/yarn-project/circuits.js/src/rollup/rollup_wasm_wrapper.ts b/yarn-project/circuits.js/src/rollup/rollup_wasm_wrapper.ts index 6d87966d9dc..000a497ed21 100644 --- a/yarn-project/circuits.js/src/rollup/rollup_wasm_wrapper.ts +++ b/yarn-project/circuits.js/src/rollup/rollup_wasm_wrapper.ts @@ -26,11 +26,11 @@ export class RollupWasmWrapper { // Adapted from yarn-project/circuits.js/src/tests/expectSerialize.ts private async callWasm( method: string, - input: { toBuffer: () => Buffer }, + input: Buffer | { toBuffer: () => Buffer }, outputType: { fromBuffer: (b: Buffer) => T }, ): Promise { const wasm = this.wasm; - const inputBuf = input.toBuffer(); + const inputBuf: Buffer = input instanceof Buffer ? input : input.toBuffer(); // Allocate memory for the input buffer and the pointer to the pointer to the output buffer const inputBufPtr = wasm.call('bbmalloc', inputBuf.length);