Skip to content

Commit

Permalink
chore: Refactor Cli interface to be more unix-like (#1833)
Browse files Browse the repository at this point in the history
resolves #1828 

# Checklist:
Remove the checklist to signal you've completed it. Enable auto-merge if
the PR is ready to merge.
- [ ] If the pull request requires a cryptography review (e.g.
cryptographic algorithm implementations) I have added the 'crypto' tag.
- [ ] I have reviewed my diff in github, line by line and removed
unexpected formatting changes, testing logs, or commented-out code.
- [ ] Every change is related to the PR description.
- [ ] I have
[linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)
this pull request to relevant issues (if any exist).
  • Loading branch information
kevaundray authored Aug 28, 2023
1 parent 88940be commit 28d722e
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once
#include "exec_pipe.hpp"
#include "file_io.hpp"
#include "vinfo.hpp"
#include "log.hpp"
#include <barretenberg/ecc/curves/bn254/g1.hpp>
#include <barretenberg/srs/io.hpp>
#include <filesystem>
Expand Down
66 changes: 66 additions & 0 deletions circuits/cpp/barretenberg/cpp/src/barretenberg/bb/log.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#pragma once
#include <barretenberg/common/log.hpp>
#include <iostream>

extern bool verbose;

template <typename... Args> inline void vinfo(Args... args)
{
if (verbose) {
info(args...);
}
}

/**
* @brief Writes raw bytes of the vector to stdout
*
* Note: std::cout << byte is not being used here because that always prints the numerical value.
* << can also apply formatting and seems is not the appropriate way to write raw bytes to stdout.
*
* Example:
*
* uint8_t byte = 'A'
* std::cout << byte; // prints 65
* std::cout.put(byte); // prints 'A'
*
* @param data The raw bytes that we want to write to stdout
*/
inline void writeRawBytesToStdout(const std::vector<uint8_t>& data)
{
for (auto byte : data) {
// Safety: a byte and a char occupy one byte
std::cout.put(static_cast<char>(byte));
}
}

/**
* @brief Writes a uint64_t to stdout in little endian format
*
* @param value The value to be written to stdout
*/
inline void writeUint64AsRawBytesToStdout(uint64_t value)
{
// Convert the uint64_t to a vector of bytes, since std::cout.put
// only accepts a single byte.
std::vector<uint8_t> bytes;
bytes.reserve(sizeof(uint64_t));

for (size_t i = 0; i < sizeof(uint64_t); ++i) {
bytes.push_back(static_cast<uint8_t>(value & 0xFF));
value >>= 8;
}

writeRawBytesToStdout(bytes);
}

/**
* @brief Writes a sting to stdout
*
* @param str The raw string to write to stdout
*/
inline void writeStringToStdout(const std::string& str)
{
for (char ch : str) {
std::cout.put(ch);
}
}
83 changes: 53 additions & 30 deletions circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "barretenberg/bb/get_crs.hpp"
#include "get_bytecode.hpp"
#include "get_crs.hpp"
#include "get_witness.hpp"
#include "log.hpp"
#include <barretenberg/common/container.hpp>
#include <barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp>
#include <barretenberg/dsl/acir_proofs/acir_composer.hpp>
Expand All @@ -11,9 +12,9 @@
#include <vector>

using namespace barretenberg;
// The maximum size that we can do in the browser is 2^19
// The maximum size that we can do in the browser and node is 2^19
// based on memory constraints for UltraPlonk.
// However, since this will be ran natively, we can increase the
// However, since this CLI does not use WASM, we can increase the
// size.
uint32_t MAX_CIRCUIT_SIZE = 1 << 23;
std::string CRS_PATH = "./crs";
Expand Down Expand Up @@ -43,7 +44,8 @@ acir_format::acir_format get_constraint_system(std::string const& bytecode_path)
* @brief Proves and Verifies an ACIR circuit
*
* Communication:
* - stdout: A boolean value is printed to stdout indicating whether the proof is valid
* - proc_exit: A boolean value is returned indicating whether the proof is valid.
* an exit code of 0 will be returned for success and 1 for failure.
*
* @param bytecodePath Path to the file containing the serialized circuit
* @param witnessPath Path to the file containing the serialized witness
Expand All @@ -59,9 +61,10 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP
auto proof = acir_composer->create_proof(srs::get_crs_factory(), constraint_system, witness, recursive);
auto verified = acir_composer->verify_proof(proof, recursive);

std::cout << verified << std::endl;
vinfo("verified: ", verified);
return verified;
}

