Skip to content

Commit

Permalink
feat: initial integration avm prover (#4878)
Browse files Browse the repository at this point in the history
Basic shim to integrate avm simulator with the avm witgen/prover.

Currently uses the bb binary and supports proving(including passing in
calldata) and verifying (although both are currently WIP in the avm
prover).

Built on top of #4877
  • Loading branch information
IlyasRidhuan committed Mar 11, 2024
1 parent f6d17c9 commit 2e2554e
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 21 deletions.
88 changes: 72 additions & 16 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,70 @@ void acvm_info(const std::string& output_path)
}
}

/**
* @brief Writes an avm proof and corresponding (incomplete) verification key to files.
*
* Communication:
* - Filesystem: The proof and vk are written to the paths output_path/proof and output_path/vk
*
* @param bytecode_path Path to the file containing the serialised bytecode
* @param calldata_path Path to the file containing the serialised calldata (could be empty)
* @param crs_path Path to the file containing the CRS (ignition is suitable for now)
* @param output_path Path to write the output proof and verification key
*/
void avm_prove(const std::filesystem::path& bytecode_path,
const std::filesystem::path& calldata_path,
const std::string& crs_path,
const std::filesystem::path& output_path)
{
// Get Bytecode
std::vector<uint8_t> const avm_bytecode = read_file(bytecode_path);
std::vector<uint8_t> call_data_bytes{};
if (std::filesystem::exists(calldata_path)) {
call_data_bytes = read_file(calldata_path);
}
std::vector<fr> const call_data = many_from_buffer<fr>(call_data_bytes);

srs::init_crs_factory(crs_path);

// Prove execution and return vk
auto const [verification_key, proof] = avm_trace::Execution::prove(avm_bytecode, call_data);
// TODO(ilyas): <#4887>: Currently we only need these two parts of the vk, look into pcs_verification key reqs
std::vector<size_t> vk_vector = { verification_key.circuit_size, verification_key.num_public_inputs };

std::filesystem::path output_vk_path = output_path.parent_path() / "vk";
write_file(output_vk_path, to_buffer(vk_vector));
write_file(output_path, to_buffer(proof));
}

/**
* @brief Verifies an avm proof and writes the result to stdout
*
* Communication:
* - stdout: The boolean value indicating whether the proof is valid.
* - proc_exit: A boolean value is returned indicating whether the proof is valid.
*
* @param proof_path Path to the file containing the serialised proof (the vk should also be in the parent)
*/
bool avm_verify(const std::filesystem::path& proof_path)
{
std::filesystem::path vk_path = proof_path.parent_path() / "vk";

// Actual verification temporarily stopped (#4954)
// std::vector<fr> const proof = many_from_buffer<fr>(read_file(proof_path));
//
// std::vector<uint8_t> vk_bytes = read_file(vk_path);
// auto circuit_size = from_buffer<size_t>(vk_bytes, 0);
// auto _num_public_inputs = from_buffer<size_t>(vk_bytes, sizeof(size_t));
// auto vk = AvmFlavor::VerificationKey(circuit_size, num_public_inputs);
//
// std::cout << avm_trace::Execution::verify(vk, proof);
// return avm_trace::Execution::verify(vk, proof);

std::cout << 1;
return true;
}

