From 28d722ef965d907b7b7820ccdd7ee0afc97c88fa Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 28 Aug 2023 18:02:22 +0100 Subject: [PATCH] chore: Refactor Cli interface to be more unix-like (#1833) 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). --- .../cpp/src/barretenberg/bb/get_crs.hpp | 2 +- .../cpp/src/barretenberg/bb/log.hpp | 66 +++++++++++++++ .../cpp/src/barretenberg/bb/main.cpp | 83 ++++++++++++------- .../cpp/src/barretenberg/bb/readme.md | 16 ++++ .../cpp/src/barretenberg/bb/vinfo.hpp | 11 --- circuits/cpp/barretenberg/ts/src/main.ts | 58 ++++++++----- 6 files changed, 174 insertions(+), 62 deletions(-) create mode 100644 circuits/cpp/barretenberg/cpp/src/barretenberg/bb/log.hpp create mode 100644 circuits/cpp/barretenberg/cpp/src/barretenberg/bb/readme.md delete mode 100644 circuits/cpp/barretenberg/cpp/src/barretenberg/bb/vinfo.hpp diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/get_crs.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/get_crs.hpp index 4f9bc58192c..808bb5a0f01 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/get_crs.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/get_crs.hpp @@ -1,7 +1,7 @@ #pragma once #include "exec_pipe.hpp" #include "file_io.hpp" -#include "vinfo.hpp" +#include "log.hpp" #include #include #include diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/log.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/log.hpp new file mode 100644 index 00000000000..8c378b71ee2 --- /dev/null +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/log.hpp @@ -0,0 +1,66 @@ +#pragma once +#include +#include + +extern bool verbose; + +template 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& data) +{ + for (auto byte : data) { + // Safety: a byte and a char occupy one byte + std::cout.put(static_cast(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 bytes; + bytes.reserve(sizeof(uint64_t)); + + for (size_t i = 0; i < sizeof(uint64_t); ++i) { + bytes.push_back(static_cast(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); + } +} \ No newline at end of file diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp index 506341e1849..1f2ae5c1654 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -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 #include #include @@ -11,9 +12,9 @@ #include 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"; @@ -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 @@ -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 * @@ -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 * @@ -103,8 +110,11 @@ 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(gate_count)); + vinfo("gate count: ", gate_count); } + /** * @brief Verifies a proof for an ACIR circuit * @@ -112,7 +122,8 @@ void gateCount(const std::string& bytecodePath) * 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 @@ -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; } @@ -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); + } } /** @@ -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 * @@ -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); + } } /** @@ -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& args, const std::string& flag) diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/readme.md b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/readme.md new file mode 100644 index 00000000000..ea265a3a8c1 --- /dev/null +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/readme.md @@ -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. \ No newline at end of file diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/vinfo.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/vinfo.hpp deleted file mode 100644 index 31251232762..00000000000 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/vinfo.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include - -extern bool verbose; - -template inline void vinfo(Args... args) -{ - if (verbose) { - info(args...); - } -} \ No newline at end of file diff --git a/circuits/cpp/barretenberg/ts/src/main.ts b/circuits/cpp/barretenberg/ts/src/main.ts index 87f73773592..6cf21570c99 100755 --- a/circuits/cpp/barretenberg/ts/src/main.ts +++ b/circuits/cpp/barretenberg/ts/src/main.ts @@ -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(); @@ -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(); } @@ -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(); @@ -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(); } @@ -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(); } @@ -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 { @@ -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 { @@ -277,7 +295,7 @@ program .command('contract') .description('Output solidity verification key contract.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/main.bytecode') - .option('-o, --output-path ', 'Specify the path to write the contract', '-') + .option('-o, --output-path ', 'Specify the path to write the contract', './target/contract.sol') .requiredOption('-k, --vk ', 'path to a verification key. avoids recomputation.') .action(async ({ outputPath, vk }) => { handleGlobalOptions();