diff --git a/barretenberg/acir_tests/bbjs-test/src/index.ts b/barretenberg/acir_tests/bbjs-test/src/index.ts index a40a0bc919c2..18420da2a368 100644 --- a/barretenberg/acir_tests/bbjs-test/src/index.ts +++ b/barretenberg/acir_tests/bbjs-test/src/index.ts @@ -12,7 +12,8 @@ const BYTES_PER_FIELD = 32; const UH_PROOF_LENGTH_IN_BYTES = UH_PROOF_FIELDS_LENGTH * BYTES_PER_FIELD; const proofPath = (dir: string) => path.join(dir, "proof"); -const publicInputsPath = (dir: string) => path.join(dir, "public-inputs"); +const publicInputsAsFieldsPath = (dir: string) => + path.join(dir, "public_inputs_fields.json"); const vkeyPath = (dir: string) => path.join(dir, "vk"); async function generateProof({ @@ -33,19 +34,33 @@ async function generateProof({ debug(`Generating proof for ${bytecodePath}...`); const circuitArtifact = await fs.readFile(bytecodePath); const bytecode = JSON.parse(circuitArtifact.toString()).bytecode; - const backend = new UltraHonkBackend(bytecode, { threads: multiThreaded ? 8 : 1 }); + const backend = new UltraHonkBackend(bytecode, { + threads: multiThreaded ? 8 : 1, + }); const witness = await fs.readFile(witnessPath); - const proof = await backend.generateProof(new Uint8Array(witness), { keccak: (oracleHash === "keccak") }); - assert(proof.proof.length === UH_PROOF_LENGTH_IN_BYTES, `Unexpected proof length ${proof.proof.length} for ${bytecodePath}`); + const proof = await backend.generateProof(new Uint8Array(witness), { + keccak: oracleHash === "keccak", + }); + assert( + proof.proof.length === UH_PROOF_LENGTH_IN_BYTES, + `Unexpected proof length ${proof.proof.length} for ${bytecodePath}` + ); await fs.writeFile(proofPath(outputDirectory), Buffer.from(proof.proof)); debug("Proof written to " + proofPath(outputDirectory)); - await fs.writeFile(publicInputsPath(outputDirectory), JSON.stringify(proof.publicInputs)); - debug("Public inputs written to " + publicInputsPath(outputDirectory)); + await fs.writeFile( + publicInputsAsFieldsPath(outputDirectory), + JSON.stringify(proof.publicInputs) + ); + debug( + "Public inputs written to " + publicInputsAsFieldsPath(outputDirectory) + ); - const verificationKey = await backend.getVerificationKey({ keccak: (oracleHash === "keccak") }); + const verificationKey = await backend.getVerificationKey({ + keccak: oracleHash === "keccak", + }); await fs.writeFile(vkeyPath(outputDirectory), Buffer.from(verificationKey)); debug("Verification key written to " + vkeyPath(outputDirectory)); @@ -58,9 +73,14 @@ async function verifyProof({ directory }: { directory: string }) { const verifier = new BarretenbergVerifier(); const proof = await fs.readFile(proofPath(directory)); - assert(proof.length === UH_PROOF_LENGTH_IN_BYTES, `Unexpected proof length ${proof.length}`); + assert( + proof.length === UH_PROOF_LENGTH_IN_BYTES, + `Unexpected proof length ${proof.length}` + ); - const publicInputs = JSON.parse(await fs.readFile(publicInputsPath(directory), "utf8")); + const publicInputs = JSON.parse( + await fs.readFile(publicInputsAsFieldsPath(directory), "utf8") + ); const vkey = await fs.readFile(vkeyPath(directory)); const verified = await verifier.verifyUltraHonkProof( diff --git a/barretenberg/acir_tests/bootstrap.sh b/barretenberg/acir_tests/bootstrap.sh index 2645b199f062..1a1e6f36b369 100755 --- a/barretenberg/acir_tests/bootstrap.sh +++ b/barretenberg/acir_tests/bootstrap.sh @@ -38,12 +38,10 @@ function run_proof_generation { dump_fail "$prove_cmd" local vk_fields=$(cat "$outdir/vk_fields.json") + local public_inputs_fields=$(cat "$outdir/public_inputs_fields.json") local proof_fields=$(cat "$outdir/proof_fields.json") - local num_inner_public_inputs=$(( 16#$(echo "$vk_fields" | jq -r '.[1] | ltrimstr("0x")') - adjustment )) - echo "num_inner_public_inputs for $program = $num_inner_public_inputs" - - generate_toml "$program" "$vk_fields" "$proof_fields" "$num_inner_public_inputs" + generate_toml "$program" "$vk_fields" "$proof_fields" "$public_inputs_fields" } function generate_toml { @@ -56,15 +54,15 @@ function generate_toml { jq -nr \ --arg key_hash "$key_hash" \ - --argjson vkf "$vk_fields" \ - --argjson prooff "$proof_fields" \ - --argjson num_inner_public_inputs "$num_inner_public_inputs" \ + --argjson vk_f "$vk_fields" \ + --argjson public_inputs_f "$public_inputs_fields" \ + --argjson proof_f "$proof_fields" \ '[ "key_hash = \($key_hash)", - "proof = [\($prooff | .[$num_inner_public_inputs:] | map("\"" + . + "\"") | join(", "))]", - "public_inputs = [\($prooff | .[:$num_inner_public_inputs] | map("\"" + . + "\"") | join(", "))]", - "verification_key = [\($vkf | map("\"" + . + "\"") | join(", "))]" - '"$( [[ $program == *"double"* ]] && echo ',"proof_b = [\($prooff | .[$num_inner_public_inputs:] | map("\"" + . + "\"") | join(", "))]"' )"' + "proof = [\($proof_f | map("\"" + . + "\"") | join(", "))]", + "public_inputs = [\($public_inputs_f | map("\"" + . + "\"") | join(", "))]", + "verification_key = [\($vk_f | map("\"" + . + "\"") | join(", "))]" + '"$( [[ $program == *"double"* ]] && echo ',"proof_b = [\($proof_f | map("\"" + . + "\"") | join(", "))]"' )"' ] | join("\n")' > "$output_file" } diff --git a/barretenberg/acir_tests/flows/bb_prove_bbjs_verify.sh b/barretenberg/acir_tests/flows/bb_prove_bbjs_verify.sh index 345c3c261030..843bad92880a 100755 --- a/barretenberg/acir_tests/flows/bb_prove_bbjs_verify.sh +++ b/barretenberg/acir_tests/flows/bb_prove_bbjs_verify.sh @@ -36,7 +36,7 @@ $BIN write_vk \ PROOF_FIELDS_LENGTH=$(jq 'length' $output_dir/proof_fields.json) UH_PROOF_FIELDS_LENGTH=440 NUM_PUBLIC_INPUTS=$((PROOF_FIELDS_LENGTH - UH_PROOF_FIELDS_LENGTH)) -jq ".[:$NUM_PUBLIC_INPUTS]" $output_dir/proof_fields.json > $output_dir/public-inputs +jq ".[:$NUM_PUBLIC_INPUTS]" $output_dir/proof_fields.json > $output_dir/public_inputs_fields.json # Remove public inputs from the proof (first NUM_PUBLIC_INPUTS*32 bytes) # Also remove the first 4 bytes, which is the proof length in fields diff --git a/barretenberg/acir_tests/flows/bbjs_prove_bb_verify.sh b/barretenberg/acir_tests/flows/bbjs_prove_bb_verify.sh index ebfb7b70aa5a..403e0864fe0c 100755 --- a/barretenberg/acir_tests/flows/bbjs_prove_bb_verify.sh +++ b/barretenberg/acir_tests/flows/bbjs_prove_bb_verify.sh @@ -13,7 +13,7 @@ output_dir=$artifact_dir/bb-bbjs-tmp mkdir -p $output_dir # Cleanup on exit -trap "rm -rf $output_dir" EXIT +# trap "rm -rf $output_dir" EXIT # Writes the proof, public inputs ./target; this also writes the VK node ../../bbjs-test prove \ @@ -24,25 +24,34 @@ node ../../bbjs-test prove \ # Join the proof and public inputs to a single file # this will not be needed after #11024 -NUM_PUBLIC_INPUTS=$(cat $output_dir/public-inputs | jq 'length') +NUM_PUBLIC_INPUTS=$(cat $output_dir/public_inputs_fields.json | jq 'length') UH_PROOF_FIELDS_LENGTH=440 -PROOF_AND_PI_LENGTH_IN_FIELDS=$((NUM_PUBLIC_INPUTS + UH_PROOF_FIELDS_LENGTH)) +PROOF_LENGTH_IN_FIELDS=$((UH_PROOF_FIELDS_LENGTH)) +PI_LENGTH_IN_FIELDS=$((NUM_PUBLIC_INPUTS)) # First 4 bytes is PROOF_AND_PI_LENGTH_IN_FIELDS -proof_header=$(printf "%08x" $PROOF_AND_PI_LENGTH_IN_FIELDS) +proof_header=$(printf "%08x" $PROOF_LENGTH_IN_FIELDS) +pi_header=$(printf "%08x" $PI_LENGTH_IN_FIELDS) proof_bytes=$(cat $output_dir/proof | xxd -p) -public_inputs=$(cat $output_dir/public-inputs | jq -r '.[]') +public_inputs=$(cat $output_dir/public_inputs_fields.json | jq -r '.[]') public_inputs_bytes="" for input in $public_inputs; do public_inputs_bytes+=$input done -# Combine proof header, public inputs, and the proof to a single file -echo -n $proof_header$public_inputs_bytes$proof_bytes | xxd -r -p > $output_dir/proof +# Combine proof header and the proof to a single file +echo -n $proof_header$proof_bytes | xxd -r -p > $output_dir/proof +echo -n $pi_header$public_inputs_bytes | xxd -r -p > $output_dir/public_inputs +echo "$BIN verify \ + --scheme ultra_honk \ + -k $output_dir/vk \ + -p $output_dir/proof \ + -i $output_dir/public_inputs" # Verify the proof with bb cli $BIN verify \ --scheme ultra_honk \ -k $output_dir/vk \ - -p $output_dir/proof + -p $output_dir/proof \ + -i $output_dir/public_inputs diff --git a/barretenberg/acir_tests/flows/bbjs_prove_sol_verify.sh b/barretenberg/acir_tests/flows/bbjs_prove_sol_verify.sh index 7fbf519a5a91..0d4a9fb5f301 100755 --- a/barretenberg/acir_tests/flows/bbjs_prove_sol_verify.sh +++ b/barretenberg/acir_tests/flows/bbjs_prove_sol_verify.sh @@ -31,7 +31,7 @@ $BIN write_solidity_verifier --scheme ultra_honk -k $VK -o $VERIFIER_PATH # Verify the proof using the solidity verifier export PROOF=$output_dir/proof -export PUBLIC_INPUTS=$output_dir/public-inputs +export PUBLIC_INPUTS_AS_FIELDS=$output_dir/public_inputs_fields.json export TEST_PATH=$(realpath "../../sol-test/HonkTest.sol") export TESTING_HONK="true" export TEST_NAME=$(basename $(realpath ./)) diff --git a/barretenberg/acir_tests/flows/prove_then_verify.sh b/barretenberg/acir_tests/flows/prove_then_verify.sh index b26d547653f8..6d738105e5da 100755 --- a/barretenberg/acir_tests/flows/prove_then_verify.sh +++ b/barretenberg/acir_tests/flows/prove_then_verify.sh @@ -45,9 +45,14 @@ case ${SYS:-} in FLAGS+=" --scheme $SYS --oracle_hash ${HASH:-poseidon2}" [ "${ROLLUP:-false}" = "true" ] && FLAGS+=" --ipa_accumulation" [ "${RECURSIVE}" = "true" ] && FLAGS+=" --init_kzg_accumulator" + + OUTDIR=$(mktemp -d) + trap "rm -rf $OUTDIR" EXIT + $BIN prove $FLAGS $BFLAG -o $OUTDIR $BIN verify $FLAGS \ -k <($BIN write_vk $FLAGS $BFLAG -o - ) \ - -p <($BIN prove $FLAGS $BFLAG -o - ) + -p $OUTDIR/proof \ + -i $OUTDIR/public_inputs ;; "ultra_honk_deprecated") # deprecated flow is necessary until we finish C++ api refactor and then align ts api diff --git a/barretenberg/acir_tests/flows/sol_honk.sh b/barretenberg/acir_tests/flows/sol_honk.sh index 1d3dcada3107..3f961e75d72a 100755 --- a/barretenberg/acir_tests/flows/sol_honk.sh +++ b/barretenberg/acir_tests/flows/sol_honk.sh @@ -12,6 +12,8 @@ outdir=$(mktemp -d) trap "rm -rf $outdir" EXIT # Export the paths to the environment variables for the js test runner +export PUBLIC_INPUTS="$outdir/public_inputs" +export PUBLIC_INPUTS_AS_FIELDS="$outdir/public_inputs_fields.json" export PROOF="$outdir/proof" export PROOF_AS_FIELDS="$outdir/proof_fields.json" export VK="$outdir/vk" @@ -19,7 +21,7 @@ export VERIFIER_CONTRACT="$outdir/Verifier.sol" # Create a proof, write the solidity contract, write the proof as fields in order to extract the public inputs $BIN prove $PROVE_FLAGS -o $outdir -$BIN verify $VERIFY_FLAGS -k $VK -p $PROOF +$BIN verify $VERIFY_FLAGS -i $PUBLIC_INPUTS -k $VK -p $PROOF $BIN write_solidity_verifier $FLAGS -k $VK -o $VERIFIER_CONTRACT # Export the paths to the environment variables for the js test runner diff --git a/barretenberg/acir_tests/flows/sol_honk_zk.sh b/barretenberg/acir_tests/flows/sol_honk_zk.sh index 7475fe54d914..a7c25f7a4853 100755 --- a/barretenberg/acir_tests/flows/sol_honk_zk.sh +++ b/barretenberg/acir_tests/flows/sol_honk_zk.sh @@ -11,6 +11,8 @@ outdir=$(mktemp -d) trap "rm -rf $outdir" EXIT # Export the paths to the environment variables for the js test runner +export PUBLIC_INPUTS="$outdir/public_inputs" +export PUBLIC_INPUTS_AS_FIELDS="$outdir/public_inputs_fields.json" export PROOF="$outdir/proof" export PROOF_AS_FIELDS="$outdir/proof_fields.json" export VK="$outdir/vk" @@ -18,7 +20,7 @@ export VERIFIER_CONTRACT="$outdir/Verifier.sol" # Create a proof, write the solidity contract, write the proof as fields in order to extract the public inputs $BIN prove -o $outdir $FLAGS $BFLAG $PROTOCOL_FLAGS --output_format bytes_and_fields --write_vk -$BIN verify -k $VK -p $PROOF $FLAGS $PROTOCOL_FLAGS +$BIN verify -i $PUBLIC_INPUTS -k $VK -p $PROOF $FLAGS $PROTOCOL_FLAGS $BIN write_solidity_verifier $FLAGS -k $VK -o $VERIFIER_CONTRACT --zk # Export the paths to the environment variables for the js test runner diff --git a/barretenberg/acir_tests/sol-test/src/index.js b/barretenberg/acir_tests/sol-test/src/index.js index 2b11dcb3dac4..b966ab4b72ff 100644 --- a/barretenberg/acir_tests/sol-test/src/index.js +++ b/barretenberg/acir_tests/sol-test/src/index.js @@ -216,45 +216,54 @@ const killAnvil = () => { console.log(testName, " complete"); }; +// TODO(https://github.com/AztecProtocol/barretenberg/issues/1316): Clean this code up. We are trying to use this logic for three different flows: bb plonk, bb honk, and bbjs honk, and all three have different setups. try { const proofPath = getEnvVar("PROOF"); - let publicInputsPath; + + let proofStr = ""; + + const proof = readFileSync(proofPath); + proofStr = proof.toString("hex"); + + let publicInputsAsFieldsPath; // PUBLIC_INPUTS_AS_FIELDS is not defined for bb plonk, but is for bb honk and bbjs honk. try { - publicInputsPath = getEnvVar("PUBLIC_INPUTS"); + publicInputsAsFieldsPath = getEnvVar("PUBLIC_INPUTS_AS_FIELDS"); } catch (e) { // noop } - - let proofStr = ''; - let publicInputs = []; - - // If "path to public inputs" is provided, it means that the proof and public inputs are saved as separate files - // A bit hacky, but this can go away once BB CLI saves them as separate files - #11024 - if (publicInputsPath) { - const proof = readFileSync(proofPath); - proofStr = proof.toString("hex"); - publicInputs = JSON.parse(readFileSync(publicInputsPath).toString()); // assumes JSON array of PI hex strings - } else { - // Proof and public inputs are saved in a single file; we need to extract the PI from the proof - const proof = readFileSync(proofPath); - proofStr = proof.toString("hex"); - - const proofAsFieldsPath = getEnvVar("PROOF_AS_FIELDS"); + var publicInputs; + let proofAsFieldsPath; // PROOF_AS_FIELDS is not defined for bbjs, but is for bb plonk and bb honk. + try { + proofAsFieldsPath = getEnvVar("PROOF_AS_FIELDS"); + } catch (e) { + // noop + } + let numExtraPublicInputs = 0; + let extraPublicInputs = []; + if (proofAsFieldsPath) { const proofAsFields = readFileSync(proofAsFieldsPath); - - let numPublicInputs; - [numPublicInputs, publicInputs] = readPublicInputs( + // We need to extract the public inputs from the proof. This might be empty, or just the pairing point object, or be the entire public inputs... + [numExtraPublicInputs, extraPublicInputs] = readPublicInputs( JSON.parse(proofAsFields.toString()) ); - - proofStr = proofStr.substring(32 * 2 * numPublicInputs); // Remove the publicInput bytes from the proof - - // Honk proof from the CLI have field length as the first 4 bytes. This should go away in the future if (testingHonk) { + // Honk proof from the CLI have field length as the first 4 bytes. This should go away in the future proofStr = proofStr.substring(8); } } + // We need to do this because plonk doesn't define this path + if (publicInputsAsFieldsPath) { + const innerPublicInputs = JSON.parse( + readFileSync(publicInputsAsFieldsPath).toString() + ); // assumes JSON array of PI hex strings + + publicInputs = innerPublicInputs.concat(extraPublicInputs); + } else { + // for plonk, the extraPublicInputs are all of the public inputs + publicInputs = extraPublicInputs; + } + proofStr = proofStr.substring(64 * numExtraPublicInputs); proofStr = "0x" + proofStr; const key = diff --git a/barretenberg/cpp/src/barretenberg/api/api.hpp b/barretenberg/cpp/src/barretenberg/api/api.hpp index f2179fc2b5cc..a78a1c2d967b 100644 --- a/barretenberg/cpp/src/barretenberg/api/api.hpp +++ b/barretenberg/cpp/src/barretenberg/api/api.hpp @@ -59,6 +59,7 @@ class API { const std::filesystem::path& output_dir) = 0; virtual bool verify(const Flags& flags, + const std::filesystem::path& public_inputs_path, const std::filesystem::path& proof_path, const std::filesystem::path& vk_path) = 0; diff --git a/barretenberg/cpp/src/barretenberg/api/api_client_ivc.cpp b/barretenberg/cpp/src/barretenberg/api/api_client_ivc.cpp index 1907aa921d34..c784e533f9e8 100644 --- a/barretenberg/cpp/src/barretenberg/api/api_client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/api/api_client_ivc.cpp @@ -123,7 +123,9 @@ void write_standalone_vk(const std::string& output_data_type, auto proving_key = std::make_shared(builder, trace_settings); Prover prover{ proving_key }; init_bn254_crs(prover.proving_key->proving_key.circuit_size); - ProofAndKey to_write{ {}, std::make_shared(prover.proving_key->proving_key) }; + PubInputsProofAndKey to_write{ + PublicInputsVector{}, HonkProof{}, std::make_shared(prover.proving_key->proving_key) + }; write(to_write, output_data_type, "vk", output_path); } @@ -283,6 +285,7 @@ void ClientIVCAPI::prove(const Flags& flags, } bool ClientIVCAPI::verify([[maybe_unused]] const Flags& flags, + [[maybe_unused]] const std::filesystem::path& public_inputs_path, const std::filesystem::path& proof_path, const std::filesystem::path& vk_path) { diff --git a/barretenberg/cpp/src/barretenberg/api/api_client_ivc.hpp b/barretenberg/cpp/src/barretenberg/api/api_client_ivc.hpp index 9151e6afdee5..77673a48a9e9 100644 --- a/barretenberg/cpp/src/barretenberg/api/api_client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/api/api_client_ivc.hpp @@ -16,6 +16,7 @@ class ClientIVCAPI : public API { const std::filesystem::path& output_dir) override; bool verify(const Flags& flags, + const std::filesystem::path& public_inputs_path, const std::filesystem::path& proof_path, const std::filesystem::path& vk_path) override; diff --git a/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp b/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp index daa284af1855..702eb7229a7f 100644 --- a/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp +++ b/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp @@ -10,6 +10,8 @@ #include "barretenberg/dsl/acir_format/proof_surgeon.hpp" #include "barretenberg/dsl/acir_proofs/honk_contract.hpp" #include "barretenberg/dsl/acir_proofs/honk_zk_contract.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" +#include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp" #include "barretenberg/srs/global_crs.hpp" #include "barretenberg/ultra_vanilla_client_ivc/ultra_vanilla_client_ivc.hpp" @@ -57,33 +59,61 @@ UltraProver_ _compute_prover(const std::string& bytecode_path, } template -ProofAndKey _compute_vk(const bool init_kzg_accumulator, - const std::filesystem::path& bytecode_path, - const std::filesystem::path& witness_path) +PubInputsProofAndKey _compute_vk(const bool init_kzg_accumulator, + const std::filesystem::path& bytecode_path, + const std::filesystem::path& witness_path) { auto prover = _compute_prover(bytecode_path.string(), witness_path.string(), init_kzg_accumulator); - return { HonkProof{}, std::make_shared(prover.proving_key->proving_key) }; + return { PublicInputsVector{}, HonkProof{}, std::make_shared(prover.proving_key->proving_key) }; } template -ProofAndKey _prove(const bool compute_vk, - const bool init_kzg_accumulator, - const std::filesystem::path& bytecode_path, - const std::filesystem::path& witness_path) +PubInputsProofAndKey _prove(const bool compute_vk, + const bool init_kzg_accumulator, + const std::filesystem::path& bytecode_path, + const std::filesystem::path& witness_path) { auto prover = _compute_prover(bytecode_path.string(), witness_path.string(), init_kzg_accumulator); - return { prover.construct_proof(), compute_vk ? std::make_shared(prover.proving_key->proving_key) : nullptr }; + HonkProof concat_pi_and_proof = prover.construct_proof(); + size_t num_inner_public_inputs = prover.proving_key->proving_key.num_public_inputs; + if (init_kzg_accumulator) { + ASSERT(num_inner_public_inputs >= PAIRING_POINT_ACCUMULATOR_SIZE); + num_inner_public_inputs -= PAIRING_POINT_ACCUMULATOR_SIZE; + } + if constexpr (HasIPAAccumulator) { + ASSERT(num_inner_public_inputs >= IPA_CLAIM_SIZE); + num_inner_public_inputs -= IPA_CLAIM_SIZE; + } + // We split the inner public inputs, which are stored at the front of the proof, from the rest of the proof. Now, + // the "proof" refers to everything except the inner public inputs. + PublicInputsAndProof public_inputs_and_proof{ + PublicInputsVector(concat_pi_and_proof.begin(), + concat_pi_and_proof.begin() + static_cast(num_inner_public_inputs)), + HonkProof(concat_pi_and_proof.begin() + static_cast(num_inner_public_inputs), + concat_pi_and_proof.end()) + }; + return { public_inputs_and_proof.public_inputs, + public_inputs_and_proof.proof, + compute_vk ? std::make_shared(prover.proving_key->proving_key) : nullptr }; } template -bool _verify(const bool honk_recursion_2, const std::filesystem::path& proof_path, const std::filesystem::path& vk_path) +bool _verify(const bool honk_recursion_2, + const std::filesystem::path& public_inputs_path, + const std::filesystem::path& proof_path, + const std::filesystem::path& vk_path) { using VerificationKey = typename Flavor::VerificationKey; using Verifier = UltraVerifier_; auto g2_data = get_bn254_g2_data(CRS_PATH); srs::init_crs_factory({}, g2_data); + auto public_inputs = from_buffer>(read_file(public_inputs_path)); auto proof = from_buffer>(read_file(proof_path)); + // concatenate public inputs and proof + std::vector complete_proof = public_inputs; + complete_proof.insert(complete_proof.end(), proof.begin(), proof.end()); + auto vk = std::make_shared(from_buffer(read_file(vk_path))); vk->pcs_verification_key = std::make_shared>(); @@ -100,14 +130,15 @@ bool _verify(const bool honk_recursion_2, const std::filesystem::path& proof_pat const size_t HONK_PROOF_LENGTH = Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS - IPA_PROOF_LENGTH; const size_t num_public_inputs = static_cast(vk->num_public_inputs); // The extra calculation is for the IPA proof length. - ASSERT(proof.size() == HONK_PROOF_LENGTH + IPA_PROOF_LENGTH + num_public_inputs); + ASSERT(complete_proof.size() == HONK_PROOF_LENGTH + IPA_PROOF_LENGTH + num_public_inputs); const std::ptrdiff_t honk_proof_with_pub_inputs_length = static_cast(HONK_PROOF_LENGTH + num_public_inputs); - auto ipa_proof = HonkProof(proof.begin() + honk_proof_with_pub_inputs_length, proof.end()); - auto tube_honk_proof = HonkProof(proof.begin(), proof.begin() + honk_proof_with_pub_inputs_length); - verified = verifier.verify_proof(proof, ipa_proof); + auto ipa_proof = HonkProof(complete_proof.begin() + honk_proof_with_pub_inputs_length, complete_proof.end()); + auto tube_honk_proof = + HonkProof(complete_proof.begin(), complete_proof.begin() + honk_proof_with_pub_inputs_length); + verified = verifier.verify_proof(complete_proof, ipa_proof); } else { - verified = verifier.verify_proof(proof); + verified = verifier.verify_proof(complete_proof); } if (verified) { @@ -152,21 +183,22 @@ void UltraHonkAPI::prove(const Flags& flags, } bool UltraHonkAPI::verify(const Flags& flags, + const std::filesystem::path& public_inputs_path, const std::filesystem::path& proof_path, const std::filesystem::path& vk_path) { const bool ipa_accumulation = flags.ipa_accumulation; if (ipa_accumulation) { - return _verify(ipa_accumulation, proof_path, vk_path); + return _verify(ipa_accumulation, public_inputs_path, proof_path, vk_path); } if (flags.zk) { - return _verify(ipa_accumulation, proof_path, vk_path); + return _verify(ipa_accumulation, public_inputs_path, proof_path, vk_path); } if (flags.oracle_hash_type == "poseidon2") { - return _verify(ipa_accumulation, proof_path, vk_path); + return _verify(ipa_accumulation, public_inputs_path, proof_path, vk_path); } if (flags.oracle_hash_type == "keccak") { - return _verify(ipa_accumulation, proof_path, vk_path); + return _verify(ipa_accumulation, public_inputs_path, proof_path, vk_path); } return false; } diff --git a/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.hpp b/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.hpp index 7a8e6ca58d8f..02ba7a95bdf3 100644 --- a/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.hpp +++ b/barretenberg/cpp/src/barretenberg/api/api_ultra_honk.hpp @@ -20,6 +20,7 @@ class UltraHonkAPI : public API { const std::filesystem::path& output_dir) override; bool verify(const Flags& flags, + const std::filesystem::path& public_inputs_path, const std::filesystem::path& proof_path, const std::filesystem::path& vk_path) override; diff --git a/barretenberg/cpp/src/barretenberg/api/prove_tube.cpp b/barretenberg/cpp/src/barretenberg/api/prove_tube.cpp index 2812d66a1c19..1028e1cc40c1 100644 --- a/barretenberg/cpp/src/barretenberg/api/prove_tube.cpp +++ b/barretenberg/cpp/src/barretenberg/api/prove_tube.cpp @@ -2,6 +2,7 @@ #include "barretenberg/api/file_io.hpp" #include "barretenberg/api/init_srs.hpp" #include "barretenberg/common/map.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/stdlib/client_ivc_verifier/client_ivc_recursive_verifier.hpp" namespace bb { @@ -65,14 +66,27 @@ void prove_tube(const std::string& output_path, const std::string& vk_path) using Verifier = UltraVerifier_; Prover tube_prover{ *builder }; auto tube_proof = tube_prover.construct_proof(); + std::string tubePublicInputsPath = output_path + "/public_inputs"; std::string tubeProofPath = output_path + "/proof"; - write_file(tubeProofPath, to_buffer(tube_proof)); + PublicInputsAndProof public_inputs_and_proof{ + PublicInputsVector(tube_proof.begin(), + tube_proof.begin() + static_cast(num_inner_public_inputs)), + HonkProof(tube_proof.begin() + static_cast(num_inner_public_inputs), tube_proof.end()) + }; + write_file(tubePublicInputsPath, to_buffer(public_inputs_and_proof.public_inputs)); + write_file(tubeProofPath, to_buffer(public_inputs_and_proof.proof)); + std::string tubePublicInputsAsFieldsPath = output_path + "/public_inputs_fields.json"; std::string tubeProofAsFieldsPath = output_path + "/proof_fields.json"; const auto to_json = [](const std::vector& data) { + if (data.empty()) { + return std::string("[]"); + } return format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]"); }; - auto proof_data = to_json(tube_proof); + auto public_inputs_data = to_json(public_inputs_and_proof.public_inputs); + auto proof_data = to_json(public_inputs_and_proof.proof); + write_file(tubePublicInputsAsFieldsPath, { public_inputs_data.begin(), public_inputs_data.end() }); write_file(tubeProofAsFieldsPath, { proof_data.begin(), proof_data.end() }); std::string tubeVkPath = output_path + "/vk"; diff --git a/barretenberg/cpp/src/barretenberg/api/write_prover_output.hpp b/barretenberg/cpp/src/barretenberg/api/write_prover_output.hpp index 35510c55729f..6baff8e3b7a0 100644 --- a/barretenberg/cpp/src/barretenberg/api/write_prover_output.hpp +++ b/barretenberg/cpp/src/barretenberg/api/write_prover_output.hpp @@ -8,7 +8,8 @@ namespace bb { -template struct ProofAndKey { +template struct PubInputsProofAndKey { + PublicInputsVector public_inputs; HonkProof proof; std::shared_ptr key; }; @@ -19,16 +20,33 @@ void write(const ProverOutput& prover_output, const std::string& output_content, const std::filesystem::path& output_dir) { - enum class ObjectToWrite : size_t { PROOF, VK }; + enum class ObjectToWrite : size_t { PUBLIC_INPUTS, PROOF, VK }; const bool output_to_stdout = output_dir == "-"; const auto to_json = [](const std::vector& data) { + if (data.empty()) { + return std::string("[]"); + } return format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]"); }; const auto write_bytes = [&](const ObjectToWrite& obj) { switch (obj) { + case ObjectToWrite::PUBLIC_INPUTS: { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1312): Try to avoid include_size=true, which is + // used for deserialization. + const auto buf = to_buffer(prover_output.public_inputs); + if (output_to_stdout) { + write_bytes_to_stdout(buf); + } else { + write_file(output_dir / "public_inputs", buf); + info("Public inputs saved to ", output_dir / "public_inputs"); + } + break; + } case ObjectToWrite::PROOF: { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1312): Try to avoid include_size=true, which is + // used for deserialization. const auto buf = to_buffer(prover_output.proof); if (output_to_stdout) { write_bytes_to_stdout(buf); @@ -53,6 +71,17 @@ void write(const ProverOutput& prover_output, const auto write_fields = [&](const ObjectToWrite& obj) { switch (obj) { + case ObjectToWrite::PUBLIC_INPUTS: { + const std::string public_inputs_json = to_json(prover_output.public_inputs); + if (output_to_stdout) { + std::cout << public_inputs_json; + } else { + write_file(output_dir / "public_inputs_fields.json", + { public_inputs_json.begin(), public_inputs_json.end() }); + info("Public inputs fields saved to ", output_dir / "public_inputs_fields.json"); + } + break; + } case ObjectToWrite::PROOF: { const std::string proof_json = to_json(prover_output.proof); if (output_to_stdout) { @@ -78,10 +107,14 @@ void write(const ProverOutput& prover_output, if (output_content == "proof") { if (output_format == "bytes") { + write_bytes(ObjectToWrite::PUBLIC_INPUTS); write_bytes(ObjectToWrite::PROOF); } else if (output_format == "fields") { + write_fields(ObjectToWrite::PUBLIC_INPUTS); write_fields(ObjectToWrite::PROOF); } else if (output_format == "bytes_and_fields") { + write_bytes(ObjectToWrite::PUBLIC_INPUTS); + write_fields(ObjectToWrite::PUBLIC_INPUTS); write_bytes(ObjectToWrite::PROOF); write_fields(ObjectToWrite::PROOF); } else { @@ -100,12 +133,16 @@ void write(const ProverOutput& prover_output, } } else if (output_content == "proof_and_vk") { if (output_format == "bytes") { + write_bytes(ObjectToWrite::PUBLIC_INPUTS); write_bytes(ObjectToWrite::PROOF); write_bytes(ObjectToWrite::VK); } else if (output_format == "fields") { + write_fields(ObjectToWrite::PUBLIC_INPUTS); write_fields(ObjectToWrite::PROOF); write_fields(ObjectToWrite::VK); } else if (output_format == "bytes_and_fields") { + write_bytes(ObjectToWrite::PUBLIC_INPUTS); + write_fields(ObjectToWrite::PUBLIC_INPUTS); write_bytes(ObjectToWrite::PROOF); write_fields(ObjectToWrite::PROOF); write_bytes(ObjectToWrite::VK); diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index f007a15da5b4..9aef926c11c7 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -84,6 +84,7 @@ int main(int argc, char* argv[]) std::filesystem::path output_path{ "./out" }; // sometimes a directory where things will be written, sometimes the path of a file to be written + std::filesystem::path public_inputs_path{ "./target/public_inputs" }; std::filesystem::path proof_path{ "./target/proof" }; std::filesystem::path vk_path{ "./target/vk" }; flags.scheme = ""; @@ -201,6 +202,11 @@ int main(int argc, char* argv[]) /* ->check(CLI::ExistingFile) OR stdin indicator - */; }; + const auto add_public_inputs_path_option = [&](CLI::App* subcommand) { + return subcommand->add_option( + "--public_inputs_path, -i", public_inputs_path, "Path to public inputs.") /* ->check(CLI::ExistingFile) */; + }; + const auto add_proof_path_option = [&](CLI::App* subcommand) { return subcommand->add_option( "--proof_path, -p", proof_path, "Path to a proof.") /* ->check(CLI::ExistingFile) */; @@ -330,6 +336,7 @@ int main(int argc, char* argv[]) ***************************************************************************************************************/ CLI::App* verify = app.add_subcommand("verify", "Verify a proof."); + add_public_inputs_path_option(verify); add_proof_path_option(verify); add_vk_path_option(verify); @@ -695,7 +702,7 @@ int main(int argc, char* argv[]) return 0; } if (verify->parsed()) { - const bool verified = api.verify(flags, proof_path, vk_path); + const bool verified = api.verify(flags, public_inputs_path, proof_path, vk_path); vinfo("verified: ", verified); return verified ? 0 : 1; } @@ -755,10 +762,12 @@ int main(int argc, char* argv[]) else if (prove_tube_command->parsed()) { prove_tube(prove_tube_output_path, vk_path); } else if (verify_tube_command->parsed()) { + auto tube_public_inputs_path = tube_proof_and_vk_path + "/public_inputs"; auto tube_proof_path = tube_proof_and_vk_path + "/proof"; auto tube_vk_path = tube_proof_and_vk_path + "/vk"; UltraHonkAPI api; - return api.verify({ .ipa_accumulation = true }, tube_proof_path, tube_vk_path) ? 0 : 1; + return api.verify({ .ipa_accumulation = true }, tube_public_inputs_path, tube_proof_path, tube_vk_path) ? 0 + : 1; } // CLIENT IVC EXTRA COMMAND else if (OLD_API_gates_for_ivc->parsed()) { diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index 2e741a04452b..dc1c10774d32 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -370,10 +370,13 @@ template concept IsPlonkFlavor = IsAnyOf; template -concept IsUltraPlonkOrHonk = IsAnyOf; +concept IsUltraHonkFlavor = IsAnyOf; +template +concept IsUltraFlavor = IsUltraHonkFlavor || IsAnyOf; template -concept IsUltraFlavor = IsAnyOf; +concept IsUltraPlonkOrHonk = IsAnyOf || IsUltraFlavor; + template concept IsMegaFlavor = IsAnyOf; // this can be fr? +using PublicInputsVector = std::vector; +using HonkProof = std::vector; +struct PublicInputsAndProof { + PublicInputsVector public_inputs; + HonkProof proof; + + MSGPACK_FIELDS(public_inputs, proof); +}; struct ECCVMProof { HonkProof pre_ipa_proof; HonkProof ipa_proof; MSGPACK_FIELDS(pre_ipa_proof, ipa_proof); }; - +template using StdlibPublicInputsVector = std::vector>; template using StdlibProof = std::vector>; } // namespace bb diff --git a/barretenberg/sol/src/ultra/keys/Add2UltraVerificationKey.sol b/barretenberg/sol/src/ultra/keys/Add2UltraVerificationKey.sol index 89c44572fbbe..f6b5c071a320 100644 --- a/barretenberg/sol/src/ultra/keys/Add2UltraVerificationKey.sol +++ b/barretenberg/sol/src/ultra/keys/Add2UltraVerificationKey.sol @@ -1,11 +1,11 @@ -// Verification Key Hash: 594d0af354947ea7aa13eece55ec3ccdce54dff437d9354a8a50cb2a0438a86a +// Verification Key Hash: 52b5c39ccd4966230bccd3c5313992f8e8b0c4ca8120939c1a053b41d23ff61c // SPDX-License-Identifier: Apache-2.0 // Copyright 2022 Aztec pragma solidity >=0.8.4; library Add2UltraVerificationKey { function verificationKeyHash() internal pure returns (bytes32) { - return 0x594d0af354947ea7aa13eece55ec3ccdce54dff437d9354a8a50cb2a0438a86a; + return 0x52b5c39ccd4966230bccd3c5313992f8e8b0c4ca8120939c1a053b41d23ff61c; } function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure { diff --git a/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol b/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol index 6bf25e6238fb..ffd447c56fa0 100644 --- a/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol +++ b/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol @@ -1,11 +1,11 @@ -// Verification Key Hash: 6c68aa62aa452a4dbb84e1246b10262e6fb6ff860b59d906db6fb0807d11b872 +// Verification Key Hash: 3e90f6833288b5b3f8c6006ddff945b552ee9e6b00f3e047b1cb50927248e165 // SPDX-License-Identifier: Apache-2.0 // Copyright 2022 Aztec pragma solidity >=0.8.4; library BlakeUltraVerificationKey { function verificationKeyHash() internal pure returns (bytes32) { - return 0x6c68aa62aa452a4dbb84e1246b10262e6fb6ff860b59d906db6fb0807d11b872; + return 0x3e90f6833288b5b3f8c6006ddff945b552ee9e6b00f3e047b1cb50927248e165; } function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure { diff --git a/barretenberg/sol/src/ultra/keys/EcdsaUltraVerificationKey.sol b/barretenberg/sol/src/ultra/keys/EcdsaUltraVerificationKey.sol index 29b3cd73f1a9..eeffbdec7523 100644 --- a/barretenberg/sol/src/ultra/keys/EcdsaUltraVerificationKey.sol +++ b/barretenberg/sol/src/ultra/keys/EcdsaUltraVerificationKey.sol @@ -1,11 +1,11 @@ -// Verification Key Hash: 543ba4bb3b055949d65bec2da67f0c096105b2fbde342418408e48a04c96b193 +// Verification Key Hash: 051da9b5f32346e9d0892040b55aa70862447630aa29cecc3abe3cb74517137c // SPDX-License-Identifier: Apache-2.0 // Copyright 2022 Aztec pragma solidity >=0.8.4; library EcdsaUltraVerificationKey { function verificationKeyHash() internal pure returns (bytes32) { - return 0x543ba4bb3b055949d65bec2da67f0c096105b2fbde342418408e48a04c96b193; + return 0x051da9b5f32346e9d0892040b55aa70862447630aa29cecc3abe3cb74517137c; } function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure { diff --git a/yarn-project/bb-prover/src/bb/execute.ts b/yarn-project/bb-prover/src/bb/execute.ts index e675dbf01152..270c87fc361b 100644 --- a/yarn-project/bb-prover/src/bb/execute.ts +++ b/yarn-project/bb-prover/src/bb/execute.ts @@ -12,6 +12,8 @@ import { CLIENT_IVC_PROOF_FILE_NAME } from '../prover/client_ivc_proof_utils.js' export const VK_FILENAME = 'vk'; export const VK_FIELDS_FILENAME = 'vk_fields.json'; +export const PUBLIC_INPUTS_FILENAME = 'public_inputs'; +export const PUBLIC_INPUTS_FIELDS_FILENAME = 'public_inputs_fields.json'; export const PROOF_FILENAME = 'proof'; export const PROOF_FIELDS_FILENAME = 'proof_fields.json'; export const AVM_INPUTS_FILENAME = 'avm_inputs.bin'; @@ -666,8 +668,19 @@ async function verifyProofInternal( logger.verbose(`bb-prover (verify) BB out - ${message}`); }; + // take proofFullPath and remove the suffix past the / to get the directory + const proofDir = proofFullPath.substring(0, proofFullPath.lastIndexOf('/')); + const publicInputsFullPath = join(proofDir, '/public_inputs'); + + logger.debug(`public inputs path: ${publicInputsFullPath}`); try { - const args = ['-p', proofFullPath, '-k', verificationKeyPath, ...extraArgs]; + let args; + // Specify the public inputs path in the case of UH verification. + if (command == 'verify') { + args = ['-p', proofFullPath, '-k', verificationKeyPath, '-i', publicInputsFullPath, ...extraArgs]; + } else { + args = ['-p', proofFullPath, '-k', verificationKeyPath, ...extraArgs]; + } const loggingArg = logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : ''; if (loggingArg !== '') { diff --git a/yarn-project/bb-prover/src/prover/bb_prover.ts b/yarn-project/bb-prover/src/prover/bb_prover.ts index bc8af867d63d..235ef49a335e 100644 --- a/yarn-project/bb-prover/src/prover/bb_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_prover.ts @@ -80,6 +80,7 @@ import { BB_RESULT, PROOF_FIELDS_FILENAME, PROOF_FILENAME, + PUBLIC_INPUTS_FILENAME, VK_FILENAME, generateAvmProof, generateProof, @@ -386,7 +387,8 @@ export class BBNativeRollupProver implements ServerCircuitProver { const verificationKey = this.getVerificationKeyDataForCircuit('RootRollupArtifact'); await this.verifyProof('RootRollupArtifact', proof); - + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/13188): Remove this hack. + recursiveProof.binaryProof.numPublicInputs += AGGREGATION_OBJECT_LENGTH; return makePublicInputsAndRecursiveProof(circuitOutput, recursiveProof, verificationKey); } @@ -473,15 +475,13 @@ export class BBNativeRollupProver implements ServerCircuitProver { convertOutput, bbWorkingDirectory, ); - - // Read the binary proof - const rawProof = await fs.readFile(`${provingResult.proofPath!}/${PROOF_FILENAME}`); const vkData = this.getVerificationKeyDataForCircuit(circuitType); - const proof = new Proof(rawProof, vkData.numPublicInputs); + const proof = await this.readProofAsFields(provingResult.proofPath!, vkData, RECURSIVE_PROOF_LENGTH); + const circuitName = mapProtocolArtifactNameToCircuitName(circuitType); this.instrumentation.recordDuration('provingDuration', circuitName, provingResult.durationMs); - this.instrumentation.recordSize('proofSize', circuitName, proof.buffer.length); + this.instrumentation.recordSize('proofSize', circuitName, proof.binaryProof.buffer.length); this.instrumentation.recordSize('circuitPublicInputCount', circuitName, vkData.numPublicInputs); this.instrumentation.recordSize('circuitSize', circuitName, vkData.circuitSize); @@ -489,7 +489,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { circuitName, // does not include reading the proof from disk duration: provingResult.durationMs, - proofSize: proof.buffer.length, + proofSize: proof.binaryProof.buffer.length, eventName: 'circuit-proving', // circuitOutput is the partial witness that became the input to the proof inputSize: output.toBuffer().length, @@ -497,7 +497,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { numPublicInputs: vkData.numPublicInputs, } satisfies CircuitProvingStats); - return { circuitOutput: output, proof }; + return { circuitOutput: output, proof: proof.binaryProof }; }; return await this.runInDirectory(operation); } @@ -694,10 +694,11 @@ export class BBNativeRollupProver implements ServerCircuitProver { verificationFunction: (proofPath: string, vkPath: string) => Promise, ) { const operation = async (bbWorkingDirectory: string) => { + const publicInputsFileName = path.join(bbWorkingDirectory, PUBLIC_INPUTS_FILENAME); const proofFileName = path.join(bbWorkingDirectory, PROOF_FILENAME); const verificationKeyPath = path.join(bbWorkingDirectory, VK_FILENAME); - - await fs.writeFile(proofFileName, proof.buffer); + await fs.writeFile(publicInputsFileName, proof.buffer.slice(0, proof.numPublicInputs * 32 + 4)); + await fs.writeFile(proofFileName, proof.buffer.slice(proof.numPublicInputs * 32 + 4)); await fs.writeFile(verificationKeyPath, verificationKey.keyAsBytes); const result = await verificationFunction(proofFileName, verificationKeyPath!); @@ -731,10 +732,12 @@ export class BBNativeRollupProver implements ServerCircuitProver { vkData: VerificationKeyData, proofLength: PROOF_LENGTH, ): Promise> { + const publicInputsFilename = path.join(filePath, PUBLIC_INPUTS_FILENAME); const proofFilename = path.join(filePath, PROOF_FILENAME); const proofFieldsFilename = path.join(filePath, PROOF_FIELDS_FILENAME); - const [binaryProof, proofString] = await Promise.all([ + const [binaryPublicInputs, binaryProof, proofString] = await Promise.all([ + fs.readFile(publicInputsFilename), fs.readFile(proofFilename), fs.readFile(proofFieldsFilename, { encoding: 'utf-8' }), ]); @@ -746,14 +749,28 @@ export class BBNativeRollupProver implements ServerCircuitProver { numPublicInputs -= IPA_CLAIM_LENGTH; } - assert(json.length - numPublicInputs == proofLength, 'Proof length mismatch'); + assert(json.length == proofLength, 'Proof length mismatch'); - const fieldsWithoutPublicInputs = json.slice(numPublicInputs).map(Fr.fromHexString); + const fieldsWithoutPublicInputs = json.map(Fr.fromHexString); + + // Concat binary public inputs and binary proof + // This buffer will have the form: [4 bytes of metadata for public inputs, binary public inputs, 4 bytes of metadata for proof, binary proof] + const binaryProofWithPublicInputs = Buffer.concat([binaryPublicInputs, binaryProof]); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1312): Get rid of if possible. + const metadataLength = 4; + assert( + binaryProofWithPublicInputs.length == + metadataLength + numPublicInputs * 32 + metadataLength + NESTED_RECURSIVE_PROOF_LENGTH * 32, + ); logger.debug( - `Circuit path: ${filePath}, complete proof length: ${json.length}, num public inputs: ${numPublicInputs}, circuit size: ${vkData.circuitSize}, is recursive: ${vkData.isRecursive}, raw length: ${binaryProof.length}`, + `Circuit path: ${filePath}, complete proof length: ${json.length}, num public inputs: ${numPublicInputs}, circuit size: ${vkData.circuitSize}, is recursive: ${vkData.isRecursive}, raw length: ${binaryProofWithPublicInputs.length}`, + ); + return new RecursiveProof( + fieldsWithoutPublicInputs, + new Proof(binaryProofWithPublicInputs, numPublicInputs), + true, + proofLength, ); - - return new RecursiveProof(fieldsWithoutPublicInputs, new Proof(binaryProof, numPublicInputs), true, proofLength); } private async readAvmProofAsFields( diff --git a/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts b/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts index 8d7195cffcc4..e03f6c528e63 100644 --- a/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts @@ -1,5 +1,5 @@ import { BBNativeRollupProver, type BBProverConfig } from '@aztec/bb-prover'; -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; +import { AGGREGATION_OBJECT_LENGTH, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; import { makeTuple } from '@aztec/foundation/array'; import { timesParallel } from '@aztec/foundation/collection'; import { parseBooleanEnv } from '@aztec/foundation/config'; @@ -79,6 +79,8 @@ describe('prover/bb_prover/full-rollup', () => { const epochResult = await context.orchestrator.finaliseEpoch(); if (prover) { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/13188): Handle the pairing point object without these hacks. + epochResult.proof.numPublicInputs -= AGGREGATION_OBJECT_LENGTH; await expect(prover.verifyProof('RootRollupArtifact', epochResult.proof)).resolves.not.toThrow(); } diff --git a/yarn-project/prover-node/src/prover-node-publisher.ts b/yarn-project/prover-node/src/prover-node-publisher.ts index 3d81026f3107..f62051ca3132 100644 --- a/yarn-project/prover-node/src/prover-node-publisher.ts +++ b/yarn-project/prover-node/src/prover-node-publisher.ts @@ -97,7 +97,6 @@ export class ProverNodePublisher { const ctx = { epochNumber, fromBlock, toBlock }; if (!this.interrupted) { const timer = new Timer(); - // Validate epoch proof range and hashes are correct before submitting await this.validateEpochProofSubmission(args); diff --git a/yarn-project/stdlib/src/proofs/proof.ts b/yarn-project/stdlib/src/proofs/proof.ts index 0fd38ad19fb3..0a775448a8f4 100644 --- a/yarn-project/stdlib/src/proofs/proof.ts +++ b/yarn-project/stdlib/src/proofs/proof.ts @@ -3,6 +3,8 @@ import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { bufferToHex, hexToBuffer } from '@aztec/foundation/string'; +import { strict as assert } from 'assert'; + const EMPTY_PROOF_SIZE = 42; /** @@ -17,6 +19,7 @@ export class Proof { // Honk proofs start with a 4 byte length prefix // the proof metadata starts immediately after + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1312): Get rid of if possible. private readonly metadataOffset = 4; constructor( @@ -62,19 +65,50 @@ export class Proof { } public withoutPublicInputs(): Buffer { - return Buffer.concat([this.buffer.subarray(this.metadataOffset + Fr.SIZE_IN_BYTES * this.numPublicInputs)]); + if (this.isEmpty()) { + return this.buffer; + } + // We are indexing to this particular size because we are assuming the proof buffer looks like: + // [4 bytes of metadata for public inputs, binary public inputs, 4 bytes of metadata for proof, binary proof] + const proofStart = this.metadataOffset + Fr.SIZE_IN_BYTES * this.numPublicInputs + this.metadataOffset; + assert(this.buffer.length >= proofStart, 'Proof buffer is not appropriately sized to call withoutPublicInputs()'); + return this.buffer.subarray(proofStart); } + // This function assumes that the proof will contain an aggregation object and look something like: + // [4 bytes of metadata for public inputs, binary public inputs, 4 bytes of metadata for proof, aggregation object, rest of proof] + // We are extracting the binary public inputs and reading them as Frs, and also extracting the aggregation object. public extractPublicInputs(): Fr[] { + if (this.isEmpty()) { + // return array of this.numPublicInputs 0s + return new Array(this.numPublicInputs).fill(Fr.zero()); + } + assert(this.numPublicInputs >= AGGREGATION_OBJECT_LENGTH, 'Proof does not contain an aggregation object'); + const numInnerPublicInputs = this.numPublicInputs - AGGREGATION_OBJECT_LENGTH; const reader = BufferReader.asReader( - this.buffer.subarray(this.metadataOffset, this.metadataOffset + Fr.SIZE_IN_BYTES * this.numPublicInputs), + this.buffer.subarray(this.metadataOffset, this.metadataOffset + Fr.SIZE_IN_BYTES * numInnerPublicInputs), ); - return reader.readArray(this.numPublicInputs, Fr); + let publicInputs = reader.readArray(numInnerPublicInputs, Fr); + // concatenate Fr[] with aggregation object + publicInputs = publicInputs.concat(this.extractAggregationObject()); + return publicInputs; } public extractAggregationObject(): Fr[] { - const publicInputs = this.extractPublicInputs(); - return publicInputs.slice(-1 * AGGREGATION_OBJECT_LENGTH); + if (this.isEmpty()) { + // return array of 16 0s + return new Array(16).fill(Fr.zero()); + } + assert(this.numPublicInputs >= AGGREGATION_OBJECT_LENGTH, 'Proof does not contain an aggregation object'); + const numInnerPublicInputs = this.numPublicInputs - AGGREGATION_OBJECT_LENGTH; + // The aggregation object is currently stored after the initial inner public inputs and after the 4 byte proof metadata. + const reader = BufferReader.asReader( + this.buffer.subarray( + this.metadataOffset + Fr.SIZE_IN_BYTES * numInnerPublicInputs + this.metadataOffset, + this.metadataOffset + Fr.SIZE_IN_BYTES * this.numPublicInputs + this.metadataOffset, + ), + ); + return reader.readArray(AGGREGATION_OBJECT_LENGTH, Fr); } /**