bool flag_present(std::vector<std::string>& args, const std::string& flag)
{
return std::find(args.begin(), args.end(), flag) != args.end();
Expand Down Expand Up @@ -535,22 +599,14 @@ int main(int argc, char* argv[])
std::string output_path = get_option(args, "-o", vk_path + "_fields.json");
vk_as_fields(vk_path, output_path);
} else if (command == "avm_prove") {
std::string avm_bytecode_path = get_option(args, "-b", "./target/avm_bytecode.bin");
std::string output_path = get_option(args, "-o", "./proofs/avm_proof");
std::vector<uint8_t> call_data_bytes{};

if (flag_present(args, "-d")) {
auto const call_data_path = get_option(args, "-d", "./target/call_data.bin");
call_data_bytes = read_file(call_data_path);
}

srs::init_crs_factory("../srs_db/ignition");

std::vector<fr> const call_data = many_from_buffer<fr>(call_data_bytes);
auto const avm_bytecode = read_file(avm_bytecode_path);
auto const proof = avm_trace::Execution::run_and_prove(avm_bytecode, call_data);
std::vector<uint8_t> const proof_bytes = to_buffer(proof);
write_file(output_path, proof_bytes);
std::filesystem::path avm_bytecode_path = get_option(args, "-b", "./target/avm_bytecode.bin");
std::filesystem::path calldata_path = get_option(args, "-d", "./target/call_data.bin");
std::string crs_path = get_option(args, "-c", "../srs_db/ignition");
std::filesystem::path output_path = get_option(args, "-o", "./proofs/avm_proof");
avm_prove(avm_bytecode_path, calldata_path, crs_path, output_path);
} else if (command == "avm_verify") {
std::filesystem::path proof_path = get_option(args, "-p", "./proofs/avm_proof");
return avm_verify(proof_path) ? 0 : 1;
} else {
std::cerr << "Unknown command: " << command << "\n";
return 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,4 +369,4 @@ template <typename FF_> class avm_aluImpl {

template <typename FF> using avm_alu = Relation<avm_aluImpl<FF>>;

} // namespace bb::Avm_vm
} // namespace bb::Avm_vm
39 changes: 38 additions & 1 deletion barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#include "avm_execution.hpp"
#include "barretenberg/common/serialize.hpp"
#include "barretenberg/flavor/generated/avm_flavor.hpp"
#include "barretenberg/proof_system/circuit_builder/generated/avm_circuit_builder.hpp"
#include "barretenberg/vm/avm_trace/avm_common.hpp"
#include "barretenberg/vm/avm_trace/avm_deserialization.hpp"
#include "barretenberg/vm/avm_trace/avm_instructions.hpp"
#include "barretenberg/vm/avm_trace/avm_opcode.hpp"
#include "barretenberg/vm/avm_trace/avm_trace.hpp"
#include "barretenberg/vm/generated/avm_composer.hpp"
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <string>
#include <tuple>
#include <variant>
#include <vector>

Expand All @@ -35,7 +38,41 @@ HonkProof Execution::run_and_prove(std::vector<uint8_t> const& bytecode, std::ve

auto composer = AvmComposer();
auto prover = composer.create_prover(circuit_builder);
return prover.construct_proof();
auto verifier = composer.create_verifier(circuit_builder);
auto proof = prover.construct_proof();
return proof;
}

std::tuple<AvmFlavor::VerificationKey, HonkProof> Execution::prove(std::vector<uint8_t> const& bytecode,
std::vector<FF> const& calldata)
{
auto instructions = Deserialization::parse(bytecode);
auto trace = gen_trace(instructions, calldata);
auto circuit_builder = bb::AvmCircuitBuilder();
circuit_builder.set_trace(std::move(trace));

// Temporarily use this until #4954 is resolved
assert(circuit_builder.check_circuit());

auto composer = AvmComposer();
auto prover = composer.create_prover(circuit_builder);
auto verifier = composer.create_verifier(circuit_builder);
auto proof = prover.construct_proof();
// TODO(#4887): Might need to return PCS vk when full verify is supported
return std::make_tuple(*verifier.key, proof);
}

