Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions compiler/integration-tests/circuits/recursion/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
global HONK_PROOF_SIZE: u32 = 456;
global HONK_IDENTIFIER: u32 = 1;

fn main(
verification_key: [Field; 114],
proof: [Field; 93],
public_inputs: [Field; 1],
key_hash: Field
verification_key: [Field; 128],
proof: [Field; HONK_PROOF_SIZE],
public_inputs: [Field; 2]
) {
std::verify_proof(verification_key, proof, public_inputs, key_hash)
std::verify_proof_with_type(
verification_key,
proof,
public_inputs,
0x0,
HONK_IDENTIFIER,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ $NARGO_BACKEND_PATH write_solidity_verifier -k $KEYS/vk -o $contracts_dir/assert
# Codegen verifier contract for recursion
recursion_dir=$repo_root/compiler/integration-tests/circuits/recursion
nargo --program-dir $recursion_dir compile
$NARGO_BACKEND_PATH OLD_API write_vk -b $recursion_dir/target/recursion.json -o $KEYS/recursion
$NARGO_BACKEND_PATH OLD_API contract -k $KEYS/recursion -o $contracts_dir/recursion.sol
$NARGO_BACKEND_PATH write_vk --scheme ultra_honk --oracle_hash keccak -b $recursion_dir/target/recursion.json -o $KEYS
$NARGO_BACKEND_PATH write_solidity_verifier --scheme ultra_honk -k $KEYS/vk -o $contracts_dir/recursion.sol

rm -rf $KEYS
2 changes: 1 addition & 1 deletion compiler/integration-tests/scripts/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ self_path=$(dirname "$(readlink -f "$0")")


$self_path/compile-programs.sh
$self_path/codegen-verifiers.sh
$self_path/generate-solidity-verifiers.sh
61 changes: 19 additions & 42 deletions compiler/integration-tests/test/browser/recursion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { TEST_LOG_LEVEL } from '../environment.js';
import { Logger } from 'tslog';
import { acvm, abi, Noir } from '@noir-lang/noir_js';

import * as TOML from 'smol-toml';
import { UltraPlonkBackend } from '@aztec/bb.js';
import { Barretenberg, RawBuffer, UltraHonkBackend } from '@aztec/bb.js';
import { getFile } from './utils.js';
import { Field, InputMap } from '@noir-lang/noirc_abi';
import { InputMap } from '@noir-lang/noirc_abi';
import { createFileManager, compile } from '@noir-lang/noir_wasm';

const logger = new Logger({ name: 'test', minLevel: TEST_LOG_LEVEL });
Expand All @@ -18,7 +17,7 @@ await newABICoder();
await initACVM();

const base_relative_path = '../../../../..';
const circuit_main = 'test_programs/execution_success/assert_statement';
const circuit_main = 'compiler/integration-tests/circuits/assert_lt';
const circuit_recursion = 'compiler/integration-tests/circuits/recursion';

async function getCircuit(projectPath: string) {
Expand All @@ -33,62 +32,40 @@ async function getCircuit(projectPath: string) {
}

describe('It compiles noir program code, receiving circuit bytes and abi object.', () => {
let circuit_main_toml;

before(async () => {
circuit_main_toml = await new Response(await getFile(`${base_relative_path}/${circuit_main}/Prover.toml`)).text();
});
it('Should generate valid inner proof for correct input, then verify proof within a proof', async () => {
const main_inputs: InputMap = {
x: '2',
y: '3',
};

// TODO(https://github.com/noir-lang/noir/issues/5106): Reinstate this test.
it.skip('Should generate valid inner proof for correct input, then verify proof within a proof', async () => {
const main_program = await getCircuit(`${base_relative_path}/${circuit_main}`);
const main_inputs: InputMap = TOML.parse(circuit_main_toml) as InputMap;

const main_backend = new UltraPlonkBackend(main_program.bytecode, {}, { recursive: true });
const main_backend = new UltraHonkBackend(main_program.bytecode, {}, { recursive: true });

const { witness: main_witnessUint8Array } = await new Noir(main_program).execute(main_inputs);

const main_proof = await main_backend.generateProof(main_witnessUint8Array);
const main_verification = await main_backend.verifyProof(main_proof);

logger.debug('main_verification', main_verification);
const { proof: intermediateProof, publicInputs: intermediatePublicInputs } =
await main_backend.generateProofForRecursiveAggregation(main_witnessUint8Array);

expect(main_verification).to.be.true;

const numPublicInputs = 1;
const { proofAsFields, vkAsFields, vkHash } = await main_backend.generateRecursiveProofArtifacts(
main_proof,
numPublicInputs,
);
// Get verification key for inner circuit as fields
const innerCircuitVerificationKey = await main_backend.getVerificationKey();
const barretenbergAPI = await Barretenberg.new({ threads: 1 });
const vkAsFields = await barretenbergAPI.acirVkAsFieldsUltraHonk(new RawBuffer(innerCircuitVerificationKey));

const recursion_inputs: InputMap = {
verification_key: vkAsFields,
proof: proofAsFields,
public_inputs: [main_inputs.y as Field],
key_hash: vkHash,
verification_key: vkAsFields.map((field) => field.toString()),
proof: intermediateProof,
public_inputs: intermediatePublicInputs,
};

logger.debug('recursion_inputs', recursion_inputs);

const recursion_program = await getCircuit(`${base_relative_path}/${circuit_recursion}`);

const recursion_backend = new UltraPlonkBackend(recursion_program.bytecode, {}, { recursive: false });
const recursion_backend = new UltraHonkBackend(recursion_program.bytecode, {}, { recursive: false });

const { witness: recursion_witnessUint8Array } = await new Noir(recursion_program).execute(recursion_inputs);

const recursion_proof = await recursion_backend.generateProof(recursion_witnessUint8Array);

// Causes an "unreachable" error.
// Due to the fact that it's a non-recursive proof?
//
// const recursion_numPublicInputs = 1;
// const { proofAsFields: recursion_proofAsFields } = await recursion_backend.generateRecursiveProofArtifacts(
// recursion_proof,
// recursion_numPublicInputs,
// );
//
// logger.debug('recursion_proofAsFields', recursion_proofAsFields);

const recursion_verification = await recursion_backend.verifyProof(recursion_proof);

logger.debug('recursion_verification', recursion_verification);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,81 +1,56 @@
import { expect } from 'chai';
import { ethers } from 'hardhat';
import { CompiledCircuit, Noir } from '@noir-lang/noir_js';
import { Barretenberg, RawBuffer, UltraHonkBackend } from '@aztec/bb.js';

import assertLtCircuit from '../../circuits/assert_lt/target/assert_lt.json' assert { type: 'json' };
import recursionCircuit from '../../circuits/recursion/target/recursion.json' assert { type: 'json' };

it(`smart contract can verify a recursive proof`, async () => {
// Inner circuit
const innerBackend = new UltraHonkBackend(assertLtCircuit.bytecode, {}, { recursive: true });
const inner = new Noir(assertLtCircuit as CompiledCircuit);
const innerInputs = {
x: '2',
y: '3',
};

import { readFileSync } from 'node:fs';
import { resolve, join } from 'path';
import toml from 'toml';
// Generate intermediate proof
const { witness: main_witness } = await inner.execute(innerInputs);
const { proof: intermediateProof, publicInputs: intermediatePublicInputs } =
await innerBackend.generateProofForRecursiveAggregation(main_witness);

import { Noir } from '@noir-lang/noir_js';
import { UltraPlonkBackend } from '@aztec/bb.js';
import { Field, InputMap } from '@noir-lang/noirc_abi';
// Get verification key for inner circuit as fields
const innerCircuitVerificationKey = await innerBackend.getVerificationKey();
const barretenbergAPI = await Barretenberg.new({ threads: 1 });
const vkAsFields = await barretenbergAPI.acirVkAsFieldsUltraHonk(new RawBuffer(innerCircuitVerificationKey));

import { compile, createFileManager } from '@noir-lang/noir_wasm';
// Generate proof of the recursive circuit
const recursiveCircuitNoir = new Noir(recursionCircuit as CompiledCircuit);
const recursiveBackend = new UltraHonkBackend(recursionCircuit.bytecode, { threads: 1 });

// TODO(https://github.com/AztecProtocol/aztec-packages/issues/6672): Reinstate this test.
it.skip(`smart contract can verify a recursive proof`, async () => {
const basePath = resolve(join(__dirname, '../../../../'));
const fm = createFileManager(basePath);
const innerCompilationResult = await compile(
fm,
join(basePath, './test_programs/execution_success/assert_statement'),
const recursiveInputs = {
proof: intermediateProof,
public_inputs: intermediatePublicInputs,
verification_key: vkAsFields.map((field) => field.toString()),
};
const { witness: recursiveWitness } = await recursiveCircuitNoir.execute(recursiveInputs);
const { proof: recursiveProof, publicInputs: recursivePublicInputs } = await recursiveBackend.generateProof(
recursiveWitness,
{ keccak: true },
);
if (!('program' in innerCompilationResult)) {
throw new Error('Compilation failed');
}
const innerProgram = innerCompilationResult.program;

const recursionCompilationResult = await compile(
fm,
join(basePath, './compiler/integration-tests/circuits/recursion'),
// Verify recursive proof
const verified = await recursiveBackend.verifyProof(
{ proof: recursiveProof, publicInputs: recursivePublicInputs },
{ keccak: true },
);
if (!('program' in recursionCompilationResult)) {
throw new Error('Compilation failed');
}
const recursionProgram = recursionCompilationResult.program;

// Intermediate proof

const inner_backend = new UltraPlonkBackend(innerProgram.bytecode, {}, { recursive: true });
const inner = new Noir(innerProgram);

const inner_prover_toml = readFileSync(
join(basePath, `./test_programs/execution_success/assert_statement/Prover.toml`),
).toString();

const inner_inputs = toml.parse(inner_prover_toml);

const { witness: main_witness } = await inner.execute(inner_inputs);
const intermediate_proof = await inner_backend.generateProof(main_witness);

expect(await inner_backend.verifyProof(intermediate_proof)).to.be.true;

const { proofAsFields, vkAsFields, vkHash } = await inner_backend.generateRecursiveProofArtifacts(
intermediate_proof,
1, // 1 public input
);

// Final proof

const recursion = new Noir(recursionProgram);

const recursion_inputs: InputMap = {
verification_key: vkAsFields,
proof: proofAsFields,
public_inputs: [inner_inputs.y as Field],
key_hash: vkHash,
};

const { witness: recursionWitness } = await recursion.execute(recursion_inputs);

const recursion_backend = new UltraPlonkBackend(recursionProgram.bytecode, {}, { recursive: false });
const recursion_proof = await recursion_backend.generateProof(recursionWitness);
expect(await recursion_backend.verifyProof(recursion_proof)).to.be.true;
expect(verified).to.be.true;

// Smart contract verification

const contract = await ethers.deployContract('contracts/recursion.sol:UltraVerifier', []);

const result = await contract.verify.staticCall(recursion_proof.proof, recursion_proof.publicInputs);
const contract = await ethers.deployContract('contracts/recursion.sol:HonkVerifier', []);
const result = await contract.verify.staticCall(recursiveProof, recursivePublicInputs);

expect(result).to.be.true;
});
Loading