/**
* @brief Creates a proof for an ACIR circuit
*
Expand All @@ -84,11 +87,15 @@ void prove(const std::string& bytecodePath,
auto witness = get_witness(witnessPath);
auto proof = acir_composer->create_proof(srs::get_crs_factory(), constraint_system, witness, recursive);

std::cout << proof << std::endl;
write_file(outputPath, proof);

info("proof written to: ", outputPath);
if (outputPath == "-") {
writeRawBytesToStdout(proof);
vinfo("proof written to stdout");
} else {
write_file(outputPath, proof);
vinfo("proof written to: ", outputPath);
}
}

/**
* @brief Computes the number of Barretenberg specific gates needed to create a proof for the specific ACIR circuit
*
Expand All @@ -103,16 +110,20 @@ void gateCount(const std::string& bytecodePath)
auto constraint_system = get_constraint_system(bytecodePath);
acir_composer->create_circuit(constraint_system);
auto gate_count = acir_composer->get_total_circuit_size();
std::cout << gate_count << std::endl;

writeUint64AsRawBytesToStdout(static_cast<uint64_t>(gate_count));
vinfo("gate count: ", gate_count);
}

/**
* @brief Verifies a proof for an ACIR circuit
*
* Note: The fact that the proof was computed originally by parsing an ACIR circuit is not of importance
* because this method uses the verification key to verify the proof.
*
* Communication:
* - stdout: A boolean value is printed to stdout indicating whether the proof is valid
* - proc_exit: A boolean value is returned indicating whether the proof is valid.
* an exit code of 0 will be returned for success and 1 for failure.
*
* @param proof_path Path to the file containing the serialized proof
* @param recursive Whether to use recursive proof generation of non-recursive
Expand All @@ -127,7 +138,8 @@ bool verify(const std::string& proof_path, bool recursive, const std::string& vk
acir_composer->load_verification_key(barretenberg::srs::get_crs_factory(), std::move(vk_data));
auto verified = acir_composer->verify_proof(read_file(proof_path), recursive);

std::cout << verified << std::endl;
vinfo("verified: ", verified);

return verified;
}

Expand All @@ -148,11 +160,13 @@ void writeVk(const std::string& bytecodePath, const std::string& outputPath)
acir_composer->init_proving_key(srs::get_crs_factory(), constraint_system);
auto vk = acir_composer->init_verification_key();
auto serialized_vk = to_buffer(*vk);

std::cout << serialized_vk << std::endl;
write_file(outputPath, serialized_vk);

info("vk written to: ", outputPath);
if (outputPath == "-") {
writeRawBytesToStdout(serialized_vk);
vinfo("vk written to stdout");
} else {
write_file(outputPath, serialized_vk);
vinfo("vk written to: ", outputPath);
}
}

/**
Expand All @@ -175,11 +189,15 @@ void contract(const std::string& output_path, const std::string& vk_path)
acir_composer->load_verification_key(barretenberg::srs::get_crs_factory(), std::move(vk_data));
auto contract = acir_composer->get_solidity_verifier();

std::cout << contract << std::endl;
write_file(output_path, { contract.begin(), contract.end() });

info("contract written to: ", output_path);
if (output_path == "-") {
writeStringToStdout(contract);
vinfo("contract written to stdout");
} else {
write_file(output_path, { contract.begin(), contract.end() });
vinfo("contract written to: ", output_path);
}
}

/**
* @brief Converts a proof from a byte array into a list of field elements
*
Expand Down Expand Up @@ -212,10 +230,13 @@ void proofAsFields(const std::string& proof_path, std::string const& vk_path, co
auto data = acir_composer->serialize_proof_into_fields(read_file(proof_path), vk_data.num_public_inputs);
auto json = format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]");

std::cout << json << std::endl;
write_file(output_path, { json.begin(), json.end() });

info("proof as fields written to: ", output_path);
if (output_path == "-") {
writeStringToStdout(json);
vinfo("proof as fields written to stdout");
} else {
write_file(output_path, { json.begin(), json.end() });
vinfo("proof as fields written to: ", output_path);
}
}

/**
Expand All @@ -242,11 +263,13 @@ void vkAsFields(const std::string& vk_path, const std::string& output_path)
std::rotate(data.begin(), data.end() - 1, data.end());

auto json = format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]");

std::cout << json << std::endl;
write_file(output_path, { json.begin(), json.end() });

info("vk as fields written to: ", output_path);
if (output_path == "-") {
writeStringToStdout(json);
vinfo("vk as fields written to stdout");
} else {
write_file(output_path, { json.begin(), json.end() });
vinfo("vk as fields written to: ", output_path);
}
}

bool flagPresent(std::vector<std::string>& args, const std::string& flag)
Expand Down
16 changes: 16 additions & 0 deletions circuits/cpp/barretenberg/cpp/src/barretenberg/bb/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# BB

## Why is this needed?

Barretenberg is a library that allows one to create and verify proofs. One way to specify the circuit that one will use to create and verify
proofs over is to use the Barretenberg standard library. Another way, which pertains to this module is to supply the circuit description using
an IR called [ACIR](https://github.com/noir-lang/acvm). This binary will take as input ACIR and witness values described in the IR to create proofs.


## FilePath vs Stdout

For commands which allow you to send the output to a file using `-o {filePath}`, there is also the option to send the output to stdout by using `-o -`.

## Maximum Circuit Size

Currently the binary downloads an SRS that can be used to prove the maximum circuit size. This maximum circuit size parameter is a constant in the code and has been set to $2^{23}$ as of writing. This maximum circuit size differs from the maximum circuit size that one can prove in the browser, due to WASM limits.
11 changes: 0 additions & 11 deletions circuits/cpp/barretenberg/cpp/src/barretenberg/bb/vinfo.hpp

This file was deleted.

58 changes: 38 additions & 20 deletions circuits/cpp/barretenberg/ts/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export async function proveAndVerify(bytecodePath: string, witnessPath: string,

debug(`verifying...`);
const verified = await api.acirVerifyProof(acirComposer, proof, isRecursive);
process.stdout.write(`${verified}`);
debug(`verified: ${verified}`);
return verified;
} finally {
await api.destroy();
Expand All @@ -112,10 +112,13 @@ export async function prove(
const proof = await api.acirCreateProof(acirComposer, bytecode, witness, isRecursive);
debug(`done.`);

process.stdout.write(proof);
writeFileSync(outputPath, proof);

debug(`proof written to: ${outputPath}`);
if (outputPath === '-') {
process.stdout.write(proof);
debug(`proof written to stdout`);
} else {
writeFileSync(outputPath, proof);
debug(`proof written to: ${outputPath}`);
}
} finally {
await api.destroy();
}
Expand All @@ -135,8 +138,7 @@ export async function verify(proofPath: string, isRecursive: boolean, vkPath: st
try {
await api.acirLoadVerificationKey(acirComposer, new RawBuffer(readFileSync(vkPath)));
const verified = await api.acirVerifyProof(acirComposer, readFileSync(proofPath), isRecursive);

process.stdout.write(`${verified}`);
debug(`verified: ${verified}`);
return verified;
} finally {
await api.destroy();
Expand All @@ -149,10 +151,13 @@ export async function contract(outputPath: string, vkPath: string) {
await api.acirLoadVerificationKey(acirComposer, new RawBuffer(readFileSync(vkPath)));
const contract = await api.acirGetSolidityVerifier(acirComposer);

process.stdout.write(contract);
writeFileSync(outputPath, contract);

debug(`contract written to: ${outputPath}`);
if (outputPath === '-') {
process.stdout.write(contract);
debug(`contract written to stdout`);
} else {
writeFileSync(outputPath, contract);
debug(`contract written to: ${outputPath}`);
}
} finally {
await api.destroy();
}
Expand All @@ -168,10 +173,13 @@ export async function writeVk(bytecodePath: string, crsPath: string, outputPath:
debug('initing verification key...');
const vk = await api.acirGetVerificationKey(acirComposer);

process.stdout.write(vk);
writeFileSync(outputPath, vk);

debug(`vk written to: ${outputPath}`);
if (outputPath === '-') {
process.stdout.write(vk);
debug(`vk written to stdout`);
} else {
writeFileSync(outputPath, vk);
debug(`vk written to: ${outputPath}`);
}
} finally {
await api.destroy();
}
Expand All @@ -189,8 +197,13 @@ export async function proofAsFields(proofPath: string, numInnerPublicInputs: num
);
const jsonProofAsFields = JSON.stringify(proofAsFields.map(f => f.toString()));

process.stdout.write(jsonProofAsFields);
writeFileSync(outputPath, jsonProofAsFields);
if (outputPath === '-') {
process.stdout.write(jsonProofAsFields);
debug(`proofAsFields written to stdout`);
} else {
writeFileSync(outputPath, jsonProofAsFields);
debug(`proofAsFields written to: ${outputPath}`);
}

debug('done.');
} finally {
Expand All @@ -208,8 +221,13 @@ export async function vkAsFields(vkPath: string, vkeyOutputPath: string) {
const output = [vkHash, ...vkAsFields].map(f => f.toString());
const jsonVKAsFields = JSON.stringify(output);

process.stdout.write(jsonVKAsFields);
writeFileSync(vkeyOutputPath, jsonVKAsFields);
if (vkeyOutputPath === '-') {
process.stdout.write(jsonVKAsFields);
debug(`vkAsFields written to stdout`);
} else {
writeFileSync(vkeyOutputPath, jsonVKAsFields);
debug(`vkAsFields written to: ${vkeyOutputPath}`);
}

debug('done.');
} finally {
Expand Down Expand Up @@ -277,7 +295,7 @@ program
.command('contract')
.description('Output solidity verification key contract.')
.option('-b, --bytecode-path <path>', 'Specify the bytecode path', './target/main.bytecode')
.option('-o, --output-path <path>', 'Specify the path to write the contract', '-')
.option('-o, --output-path <path>', 'Specify the path to write the contract', './target/contract.sol')
.requiredOption('-k, --vk <path>', 'path to a verification key. avoids recomputation.')
.action(async ({ outputPath, vk }) => {
handleGlobalOptions();
Expand Down

0 comments on commit 28d722e

Please sign in to comment.