bool Execution::verify(AvmFlavor::VerificationKey vk, HonkProof const& proof)
{
auto verification_key = std::make_shared<AvmFlavor::VerificationKey>(vk);
AvmVerifier verifier(verification_key);

// todo: not needed for now until we verify the PCS/pairing of the proof
// auto pcs_verification_key = std::make_unique<VerifierCommitmentKey>(verification_key->circuit_size,
// crs_factory_);
// output_state.pcs_verification_key = std::move(pcs_verification_key);

return verifier.verify_proof(proof);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class Execution {
static std::vector<Row> gen_trace(std::vector<Instruction> const& instructions,
std::vector<FF> const& calldata = {});
static bb::HonkProof run_and_prove(std::vector<uint8_t> const& bytecode, std::vector<FF> const& calldata = {});

static std::tuple<AvmFlavor::VerificationKey, bb::HonkProof> prove(std::vector<uint8_t> const& bytecode,
std::vector<FF> const& calldata = {});
static bool verify(AvmFlavor::VerificationKey vk, HonkProof const& proof);
};

} // namespace bb::avm_trace
} // namespace bb::avm_trace
2 changes: 2 additions & 0 deletions build_manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ yarn-project-test:
- noir-packages
- l1-contracts
- noir-projects
- barretenberg-x86_64-linux-clang

# Builds all of yarn-project, with all developer dependencies.
# Creates a runnable container used to run tests and formatting checks.
Expand All @@ -170,6 +171,7 @@ yarn-project:
- l1-contracts
- noir-projects
- noir
- barretenberg-x86_64-linux-clang
multiarch: host

# A runnable container, sets entrypoint to be the aztec infrastructure entrypoint.
Expand Down
1 change: 1 addition & 0 deletions yarn-project/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ node_modules
.tsbuildinfo
tsconfig.tsbuildinfo
.eslintcache
simulator/target

