diff --git a/avm-transpiler/Dockerfile b/avm-transpiler/Dockerfile index d695622a009..3d26426c478 100644 --- a/avm-transpiler/Dockerfile +++ b/avm-transpiler/Dockerfile @@ -9,6 +9,5 @@ RUN apt-get update && apt-get install -y git RUN ./scripts/bootstrap_native.sh FROM ubuntu:noble -COPY --from=0 /usr/src/avm-transpiler/scripts/compile_then_transpile.sh /usr/src/avm-transpiler/scripts/compile_then_transpile.sh COPY --from=0 /usr/src/avm-transpiler/target/release/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler ENTRYPOINT ["/usr/src/avm-transpiler/target/release/avm-transpiler"] diff --git a/avm-transpiler/Earthfile b/avm-transpiler/Earthfile index e4db70ea091..fc7ed7e5feb 100644 --- a/avm-transpiler/Earthfile +++ b/avm-transpiler/Earthfile @@ -18,7 +18,6 @@ build: --command="./scripts/bootstrap_native.sh && rm -rf target/release/{build,deps}" \ --build_artifacts="target" SAVE ARTIFACT target/release/avm-transpiler avm-transpiler - SAVE ARTIFACT scripts/compile_then_transpile.sh format: FROM +source diff --git a/avm-transpiler/scripts/compile_then_transpile.sh b/avm-transpiler/scripts/compile_then_transpile.sh deleted file mode 100755 index 97303dd314b..00000000000 --- a/avm-transpiler/scripts/compile_then_transpile.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -# This is a wrapper script for nargo. -# Pass any args that you'd normally pass to nargo. -# If the first arg is "compile", -# run nargo and then transpile any created artifacts. -# -# Usage: compile_then_transpile.sh [nargo args] -set -eu - -NARGO=${NARGO:-nargo} -TRANSPILER=${TRANSPILER:-avm-transpiler} - -if [ "${1:-}" != "compile" ]; then - # if not compiling, just pass through to nargo verbatim - $NARGO $@ - exit $? -fi -shift # remove the compile arg so we can inject --show-artifact-paths - -# Forward all arguments to nargo, tee output to console -artifacts_to_transpile=$($NARGO compile --show-artifact-paths $@ | tee /dev/tty | grep -oP 'Saved contract artifact to: \K.*') - -# NOTE: the output that is teed to /dev/tty will normally not be redirectable by the caller. -# If the script is run via docker, however, the user will see this output on stdout and will be able to redirect. - -# Transpile each artifact -# `$artifacts_to_transpile` needs to be unquoted here, otherwise it will break if there are multiple artifacts -for artifact in $artifacts_to_transpile; do - # transpiler input and output files are the same (modify in-place) - $TRANSPILER "$artifact" "$artifact" -done diff --git a/aztec-nargo/Dockerfile b/aztec-nargo/Dockerfile index 95473b82f59..2b9a48681da 100644 --- a/aztec-nargo/Dockerfile +++ b/aztec-nargo/Dockerfile @@ -4,16 +4,21 @@ FROM aztecprotocol/noir AS built-noir # to get built avm-transpiler binary FROM aztecprotocol/avm-transpiler AS built-transpiler +# to get built barretenberg binary +FROM --platform=linux/amd64 aztecprotocol/barretenberg-x86_64-linux-clang as barretenberg + FROM ubuntu:noble # Install Tini as nargo doesn't handle signals properly. # Install git as nargo needs it to clone. -RUN apt-get update && apt-get install -y git tini && rm -rf /var/lib/apt/lists/* && apt-get clean +RUN apt-get update && apt-get install -y git tini jq && rm -rf /var/lib/apt/lists/* && apt-get clean # Copy binaries to /usr/bin COPY --from=built-noir /usr/src/noir/noir-repo/target/release/nargo /usr/bin/nargo COPY --from=built-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler /usr/bin/avm-transpiler +COPY --from=barretenberg /usr/src/barretenberg/cpp/build/bin/bb /usr/bin/bb + # Copy in script that calls both binaries -COPY ./avm-transpiler/scripts/compile_then_transpile.sh /usr/src/avm-transpiler/scripts/compile_then_transpile.sh +COPY ./aztec-nargo/compile_then_postprocess.sh /usr/src/aztec-nargo/compile_then_postprocess.sh -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/src/avm-transpiler/scripts/compile_then_transpile.sh"] +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/src/aztec-nargo/compile_then_postprocess.sh"] diff --git a/aztec-nargo/Earthfile b/aztec-nargo/Earthfile index f0615e9bd65..49a32e4a3b4 100644 --- a/aztec-nargo/Earthfile +++ b/aztec-nargo/Earthfile @@ -4,17 +4,20 @@ run: FROM ubuntu:noble # Install Tini as nargo doesn't handle signals properly. # Install git as nargo needs it to clone. - RUN apt-get update && apt-get install -y git tini && rm -rf /var/lib/apt/lists/* && apt-get clean + RUN apt-get update && apt-get install -y git tini jq && rm -rf /var/lib/apt/lists/* && apt-get clean # Copy binaries to /usr/bin COPY ../noir+nargo/nargo /usr/bin/nargo COPY ../avm-transpiler+build/avm-transpiler /usr/bin/avm-transpiler + COPY ../barretenberg/cpp/+preset-release/bin/bb /usr/bin/bb + # Copy in script that calls both binaries - COPY ../avm-transpiler+build/compile_then_transpile.sh /usr/bin/compile_then_transpile.sh + COPY ./compile_then_postprocess.sh /usr/bin/compile_then_postprocess.sh ENV PATH "/usr/bin:${PATH}" - ENTRYPOINT ["/usr/bin/tini", "--", "/usr/bin/compile_then_transpile.sh"] + ENTRYPOINT ["/usr/bin/tini", "--", "/usr/bin/compile_then_postprocess.sh"] SAVE IMAGE aztecprotocol/aztec-nargo + SAVE ARTIFACT /usr/bin/compile_then_postprocess.sh /aztec-nargo export-aztec-nargo: FROM +run diff --git a/aztec-nargo/compile_then_postprocess.sh b/aztec-nargo/compile_then_postprocess.sh new file mode 100755 index 00000000000..c859377609d --- /dev/null +++ b/aztec-nargo/compile_then_postprocess.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# This is a wrapper script for nargo. +# Pass any args that you'd normally pass to nargo. +# If the first arg is "compile", +# run nargo and then postprocess any created artifacts. +# +# Usage: compile_then_postprocess.sh [nargo args] +set -eu + +NARGO=${NARGO:-nargo} +TRANSPILER=${TRANSPILER:-avm-transpiler} +BB=${BB:-bb} + +if [ "${1:-}" != "compile" ]; then + # if not compiling, just pass through to nargo verbatim + $NARGO $@ + exit $? +fi +shift # remove the compile arg so we can inject --show-artifact-paths + +# Forward all arguments to nargo, tee output to console +artifacts_to_process=$($NARGO compile --show-artifact-paths $@ | tee /dev/tty | grep -oP 'Saved contract artifact to: \K.*') + +# NOTE: the output that is teed to /dev/tty will normally not be redirectable by the caller. +# If the script is run via docker, however, the user will see this output on stdout and will be able to redirect. + +# Postprocess each artifact +# `$artifacts_to_process` needs to be unquoted here, otherwise it will break if there are multiple artifacts +for artifact in $artifacts_to_process; do + # transpiler input and output files are the same (modify in-place) + $TRANSPILER "$artifact" "$artifact" + artifact_name=$(basename "$artifact") + echo "Generating verification keys for functions in $artifact_name" + # See contract_artifact.ts (getFunctionType) for reference + private_fn_indices=$(jq -r '.functions | to_entries | map(select((.value.custom_attributes | contains(["public"]) | not) and (.value.is_unconstrained == false))) | map(.key) | join(" ")' $artifact) + for fn_index in $private_fn_indices; do + fn_name=$(jq -r ".functions[$fn_index].name" $artifact) + fn_artifact=$(jq -r ".functions[$fn_index]" $artifact) + fn_artifact_path="$artifact.function_artifact_$fn_index.json" + echo $fn_artifact > $fn_artifact_path + + echo "Generating verification key for function $fn_name" + # BB outputs the verification key to stdout as raw bytes, however, we need to base64 encode it before storing it in the artifact + verification_key=$($BB write_vk_mega_honk -h -b ${fn_artifact_path} -o - | base64) + rm $fn_artifact_path + jq ".functions[$fn_index].verification_key = \"$verification_key\"" $artifact > $artifact.tmp + mv $artifact.tmp $artifact + done +done diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 16553a32d4a..b9c1253eaf5 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -275,6 +275,15 @@ WASM_EXPORT void acir_vk_as_fields_ultra_honk(uint8_t const* vk_buf, fr::vec_out { using VerificationKey = UltraFlavor::VerificationKey; + auto verification_key = std::make_shared(from_buffer(vk_buf)); + std::vector vkey_as_fields = verification_key->to_field_elements(); + *out_vkey = to_heap_buffer(vkey_as_fields); +} + +WASM_EXPORT void acir_vk_as_fields_mega_honk(uint8_t const* vk_buf, fr::vec_out_buf out_vkey) +{ + using VerificationKey = MegaFlavor::VerificationKey; + auto verification_key = std::make_shared(from_buffer(vk_buf)); std::vector vkey_as_fields = verification_key->to_field_elements(); *out_vkey = to_heap_buffer(vkey_as_fields); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 2ed4613b666..9efd50e4721 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -93,4 +93,6 @@ WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, bool const* r WASM_EXPORT void acir_proof_as_fields_ultra_honk(uint8_t const* proof_buf, fr::vec_out_buf out); -WASM_EXPORT void acir_vk_as_fields_ultra_honk(uint8_t const* vk_buf, fr::vec_out_buf out_vkey); \ No newline at end of file +WASM_EXPORT void acir_vk_as_fields_ultra_honk(uint8_t const* vk_buf, fr::vec_out_buf out_vkey); + +WASM_EXPORT void acir_vk_as_fields_mega_honk(uint8_t const* vk_buf, fr::vec_out_buf out_vkey); \ No newline at end of file diff --git a/barretenberg/exports.json b/barretenberg/exports.json index 67c4854c607..83b2a64ec71 100644 --- a/barretenberg/exports.json +++ b/barretenberg/exports.json @@ -917,5 +917,21 @@ } ], "isAsync": false + }, + { + "functionName": "acir_vk_as_fields_mega_honk", + "inArgs": [ + { + "name": "vk_buf", + "type": "const uint8_t *" + } + ], + "outArgs": [ + { + "name": "out_vkey", + "type": "fr::vec_out_buf" + } + ], + "isAsync": false } ] \ No newline at end of file diff --git a/barretenberg/ts/src/barretenberg_api/index.ts b/barretenberg/ts/src/barretenberg_api/index.ts index 06290eda204..a6ead4041b4 100644 --- a/barretenberg/ts/src/barretenberg_api/index.ts +++ b/barretenberg/ts/src/barretenberg_api/index.ts @@ -604,6 +604,18 @@ export class BarretenbergApi { const out = result.map((r, i) => outTypes[i].fromBuffer(r)); return out[0]; } + + async acirVkAsFieldsMegaHonk(vkBuf: Uint8Array): Promise { + const inArgs = [vkBuf].map(serializeBufferable); + const outTypes: OutputType[] = [VectorDeserializer(Fr)]; + const result = await this.wasm.callWasmExport( + 'acir_vk_as_fields_mega_honk', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } } export class BarretenbergApiSync { constructor(protected wasm: BarretenbergWasm) {} @@ -1181,4 +1193,16 @@ export class BarretenbergApiSync { const out = result.map((r, i) => outTypes[i].fromBuffer(r)); return out[0]; } + + acirVkAsFieldsMegaHonk(vkBuf: Uint8Array): Fr[] { + const inArgs = [vkBuf].map(serializeBufferable); + const outTypes: OutputType[] = [VectorDeserializer(Fr)]; + const result = this.wasm.callWasmExport( + 'acir_vk_as_fields_mega_honk', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } } diff --git a/boxes/Dockerfile b/boxes/Dockerfile index 6ca01d2f468..cd6a3ead2f9 100644 --- a/boxes/Dockerfile +++ b/boxes/Dockerfile @@ -2,17 +2,23 @@ FROM aztecprotocol/aztec AS aztec FROM aztecprotocol/noir as noir FROM aztecprotocol/noir-projects as noir-projects +FROM --platform=linux/amd64 aztecprotocol/barretenberg-x86_64-linux-clang as barretenberg +FROM aztecprotocol/avm-transpiler AS transpiler # We need yarn. Start fresh container. FROM node:18.19.0 -RUN apt update && apt install netcat-openbsd +RUN apt update && apt install netcat-openbsd jq COPY --from=aztec /usr/src /usr/src -COPY --from=noir /usr/src/noir/noir-repo/target/release/nargo /usr/src/noir/noir-repo/target/release/nargo COPY --from=noir-projects /usr/src/noir-projects/aztec-nr /usr/src/noir-projects/aztec-nr COPY --from=noir-projects /usr/src/noir-projects/noir-protocol-circuits/crates/types /usr/src/noir-projects/noir-protocol-circuits/crates/types +# Copy binaries to /usr/bin +COPY --from=noir /usr/src/noir/noir-repo/target/release/nargo /usr/src/noir/noir-repo/target/release/nargo +COPY --from=transpiler /usr/src/avm-transpiler/target/release/avm-transpiler /usr/bin/avm-transpiler +COPY --from=barretenberg /usr/src/barretenberg/cpp/build/bin/bb /usr/bin/bb + WORKDIR /usr/src/boxes COPY . . -ENV AZTEC_NARGO=/usr/src/noir/noir-repo/target/release/nargo +ENV AZTEC_NARGO=/usr/aztec-nargo/compile_then_postprocess.sh ENV AZTEC_BUILDER=/usr/src/yarn-project/builder/aztec-builder-dest RUN yarn RUN npx -y playwright@1.42 install --with-deps diff --git a/boxes/Earthfile b/boxes/Earthfile index 3c349f8aa49..d2febd89e91 100644 --- a/boxes/Earthfile +++ b/boxes/Earthfile @@ -31,10 +31,15 @@ build: COPY ../noir/+nargo/nargo /usr/src/noir/noir-repo/target/release/nargo COPY ../noir-projects/+build/aztec-nr /usr/src/noir-projects/aztec-nr COPY ../noir-projects/+build/noir-protocol-circuits/crates/types /usr/src/noir-projects/noir-protocol-circuits/crates/types - + COPY ../avm-transpiler+build/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler + COPY ../barretenberg/cpp/+preset-release/bin/bb /usr/src/barretenberg/cpp/build/bin/bb + COPY ../aztec-nargo+run/aztec-nargo /usr/src/aztec-nargo WORKDIR /usr/src/boxes - ENV AZTEC_NARGO=/usr/src/noir/noir-repo/target/release/nargo + ENV NARGO=/usr/src/noir/noir-repo/target/release/nargo + ENV TRANSPILER=/usr/src/avm-transpiler/target/release/avm-transpiler + ENV BB=/usr/src/barretenberg/cpp/build/bin/bb + ENV AZTEC_NARGO=/usr/src/aztec-nargo ENV AZTEC_BUILDER=/usr/src/yarn-project/builder/aztec-builder-dest COPY . . RUN yarn build diff --git a/build_manifest.yml b/build_manifest.yml index e171cf14059..28982430bad 100644 --- a/build_manifest.yml +++ b/build_manifest.yml @@ -55,11 +55,8 @@ avm-transpiler: aztec-nargo: buildDir: . dockerfile: aztec-nargo/Dockerfile - rebuildPatterns: - - ^aztec-nargo/ - - ^avm-transpiler/ - - ^noir/ dependencies: + - barretenberg-x86_64-linux-clang - avm-transpiler - noir multiarch: buildx @@ -250,8 +247,10 @@ boxes: buildDir: boxes dependencies: - aztec - - noir - noir-projects + - noir + - avm-transpiler + - barretenberg-x86_64-linux-clang runDependencies: - aztec diff --git a/noir-projects/noir-contracts/bootstrap.sh b/noir-projects/noir-contracts/bootstrap.sh index b739b0eb429..655a7d638d0 100755 --- a/noir-projects/noir-contracts/bootstrap.sh +++ b/noir-projects/noir-contracts/bootstrap.sh @@ -19,20 +19,15 @@ echo "Compiling contracts..." NARGO=${NARGO:-../../noir/noir-repo/target/release/nargo} $NARGO compile --silence-warnings --inliner-aggressiveness 0 -echo "Generating protocol contract vks..." +echo "Transpiling contracts..." +scripts/transpile.sh + +echo "Postprocessing contracts..." BB_HASH=${BB_HASH:-$(cd ../../ && git ls-tree -r HEAD | grep 'barretenberg/cpp' | awk '{print $3}' | git hash-object --stdin)} echo Using BB hash $BB_HASH -vkDir="./target/keys" -mkdir -p $vkDir +tempDir="./target/tmp" +mkdir -p $tempDir -protocol_contracts=$(jq -r '.[]' "./protocol_contracts.json") -for contract in $protocol_contracts; do - artifactPath="./target/$contract.json" - fnNames=$(jq -r '.functions[] | select(.custom_attributes | index("private")) | .name' "$artifactPath") - for fnName in $fnNames; do - BB_HASH=$BB_HASH node ../scripts/generate_vk_json.js "$artifactPath" "$vkDir" "$fnName" - done +for artifactPath in "./target"/*.json; do + BB_HASH=$BB_HASH node ./scripts/postprocess_contract.js "$artifactPath" "$tempDir" done - -echo "Transpiling contracts..." -scripts/transpile.sh \ No newline at end of file diff --git a/noir-projects/noir-contracts/scripts/postprocess_contract.js b/noir-projects/noir-contracts/scripts/postprocess_contract.js new file mode 100644 index 00000000000..b423bf2a50f --- /dev/null +++ b/noir-projects/noir-contracts/scripts/postprocess_contract.js @@ -0,0 +1,104 @@ +const fs = require("fs/promises"); +const path = require("path"); +const { + BB_BIN_PATH, + readVKFromS3, + writeVKToS3, + generateArtifactHash, + getBarretenbergHash, +} = require("../../scripts/verification_keys"); +const child_process = require("child_process"); +const crypto = require("crypto"); + +function getFunctionArtifactPath(outputFolder, functionName) { + return path.join(outputFolder, `${functionName}.tmp.json`); +} + +function getFunctionVkPath(outputFolder, functionName) { + return path.join(outputFolder, `${functionName}.vk.tmp.bin`); +} + +async function getBytecodeHash({ bytecode }) { + if (!bytecode) { + throw new Error("No bytecode found in function artifact"); + } + return crypto.createHash("md5").update(bytecode).digest("hex"); +} + +async function generateVkForFunction(functionArtifact, outputFolder) { + const functionArtifactPath = getFunctionArtifactPath( + outputFolder, + functionArtifact.name + ); + const outputVkPath = getFunctionVkPath(outputFolder, functionArtifact.name); + + await fs.writeFile( + functionArtifactPath, + JSON.stringify(functionArtifact, null, 2) + ); + + try { + const writeVkCommand = `${BB_BIN_PATH} write_vk_mega_honk -h -b "${functionArtifactPath}" -o "${outputVkPath}" `; + + console.log("WRITE VK CMD: ", writeVkCommand); + + await new Promise((resolve, reject) => { + child_process.exec(`${writeVkCommand}`, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + const binaryVk = await fs.readFile(outputVkPath); + await fs.unlink(outputVkPath); + + return binaryVk; + } finally { + await fs.unlink(functionArtifactPath); + } +} + +async function main() { + let [artifactPath, tempFolder] = process.argv.slice(2); + const artifact = JSON.parse(await fs.readFile(artifactPath, "utf8")); + const barretenbergHash = await getBarretenbergHash(); + for (const functionArtifact of artifact.functions.filter( + // See contract_artifact.ts (getFunctionType) for reference + (functionArtifact) => + !functionArtifact.custom_attributes.includes("public") && + !functionArtifact.is_unconstrained + )) { + const artifactName = `${artifact.name}-${functionArtifact.name}`; + const artifactHash = generateArtifactHash( + barretenbergHash, + await getBytecodeHash(functionArtifact), + true, + true + ); + if ( + functionArtifact.verification_key && + functionArtifact.artifact_hash === artifactHash + ) { + console.log("Reusing on disk VK for", artifactName); + } else { + let vk = await readVKFromS3(artifactName, artifactHash, false); + if (!vk) { + vk = await generateVkForFunction(functionArtifact, tempFolder); + await writeVKToS3(artifactName, artifactHash, vk); + } else { + console.log("Using VK from remote cache for", artifactName); + } + functionArtifact.verification_key = vk.toString("base64"); + functionArtifact.artifact_hash = artifactHash; + } + } + + await fs.writeFile(artifactPath, JSON.stringify(artifact, null, 2)); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr index b4b4e20086e..566cd989cbd 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr @@ -1,8 +1,8 @@ use dep::types::{ abis::private_kernel::private_call_data::PrivateCallData, address::AztecAddress, - constants::MAX_PROTOCOL_CONTRACTS, hash::stdlib_recursion_verification_key_compress_native_vk, - merkle_tree::root::root_from_sibling_path, + constants::MAX_PROTOCOL_CONTRACTS, merkle_tree::root::root_from_sibling_path, }; +use types::debug_log::debug_log_format; pub fn validate_contract_address( private_call_data: PrivateCallData, @@ -11,13 +11,11 @@ pub fn validate_contract_address( let contract_address = private_call_data.public_inputs.call_context.contract_address; assert(!contract_address.is_zero(), "contract address cannot be zero"); - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3062): Why is this using a hash function from the stdlib::recursion namespace - let private_call_vk_hash = - stdlib_recursion_verification_key_compress_native_vk(private_call_data.vk); + private_call_data.vk.check_hash(); let computed_address = AztecAddress::compute_from_private_function( private_call_data.public_inputs.call_context.function_selector, - private_call_vk_hash, + private_call_data.vk.hash, private_call_data.function_leaf_membership_witness, private_call_data.contract_class_artifact_hash, private_call_data.contract_class_public_bytecode_commitment, @@ -26,6 +24,7 @@ pub fn validate_contract_address( ); let protocol_contract_index = contract_address.to_field(); + let computed_protocol_contract_tree_root = if (MAX_PROTOCOL_CONTRACTS as Field).lt( protocol_contract_index, ) { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr index 7928b8e889b..dd7a5df1268 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_contract_address.nr @@ -1,8 +1,7 @@ use crate::tests::private_call_data_validator_builder::PrivateCallDataValidatorBuilder; use dep::types::address::AztecAddress; -use std::embedded_curve_ops::{ - EmbeddedCurvePoint, EmbeddedCurveScalar, fixed_base_scalar_mul as derive_public_key, -}; +use std::embedded_curve_ops::{EmbeddedCurveScalar, fixed_base_scalar_mul as derive_public_key}; +use types::hash::verification_key_hash; impl PrivateCallDataValidatorBuilder { pub fn new_with_regular_contract() -> Self { @@ -103,3 +102,25 @@ fn validate_contract_address_protocol_contract_wrong_computed_address_fails() { builder.validate(); } + +#[test(should_fail_with = "Invalid VK hash")] +fn validate_contract_address_wrong_vk_hash_fails() { + let mut builder = PrivateCallDataValidatorBuilder::new_with_regular_contract(); + + // Fake the vk hash so it doesn't match the preimage + builder.private_call.client_ivc_vk.hash = 27; + + builder.validate(); +} + +#[test(should_fail_with = "computed contract address does not match expected one")] +fn validate_contract_address_wrong_vk_fails() { + let mut builder = PrivateCallDataValidatorBuilder::new_with_regular_contract(); + + // Change the VK so the address doesn't match anymore + builder.private_call.client_ivc_vk.key[0] = 27; + builder.private_call.client_ivc_vk.hash = + verification_key_hash(builder.private_call.client_ivc_vk.key); + + builder.validate(); +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr index 72f0c2485df..3dbaa0521a2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr @@ -115,7 +115,7 @@ impl AztecAddress { pub fn compute_from_private_function( function_selector: FunctionSelector, - functino_vk_hash: Field, + function_vk_hash: Field, function_leaf_membership_witness: MembershipWitness, contract_class_artifact_hash: Field, contract_class_public_bytecode_commitment: Field, @@ -124,7 +124,7 @@ impl AztecAddress { ) -> Self { let private_functions_root = private_functions_root_from_siblings( function_selector, - functino_vk_hash, + function_vk_hash, function_leaf_membership_witness.leaf_index, function_leaf_membership_witness.sibling_path, ); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr index 5de0d2cf0f3..80ccd3129dd 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr @@ -129,16 +129,6 @@ pub fn merkle_hash(left: Field, right: Field) -> Field { poseidon2_hash([left, right]) } -pub fn stdlib_recursion_verification_key_compress_native_vk( - _vk: VerificationKey, -) -> Field { - // Original cpp code - // stdlib::recursion::verification_key::compress_native(private_call.vk, GeneratorIndex::VK); - // The above cpp method is only ever called on verification key, so it has been special cased here - let _hash_index = GENERATOR_INDEX__VK; - 0 -} - pub fn compute_l2_to_l1_hash( contract_address: AztecAddress, recipient: EthAddress, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index f48dff6111c..9b90fff5998 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -41,10 +41,10 @@ use crate::{ }, address::{AztecAddress, EthAddress, SaltedInitializationHash}, constants::{ - FUNCTION_TREE_HEIGHT, MAX_ENCRYPTED_LOGS_PER_TX, MAX_FIELD_VALUE, - MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_TX, - MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, - MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NOTE_HASHES_PER_TX, + CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, FUNCTION_TREE_HEIGHT, + MAX_ENCRYPTED_LOGS_PER_TX, MAX_FIELD_VALUE, MAX_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_TX, MAX_L2_TO_L1_MSGS_PER_TX, + MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_DATA_READS_PER_CALL, @@ -205,7 +205,10 @@ impl FixtureBuilder { let contract_data = fixtures::contracts::default_contract; let contract_function = fixtures::contract_functions::default_private_function; - builder.use_contract(contract_data).use_function(contract_function) + builder.use_contract(contract_data).use_function( + contract_function, + fixtures::contract_functions::default_vk, + ) } pub fn as_parent_contract(&mut self) -> Self { @@ -248,7 +251,7 @@ impl FixtureBuilder { let _ = self.use_contract(contract_data); self.contract_address = AztecAddress::from_field(contract_index as Field); - self.use_function(function_data) + self.use_function(function_data, fixtures::contract_functions::default_vk) } pub fn use_contract(&mut self, contract_data: ContractData) -> Self { @@ -261,10 +264,15 @@ impl FixtureBuilder { *self } - pub fn use_function(&mut self, function_data: ContractFunction) -> Self { + pub fn use_function( + &mut self, + function_data: ContractFunction, + vk: [Field; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS], + ) -> Self { self.function_data = function_data.data; self.function_leaf_membership_witness = function_data.membership_witness; self.acir_hash = function_data.acir_hash; + self.client_ivc_vk = VerificationKey { key: vk, hash: function_data.vk_hash }; *self } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contract_functions.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contract_functions.nr index ffecfd4ef69..9a08c162fcc 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contract_functions.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contract_functions.nr @@ -1,5 +1,5 @@ use crate::abis::{function_data::FunctionData, function_selector::FunctionSelector}; -use crate::constants::FUNCTION_TREE_HEIGHT; +use crate::constants::{CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, FUNCTION_TREE_HEIGHT}; use crate::merkle_tree::membership::MembershipWitness; pub struct ContractFunction { @@ -9,10 +9,12 @@ pub struct ContractFunction { membership_witness: MembershipWitness, } +global default_vk = [0; CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS]; + // sibling_path taken from __snapshots__/noir_test_gen.test.ts.snap global default_private_function = ContractFunction { data: FunctionData { selector: FunctionSelector { inner: 1010101 }, is_private: true }, - vk_hash: 0, + vk_hash: crate::hash::verification_key_hash(default_vk), acir_hash: 1111, membership_witness: MembershipWitness { leaf_index: 0, @@ -48,7 +50,7 @@ pub fn get_protocol_contract_function(contract_index: u32) -> ContractFunction { selector: FunctionSelector { inner: 98989 + contract_index }, is_private: true, }, - vk_hash: 0, + vk_hash: crate::hash::verification_key_hash(default_vk), acir_hash: 5555 + contract_index as Field, membership_witness: MembershipWitness { leaf_index: contract_index as Field, diff --git a/noir-projects/scripts/generate_vk_json.js b/noir-projects/scripts/generate_vk_json.js index 47aebef30d7..c891d1f7ca4 100644 --- a/noir-projects/scripts/generate_vk_json.js +++ b/noir-projects/scripts/generate_vk_json.js @@ -1,20 +1,16 @@ const path = require("path"); const fs = require("fs/promises"); -const fs_stream = require("fs"); const child_process = require("child_process"); - -const { fromIni } = require("@aws-sdk/credential-providers"); -const { S3 } = require("@aws-sdk/client-s3"); - const crypto = require("crypto"); const megaHonkPatterns = require("../mega_honk_circuits.json"); - -const BB_BIN_PATH = - process.env.BB_BIN || - path.join(__dirname, "../../barretenberg/cpp/build/bin/bb"); -const BUCKET_NAME = "aztec-ci-artifacts"; -const PREFIX = "protocol"; +const { + readVKFromS3, + writeVKToS3, + getBarretenbergHash, + generateArtifactHash, + BB_BIN_PATH, +} = require("./verification_keys"); function vkBinaryFileNameForArtifactName(outputFolder, artifactName) { return path.join(outputFolder, `${artifactName}.vk`); @@ -28,32 +24,6 @@ function vkDataFileNameForArtifactName(outputFolder, artifactName) { return path.join(outputFolder, `${artifactName}.vk.data.json`); } -function getFunctionArtifactPath(outputFolder, functionName) { - return path.join(outputFolder, `${functionName}.tmp.json`); -} - -async function createFunctionArtifact( - contractArtifactPath, - functionName, - outputFolder -) { - const contractArtifact = JSON.parse(await fs.readFile(contractArtifactPath)); - const artifact = contractArtifact.functions.find( - (fn) => fn.name === functionName - ); - if (!artifact) { - throw new Error(`Cannot find artifact for function: ${functionName}.`); - } - - const artifactPath = getFunctionArtifactPath(outputFolder, functionName); - await fs.writeFile(artifactPath, JSON.stringify(artifact, null, 2)); - return artifactPath; -} - -async function removeFunctionArtifact(artifactPath) { - await fs.unlink(artifactPath); -} - async function getBytecodeHash(artifactPath) { const { bytecode } = JSON.parse(await fs.readFile(artifactPath)); if (!bytecode) { @@ -62,37 +32,6 @@ async function getBytecodeHash(artifactPath) { return crypto.createHash("md5").update(bytecode).digest("hex"); } -function getBarretenbergHash() { - if (process.env.BB_HASH) { - return Promise.resolve(process.env.BB_HASH); - } - return new Promise((res, rej) => { - const hash = crypto.createHash("md5"); - - const rStream = fs_stream.createReadStream(BB_BIN_PATH); - rStream.on("data", (data) => { - hash.update(data); - }); - rStream.on("end", () => { - res(hash.digest("hex")); - }); - rStream.on("error", (err) => { - rej(err); - }); - }); -} - -function generateArtifactHash( - barretenbergHash, - bytecodeHash, - isMegaHonk, - isRecursive -) { - return `${barretenbergHash}-${bytecodeHash}-${ - isMegaHonk ? "mega-honk" : "ultra-honk" - }-${isRecursive}`; -} - async function getArtifactHash(artifactPath, isMegaHonk, isRecursive) { const bytecodeHash = await getBytecodeHash(artifactPath); const barretenbergHash = await getBarretenbergHash(); @@ -128,12 +67,7 @@ function isMegaHonkCircuit(artifactName) { ); } -async function processArtifact( - artifactPath, - artifactName, - outputFolder, - syncWithS3 -) { +async function processArtifact(artifactPath, artifactName, outputFolder) { const isMegaHonk = isMegaHonkCircuit(artifactName); const isRecursive = true; @@ -151,9 +85,7 @@ async function processArtifact( return; } - let vkData = syncWithS3 - ? await readVKFromS3(artifactName, artifactHash) - : undefined; + let vkData = await readVKFromS3(artifactName, artifactHash); if (!vkData) { vkData = await generateVKData( artifactName, @@ -163,9 +95,7 @@ async function processArtifact( isMegaHonk, isRecursive ); - if (syncWithS3) { - await writeVKToS3(artifactName, artifactHash, JSON.stringify(vkData)); - } + await writeVKToS3(artifactName, artifactHash, JSON.stringify(vkData)); } else { console.log("Using VK from remote cache for", artifactName); } @@ -230,84 +160,19 @@ async function generateVKData( } async function main() { - let [artifactPath, outputFolder, functionName] = process.argv.slice(2); + let [artifactPath, outputFolder] = process.argv.slice(2); if (!artifactPath || !outputFolder) { console.log( - "Usage: node generate_vk_json.js [functionName]" + "Usage: node generate_vk_json.js " ); return; } - const sourceArtifactPath = !functionName - ? artifactPath - : await createFunctionArtifact(artifactPath, functionName, outputFolder); - - const artifactName = [ - path.basename(artifactPath, ".json"), - functionName ? `-${functionName}` : "", - ].join(""); - - const syncWithS3 = true; - await processArtifact( - sourceArtifactPath, - artifactName, - outputFolder, - syncWithS3 + artifactPath, + path.basename(artifactPath, ".json"), + outputFolder ); - - if (sourceArtifactPath !== artifactPath) { - await removeFunctionArtifact(sourceArtifactPath); - } -} - -function generateS3Client() { - return new S3({ - credentials: fromIni({ - profile: "default", - }), - region: "us-east-2", - }); -} - -async function writeVKToS3(artifactName, artifactHash, body) { - if (process.env.DISABLE_VK_S3_CACHE) { - return; - } - try { - const s3 = generateS3Client(); - await s3.putObject({ - Bucket: BUCKET_NAME, - Key: `${PREFIX}/${artifactName}-${artifactHash}.json`, - Body: body, - }); - } catch (err) { - console.warn("Could not write to S3 VK remote cache", err.message); - } -} - -async function readVKFromS3(artifactName, artifactHash) { - if (process.env.DISABLE_VK_S3_CACHE) { - return; - } - const key = `${PREFIX}/${artifactName}-${artifactHash}.json`; - - try { - const s3 = generateS3Client(); - const { Body: response } = await s3.getObject({ - Bucket: BUCKET_NAME, - Key: key, - }); - - const result = JSON.parse(await response.transformToString()); - return result; - } catch (err) { - console.warn( - `Could not read VK from remote cache at s3://${BUCKET_NAME}/${key}`, - err.message - ); - return undefined; - } } main().catch((err) => { diff --git a/noir-projects/scripts/verification_keys.js b/noir-projects/scripts/verification_keys.js new file mode 100644 index 00000000000..90fd90cb991 --- /dev/null +++ b/noir-projects/scripts/verification_keys.js @@ -0,0 +1,104 @@ +const { fromIni } = require("@aws-sdk/credential-providers"); +const { S3 } = require("@aws-sdk/client-s3"); +const fs_stream = require("fs"); +const path = require("path"); + +const BB_BIN_PATH = + process.env.BB_BIN || + path.join(__dirname, "../../barretenberg/cpp/build/bin/bb"); +const BUCKET_NAME = "aztec-ci-artifacts"; +const PREFIX = "protocol"; + +async function writeVKToS3(artifactName, artifactHash, body) { + if (process.env.DISABLE_VK_S3_CACHE) { + return; + } + try { + const s3 = generateS3Client(); + await s3.putObject({ + Bucket: BUCKET_NAME, + Key: `${PREFIX}/${artifactName}-${artifactHash}.json`, + Body: body, + }); + } catch (err) { + console.warn("Could not write to S3 VK remote cache", err.message); + } +} + +async function readVKFromS3(artifactName, artifactHash, json = true) { + if (process.env.DISABLE_VK_S3_CACHE) { + return; + } + const key = `${PREFIX}/${artifactName}-${artifactHash}.json`; + + try { + const s3 = generateS3Client(); + const { Body: response } = await s3.getObject({ + Bucket: BUCKET_NAME, + Key: key, + }); + + if (json) { + const result = JSON.parse(await response.transformToString()); + return result; + } else { + return Buffer.from(await response.transformToByteArray()); + } + } catch (err) { + if (err.name !== "NoSuchKey") { + console.warn( + `Could not read VK from remote cache at s3://${BUCKET_NAME}/${key}`, + err.message + ); + } + return undefined; + } +} + +function generateS3Client() { + return new S3({ + credentials: fromIni({ + profile: "default", + }), + region: "us-east-2", + }); +} + +function generateArtifactHash( + barretenbergHash, + bytecodeHash, + isMegaHonk, + isRecursive +) { + return `${barretenbergHash}-${bytecodeHash}-${ + isMegaHonk ? "mega-honk" : "ultra-honk" + }-${isRecursive}`; +} + +function getBarretenbergHash() { + if (process.env.BB_HASH) { + return Promise.resolve(process.env.BB_HASH); + } + return new Promise((res, rej) => { + const hash = crypto.createHash("md5"); + + const rStream = fs_stream.createReadStream(BB_BIN_PATH); + rStream.on("data", (data) => { + hash.update(data); + }); + rStream.on("end", () => { + res(hash.digest("hex")); + }); + rStream.on("error", (err) => { + rej(err); + }); + }); +} + +module.exports = { + BB_BIN_PATH, + writeVKToS3, + readVKFromS3, + generateArtifactHash, + getBarretenbergHash, +}; diff --git a/yarn-project/aztec.js/src/deployment/broadcast_function.ts b/yarn-project/aztec.js/src/deployment/broadcast_function.ts index 6dcf5b6ec0a..599d2d4b63c 100644 --- a/yarn-project/aztec.js/src/deployment/broadcast_function.ts +++ b/yarn-project/aztec.js/src/deployment/broadcast_function.ts @@ -44,7 +44,7 @@ export async function broadcastPrivateFunction( privateFunctionTreeLeafIndex, } = createPrivateFunctionMembershipProof(selector, artifact); - const vkHash = computeVerificationKeyHash(privateFunctionArtifact.verificationKey!); + const vkHash = computeVerificationKeyHash(privateFunctionArtifact); const bytecode = bufferAsFields( privateFunctionArtifact.bytecode, MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS, diff --git a/yarn-project/bb-prover/src/verification_key/verification_key_data.ts b/yarn-project/bb-prover/src/verification_key/verification_key_data.ts index fe4c55a31cf..14f5eb28c59 100644 --- a/yarn-project/bb-prover/src/verification_key/verification_key_data.ts +++ b/yarn-project/bb-prover/src/verification_key/verification_key_data.ts @@ -4,7 +4,7 @@ import { VerificationKeyAsFields, VerificationKeyData, } from '@aztec/circuits.js'; -import { hashVk } from '@aztec/noir-protocol-circuits-types'; +import { hashVK } from '@aztec/circuits.js/hash'; import { strict as assert } from 'assert'; import * as fs from 'fs/promises'; @@ -25,7 +25,7 @@ export async function extractVkData(vkDirectoryPath: string): Promise { artifact = getBenchmarkContractArtifact(); contractClass = getContractClassFromArtifact(artifact); privateFunction = artifact.functions.findLast(fn => fn.functionType === FunctionType.PRIVATE)!; - vkHash = computeVerificationKeyHash(privateFunction.verificationKey!); + vkHash = computeVerificationKeyHash(privateFunction); selector = FunctionSelector.fromNameAndParameters(privateFunction); }); diff --git a/yarn-project/circuits.js/src/hash/hash.ts b/yarn-project/circuits.js/src/hash/hash.ts index 932fbcf54a9..a6ca04d5227 100644 --- a/yarn-project/circuits.js/src/hash/hash.ts +++ b/yarn-project/circuits.js/src/hash/hash.ts @@ -1,31 +1,17 @@ import { type AztecAddress } from '@aztec/foundation/aztec-address'; -import { pedersenHashBuffer, poseidon2HashWithSeparator, sha256Trunc } from '@aztec/foundation/crypto'; +import { poseidon2Hash, poseidon2HashWithSeparator, sha256Trunc } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; -import { numToUInt8, numToUInt16BE, numToUInt32BE } from '@aztec/foundation/serialize'; import { GeneratorIndex } from '../constants.gen.js'; import { type ScopedL2ToL1Message } from '../structs/l2_to_l1_message.js'; -import { VerificationKey } from '../structs/verification_key.js'; /** * Computes a hash of a given verification key. - * @param vkBuf - The verification key. + * @param vkBuf - The verification key as fields. * @returns The hash of the verification key. */ -export function hashVK(vkBuf: Buffer) { - const vk = VerificationKey.fromBuffer(vkBuf); - const toHash = Buffer.concat([ - numToUInt8(vk.circuitType), - numToUInt16BE(5), // fr::coset_generator(0)? - numToUInt32BE(vk.circuitSize), - numToUInt32BE(vk.numPublicInputs), - ...Object.values(vk.commitments) - .map(e => [e.y.toBuffer(), e.x.toBuffer()]) - .flat(), - // Montgomery form of fr::one()? Not sure. But if so, why? - Buffer.from('1418144d5b080fcac24cdb7649bdadf246a6cb2426e324bedb94fb05118f023a', 'hex'), - ]); - return pedersenHashBuffer(toHash); +export function hashVK(keyAsFields: Fr[]): Fr { + return poseidon2Hash(keyAsFields); } /** diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index a1d9c6aaab8..a762d4204f2 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -222,7 +222,7 @@ export interface FunctionAbi { export interface FunctionArtifact extends FunctionAbi { /** The ACIR bytecode of the function. */ bytecode: Buffer; - /** The verification key of the function. */ + /** The verification key of the function, base64 encoded, if it's a private fn. */ verificationKey?: string; /** Maps opcodes to source code pointers */ debugSymbols: string; diff --git a/yarn-project/foundation/src/crypto/index.ts b/yarn-project/foundation/src/crypto/index.ts index 4a93708f498..9385f359563 100644 --- a/yarn-project/foundation/src/crypto/index.ts +++ b/yarn-project/foundation/src/crypto/index.ts @@ -7,6 +7,7 @@ export * from './sha512/index.js'; export * from './pedersen/index.js'; export * from './poseidon/index.js'; export * from './secp256k1-signer/index.js'; +export * from './keys/index.js'; /** * Init the bb singleton. This constructs (if not already) the barretenberg sync api within bb.js itself. diff --git a/yarn-project/foundation/src/crypto/keys/index.ts b/yarn-project/foundation/src/crypto/keys/index.ts new file mode 100644 index 00000000000..7b2066717a2 --- /dev/null +++ b/yarn-project/foundation/src/crypto/keys/index.ts @@ -0,0 +1,9 @@ +import { BarretenbergSync, RawBuffer } from '@aztec/bb.js'; + +import { Fr } from '../../fields/fields.js'; + +export function vkAsFieldsMegaHonk(input: Buffer): Fr[] { + return BarretenbergSync.getSingleton() + .acirVkAsFieldsMegaHonk(new RawBuffer(input)) + .map(bbFr => Fr.fromBuffer(Buffer.from(bbFr.toBuffer()))); // TODO(#4189): remove this conversion +} diff --git a/yarn-project/noir-protocol-circuits-types/src/index.ts b/yarn-project/noir-protocol-circuits-types/src/index.ts index d592b373f22..bfda8a89930 100644 --- a/yarn-project/noir-protocol-circuits-types/src/index.ts +++ b/yarn-project/noir-protocol-circuits-types/src/index.ts @@ -108,7 +108,6 @@ export * from './artifacts.js'; export { maxPrivateKernelResetDimensions, privateKernelResetDimensionsConfig } from './private_kernel_reset_data.js'; export * from './utils/private_kernel_reset.js'; export * from './vks.js'; -export { hashVk } from './utils/vk_json.js'; /* eslint-disable camelcase */ diff --git a/yarn-project/noir-protocol-circuits-types/src/scripts/generate_vk_hashes.ts b/yarn-project/noir-protocol-circuits-types/src/scripts/generate_vk_hashes.ts index d8bc6e0ac96..a0a2573e9e0 100644 --- a/yarn-project/noir-protocol-circuits-types/src/scripts/generate_vk_hashes.ts +++ b/yarn-project/noir-protocol-circuits-types/src/scripts/generate_vk_hashes.ts @@ -1,12 +1,11 @@ import { Fr, VerificationKeyData } from '@aztec/circuits.js'; +import { hashVK } from '@aztec/circuits.js/hash'; import { createConsoleLogger } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; import fs from 'fs/promises'; import { join } from 'path'; -import { hashVk } from '../utils/vk_json.js'; - const log = createConsoleLogger('aztec:autogenerate'); function resolveRelativePath(relativePath: string) { @@ -34,7 +33,7 @@ const main = async () => { if (!content.vkHash) { const { keyAsFields } = content; - content.vkHash = hashVk(keyAsFields.map((str: string) => Fr.fromString(str))).toString(); + content.vkHash = hashVK(keyAsFields.map((str: string) => Fr.fromString(str))).toString(); await fs.writeFile(keyPath, JSON.stringify(content, null, 2)); } } diff --git a/yarn-project/noir-protocol-circuits-types/src/utils/vk_json.ts b/yarn-project/noir-protocol-circuits-types/src/utils/vk_json.ts index 6176bcbb68f..2640a9e5cbb 100644 --- a/yarn-project/noir-protocol-circuits-types/src/utils/vk_json.ts +++ b/yarn-project/noir-protocol-circuits-types/src/utils/vk_json.ts @@ -1,5 +1,4 @@ import { Fr, VerificationKeyAsFields, VerificationKeyData } from '@aztec/circuits.js'; -import { poseidon2Hash } from '@aztec/foundation/crypto'; interface VkJson { keyAsBytes: string; @@ -17,7 +16,3 @@ export function keyJsonToVKData(json: VkJson): VerificationKeyData { Buffer.from(keyAsBytes, 'hex'), ); } - -export function hashVk(keyAsFields: Fr[]): Fr { - return poseidon2Hash(keyAsFields); -} diff --git a/yarn-project/protocol-contracts/src/protocol_contract_data.ts b/yarn-project/protocol-contracts/src/protocol_contract_data.ts index db770ce0da4..3e901637877 100644 --- a/yarn-project/protocol-contracts/src/protocol_contract_data.ts +++ b/yarn-project/protocol-contracts/src/protocol_contract_data.ts @@ -50,14 +50,14 @@ export const ProtocolContractAddress: Record }; export const ProtocolContractLeaf = { - AuthRegistry: Fr.fromString('0x04d70cb3d8222ae04cfa59e8bfed4f804832aaaef4f485d1debb004d1b9d6362'), - ContractInstanceDeployer: Fr.fromString('0x04a661c9d4d295fc485a7e0f3de40c09b35366343bce8ad229106a8ef4076fe5'), - ContractClassRegisterer: Fr.fromString('0x147ba3294403576dbad10f86d3ffd4eb83fb230ffbcd5c8b153dd02942d0611f'), - MultiCallEntrypoint: Fr.fromString('0x154b701b41d6cf6da7204fef36b2ee9578b449d21b3792a9287bf45eba48fd26'), - FeeJuice: Fr.fromString('0x1067e9dc15d3046b6d21aaa8eafcfec88216217242cee3f9d722165ffc03c767'), - Router: Fr.fromString('0x16ab75e4efc0964c0ee3d715ac645d7972b722bfe60eea730a60b527c0681973'), + AuthRegistry: Fr.fromString('0x00d6c808f3c8a78645cce0ba37e17837da720d37a42d30814ce3aa80bb273e53'), + ContractInstanceDeployer: Fr.fromString('0x144e518ae79c22843ce5736fa723cbc072b93cb4508500f779037d5114c88310'), + ContractClassRegisterer: Fr.fromString('0x0503a6a49c9671be4b6d03be3db2bb36440631062755c776e9838e05a9afb1bd'), + MultiCallEntrypoint: Fr.fromString('0x2be4d47f4c42bf7c74e75387229c8e0cc89d0d086449122a5265abdf5ea70129'), + FeeJuice: Fr.fromString('0x1a47084d9b143a50a18292b2677588b3d575a473a0edc11466696f5e1f434fb1'), + Router: Fr.fromString('0x26d7b664d410b94a7b0543defc361669cae93382087d92f875a411910c695167'), }; export const protocolContractTreeRoot = Fr.fromString( - '0x2673f1d0618d2c98ccb3a11282073002f73335c4791eac16f67bf522e24151d1', + '0x141e7aceb024c6b5aa82f9d5a9da7207bdb2953679674a5ee306290a193d674c', ); diff --git a/yarn-project/protocol-contracts/src/scripts/generate_data.ts b/yarn-project/protocol-contracts/src/scripts/generate_data.ts index d42345c68e9..0d226bcc99f 100644 --- a/yarn-project/protocol-contracts/src/scripts/generate_data.ts +++ b/yarn-project/protocol-contracts/src/scripts/generate_data.ts @@ -50,10 +50,6 @@ async function clearDestDir() { await fs.mkdir(destArtifactsDir, { recursive: true }); } -function getPrivateFunctionNames(artifact: NoirCompiledContract) { - return artifact.functions.filter(fn => fn.custom_attributes.includes('private')).map(fn => fn.name); -} - async function copyArtifact(srcName: string, destName: string) { const src = path.join(srcPath, `${srcName}.json`); const artifact = JSON.parse(await fs.readFile(src, 'utf8')) as NoirCompiledContract; @@ -62,17 +58,6 @@ async function copyArtifact(srcName: string, destName: string) { return artifact; } -async function copyVks(srcName: string, destName: string, fnNames: string[]) { - const deskVksDir = path.join(destArtifactsDir, 'keys', destName); - await fs.mkdir(deskVksDir, { recursive: true }); - - for (const fnName of fnNames) { - const src = path.join(srcPath, 'keys', `${srcName}-${fnName}.vk.data.json`); - const dest = path.join(deskVksDir, `${fnName}.vk.data.json`); - await fs.copyFile(src, dest); - } -} - function computeContractLeaf(artifact: NoirCompiledContract) { const instance = getContractInstanceFromDeployParams(loadContractArtifact(artifact), { salt }); return instance.address; @@ -191,8 +176,6 @@ async function main() { const srcName = srcNames[i]; const destName = destNames[i]; const artifact = await copyArtifact(srcName, destName); - const fnNames = getPrivateFunctionNames(artifact); - await copyVks(srcName, destName, fnNames); await generateDeclarationFile(destName); leaves.push(computeContractLeaf(artifact)); } diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts index d4ce8006693..0a949026414 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts @@ -23,7 +23,9 @@ import { VK_TREE_HEIGHT, VerificationKeyAsFields, } from '@aztec/circuits.js'; +import { hashVK } from '@aztec/circuits.js/hash'; import { makeTuple } from '@aztec/foundation/array'; +import { vkAsFieldsMegaHonk } from '@aztec/foundation/crypto'; import { createDebugLogger } from '@aztec/foundation/log'; import { assertLength } from '@aztec/foundation/serialize'; import { pushTestData } from '@aztec/foundation/testing'; @@ -143,8 +145,7 @@ export class KernelProver { await addGateCount(functionName as string, currentExecution.acir); } - const appVk = await this.proofCreator.computeAppCircuitVerificationKey(currentExecution.acir, functionName); - const privateCallData = await this.createPrivateCallData(currentExecution, appVk.verificationKey); + const privateCallData = await this.createPrivateCallData(currentExecution); if (firstIteration) { const proofInput = new PrivateKernelInitCircuitPrivateInputs( @@ -241,9 +242,12 @@ export class KernelProver { return tailOutput; } - private async createPrivateCallData({ publicInputs }: PrivateExecutionResult, vk: VerificationKeyAsFields) { + private async createPrivateCallData({ publicInputs, vk: vkAsBuffer }: PrivateExecutionResult) { const { contractAddress, functionSelector } = publicInputs.callContext; + const vkAsFields = vkAsFieldsMegaHonk(vkAsBuffer); + const vk = new VerificationKeyAsFields(vkAsFields, hashVK(vkAsFields)); + const functionLeafMembershipWitness = await this.oracle.getFunctionMembershipWitness( contractAddress, functionSelector, diff --git a/yarn-project/simulator/src/client/private_execution.ts b/yarn-project/simulator/src/client/private_execution.ts index 0d117501df2..2a4512be96c 100644 --- a/yarn-project/simulator/src/client/private_execution.ts +++ b/yarn-project/simulator/src/client/private_execution.ts @@ -76,7 +76,7 @@ export async function executePrivateFunction( return new PrivateExecutionResult( acir, - Buffer.from(artifact.verificationKey!, 'hex'), + Buffer.from(artifact.verificationKey!, 'base64'), partialWitness, publicInputs, noteHashLeafIndexMap, diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index 73896129f2f..e6c12bd4427 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -23,7 +23,6 @@ import { AZTEC_VIEW_ATTRIBUTE, type NoirCompiledContract, } from '../noir/index.js'; -import { mockVerificationKey } from './mocked_keys.js'; /** * Serializes a contract artifact to a buffer for storage. @@ -129,7 +128,7 @@ function generateFunctionParameter(param: NoirCompiledContractFunctionParameter) type NoirCompiledContractFunction = NoirCompiledContract['functions'][number]; /** - * Generates a function build artifact. Replaces verification key with a mock value. + * Generates a function build artifact. * @param fn - Noir function entry. * @param contract - Parent contract. * @returns Function artifact. @@ -180,7 +179,7 @@ function generateFunctionArtifact(fn: NoirCompiledContractFunction, contract: No parameters, returnTypes, bytecode: Buffer.from(fn.bytecode, 'base64'), - verificationKey: mockVerificationKey, + verificationKey: fn.verification_key, debugSymbols: fn.debug_symbols, errorTypes: fn.abi.error_types, };