.yarn/*
!.yarn/patches
Expand Down
8 changes: 7 additions & 1 deletion yarn-project/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ FROM --platform=linux/amd64 aztecprotocol/bb.js as bb.js
FROM --platform=linux/amd64 aztecprotocol/noir-packages as noir-packages
FROM --platform=linux/amd64 aztecprotocol/l1-contracts as contracts
FROM --platform=linux/amd64 aztecprotocol/noir-projects as noir-projects
FROM aztecprotocol/noir as noir
FROM --platform=linux/amd64 aztecprotocol/noir as noir
FROM --platform=linux/amd64 aztecprotocol/barretenberg-x86_64-linux-clang as barretenberg

FROM node:18.19.0 as builder
RUN apt update && apt install -y jq curl perl && rm -rf /var/lib/apt/lists/* && apt-get clean
Expand All @@ -14,6 +15,11 @@ COPY --from=contracts /usr/src/l1-contracts /usr/src/l1-contracts
COPY --from=noir-projects /usr/src/noir-projects /usr/src/noir-projects
# We want the native ACVM binary
COPY --from=noir /usr/src/noir/noir-repo/target/release/acvm /usr/src/noir/noir-repo/target/release/acvm
COPY --from=barretenberg /usr/src/barretenberg/cpp/build/bin/bb /usr/src/barretenberg/cpp/build/bin/bb

COPY --from=barretenberg /usr/src/barretenberg/cpp/srs_db/ /usr/src/barretenberg/cpp/srs_db/
WORKDIR /usr/src/barretenberg/cpp/srs_db
RUN ./download_ignition.sh 0

WORKDIR /usr/src/yarn-project
COPY . .
Expand Down
8 changes: 7 additions & 1 deletion yarn-project/Dockerfile.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ FROM --platform=linux/amd64 aztecprotocol/bb.js as bb.js
FROM --platform=linux/amd64 aztecprotocol/noir-packages as noir-packages
FROM --platform=linux/amd64 aztecprotocol/l1-contracts as contracts
FROM --platform=linux/amd64 aztecprotocol/noir-projects as noir-projects
FROM --platform=linux/amd64 aztecprotocol/barretenberg-x86_64-linux-clang as barretenberg

FROM node:18.19.0 as builder
RUN apt update && apt install -y jq curl perl && rm -rf /var/lib/apt/lists/* && apt-get clean
Expand All @@ -11,6 +12,11 @@ COPY --from=bb.js /usr/src/barretenberg/ts /usr/src/barretenberg/ts
COPY --from=noir-packages /usr/src/noir/packages /usr/src/noir/packages
COPY --from=contracts /usr/src/l1-contracts /usr/src/l1-contracts
COPY --from=noir-projects /usr/src/noir-projects /usr/src/noir-projects
COPY --from=barretenberg /usr/src/barretenberg/cpp/build/bin/bb /usr/src/barretenberg/cpp/build/bin/bb

COPY --from=barretenberg /usr/src/barretenberg/cpp/srs_db/ /usr/src/barretenberg/cpp/srs_db/
WORKDIR /usr/src/barretenberg/cpp/srs_db
RUN ./download_ignition.sh 0

WORKDIR /usr/src/yarn-project
COPY . .
Expand All @@ -31,4 +37,4 @@ RUN yarn prepare:check && yarn formatting && yarn test

# Avoid pushing some huge container back to ecr.
FROM scratch
COPY --from=builder /usr/src/yarn-project/README.md /usr/src/yarn-project/README.md
COPY --from=builder /usr/src/yarn-project/README.md /usr/src/yarn-project/README.md
72 changes: 72 additions & 0 deletions yarn-project/simulator/src/public/avm_executor.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { AztecAddress, CallContext, EthAddress, FunctionData, FunctionSelector, Header } from '@aztec/circuits.js';
import { makeHeader } from '@aztec/circuits.js/testing';
import { Fr } from '@aztec/foundation/fields';
import { AvmTestContractArtifact } from '@aztec/noir-contracts.js';

import { MockProxy, mock } from 'jest-mock-extended';

import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js';
import { PublicExecution } from './execution.js';
import { PublicExecutor } from './executor.js';

describe('AVM WitGen and Proof Generation', () => {
let publicState: MockProxy<PublicStateDB>;
let publicContracts: MockProxy<PublicContractsDB>;
let commitmentsDb: MockProxy<CommitmentsDB>;
let header: Header;

const callContext = CallContext.from({
msgSender: AztecAddress.random(),
storageContractAddress: AztecAddress.random(),
portalContractAddress: EthAddress.random(),
functionSelector: FunctionSelector.empty(),
isContractDeployment: false,
isDelegateCall: false,
isStaticCall: false,
startSideEffectCounter: 0,
});
const contractAddress = AztecAddress.random();

beforeEach(() => {
publicState = mock<PublicStateDB>();
publicContracts = mock<PublicContractsDB>();
commitmentsDb = mock<CommitmentsDB>();

const randomInt = Math.floor(Math.random() * 1000000);
header = makeHeader(randomInt);
}, 10000);

it('Should prove valid execution of bytecode that performs addition', async () => {
const args: Fr[] = [new Fr(1), new Fr(2)];
// Bytecode for the following contract is encoded:
// const bytecode = encodeToBytecode([
// new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 2, /*dstOffset=*/ 0),
// new Add(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2),
// new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1),
// ]);
const bytecode: Buffer = Buffer.from('HwAAAAAAAAAAAgAAAAAAAAYAAAAAAAAAAQAAAAI3AAAAAAIAAAAB', 'base64');
publicContracts.getBytecode.mockResolvedValue(bytecode);
const executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header);
const functionData = FunctionData.empty();
const execution: PublicExecution = { contractAddress, functionData, args, callContext };
const [proof, vk] = await executor.getAvmProof(execution);
const valid = await executor.verifyAvmProof(vk, proof);
expect(valid).toBe(true);
});

// This is skipped as we require MOV to be implemented in the AVM
it.skip('Should prove valid execution contract function that performs addition', async () => {
const args: Fr[] = [new Fr(1), new Fr(2)];

const addArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_addArgsReturn')!;
const bytecode = Buffer.from(addArtifact.bytecode, 'base64');
publicContracts.getBytecode.mockResolvedValue(bytecode);
const functionData = FunctionData.fromAbi(addArtifact);
const execution: PublicExecution = { contractAddress, functionData, args, callContext };

const executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header);
const [proof, vk] = await executor.getAvmProof(execution);
const valid = await executor.verifyAvmProof(vk, proof);
expect(valid).toBe(true);
});
});
Loading

0 comments on commit 2e2554e

Please sign in to comment.