14#include <nlohmann/json.hpp>
24#ifdef ENABLE_AVM_TRANSPILER
26#include <avm_transpiler.h>
36std::vector<uint8_t> extract_bytecode(
const nlohmann::json& function)
38 if (!function.contains(
"bytecode")) {
42 const auto& base64_bytecode = function[
"bytecode"].get<std::string>();
49std::string compute_bytecode_hash(
const std::vector<uint8_t>&
bytecode)
52 std::ostringstream oss;
53 for (
auto byte : hash) {
54 oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(
byte);
62std::filesystem::path get_cache_dir()
68 std::filesystem::path cache_dir = std::filesystem::path(home) /
".bb" /
BB_VERSION /
"vk_cache";
69 std::filesystem::create_directories(cache_dir);
76bool is_private_constrained_function(
const nlohmann::json& function)
78 bool is_public =
false;
79 bool is_unconstrained =
false;
82 if (function.contains(
"custom_attributes") && function[
"custom_attributes"].is_array()) {
83 for (
const auto& attr : function[
"custom_attributes"]) {
84 if (attr.is_string() && attr.get<std::string>() ==
"public") {
92 if (function.contains(
"is_unconstrained") && function[
"is_unconstrained"].is_boolean()) {
93 is_unconstrained = function[
"is_unconstrained"].get<
bool>();
96 return !is_public && !is_unconstrained;
108class VkCacheEntryLock {
110 explicit VkCacheEntryLock(
const std::filesystem::path& entry_path)
113 std::filesystem::path lock_path = entry_path;
114 lock_path +=
".lock";
115 fd = open(lock_path.c_str(), O_CREAT | O_RDWR, 0644);
120 static_cast<void>(entry_path);
131 VkCacheEntryLock(
const VkCacheEntryLock&) =
delete;
132 VkCacheEntryLock& operator=(
const VkCacheEntryLock&) =
delete;
133 VkCacheEntryLock(VkCacheEntryLock&&) =
delete;
134 VkCacheEntryLock& operator=(VkCacheEntryLock&&) =
delete;
145std::vector<uint8_t> get_or_generate_cached_app_vk(
const std::filesystem::path& cache_dir,
146 const std::string& circuit_name,
147 const std::vector<uint8_t>&
bytecode,
150 std::string hash_str = compute_bytecode_hash(
bytecode);
151 std::filesystem::path vk_cache_path = cache_dir / (hash_str +
".vk");
155 VkCacheEntryLock lock(vk_cache_path);
158 if (!force && std::filesystem::exists(vk_cache_path)) {
159 info(
"Verification key already in cache: ", hash_str);
164 info(
"Generating verification key: ", hash_str);
171 std::filesystem::path tmp_path = vk_cache_path;
175 std::filesystem::rename(tmp_path, vk_cache_path, ec);
179 std::filesystem::remove(tmp_path, ec);
182 return response.bytes;
188void generate_vks_for_functions(
const std::filesystem::path& cache_dir,
197 const size_t num_functions = functions.size();
202 size_t actual_tasks = std::min(num_functions, total_cpus);
203 size_t threads_per_task = std::min(total_cpus,
std::max(
size_t{ 2 }, total_cpus / actual_tasks * 2));
206 std::atomic<size_t> current_function{ 0 };
209 auto worker = [&]() {
215 while ((func_idx = current_function.fetch_add(1)) < num_functions) {
216 auto* function = functions[func_idx];
217 std::string fn_name = (*function)[
"name"].get<std::string>();
220 auto bytecode = extract_bytecode(*function);
223 get_or_generate_cached_app_vk(cache_dir, fn_name,
bytecode, force);
228 std::vector<std::thread> threads;
229 threads.reserve(actual_tasks);
231 for (
size_t i = 0; i < actual_tasks; ++i) {
232 threads.emplace_back(worker);
236 for (
auto& t : threads) {
241 for (
auto* function : functions) {
242 std::string fn_name = (*function)[
"name"].get<std::string>();
245 auto bytecode = extract_bytecode(*function);
248 std::string hash_str = compute_bytecode_hash(
bytecode);
249 std::filesystem::path vk_cache_path = cache_dir / (hash_str +
".vk");
253 std::string encoded_vk =
base64_encode(vk_data.data(), vk_data.size(),
false);
254 (*function)[
"verification_key"] = encoded_vk;
263bool transpile_artifact([[maybe_unused]]
const std::string& input_path, [[maybe_unused]]
const std::string& output_path)
265#ifdef ENABLE_AVM_TRANSPILER
266 info(
"Transpiling: ", input_path,
" -> ", output_path);
268 auto result = avm_transpile_file(input_path.c_str(), output_path.c_str());
270 if (
result.success == 0) {
271 if (
result.error_message) {
272 std::string error_msg(
result.error_message);
273 if (error_msg ==
"Contract already transpiled") {
275 if (input_path != output_path) {
276 std::filesystem::copy_file(
277 input_path, output_path, std::filesystem::copy_options::overwrite_existing);
280 info(
"Transpilation failed: ", error_msg);
285 info(
"Transpilation failed");
293 info(
"Transpiled: ", input_path,
" -> ", output_path);
295 throw_or_abort(
"AVM Transpiler is not enabled. Please enable it to use bb aztec_process.");
307 if (!std::filesystem::exists(output_path)) {
312 auto cache_dir = get_cache_dir();
313 info(
"Generating verification keys for functions in ", std::filesystem::path(output_path).filename().
string());
314 info(
"Cache directory: ", cache_dir.string());
317 auto artifact_content =
read_file(output_path);
318 std::string artifact_str(artifact_content.begin(), artifact_content.end());
319 auto artifact_json = nlohmann::json::parse(artifact_str);
321 if (!artifact_json.contains(
"functions")) {
322 info(
"Warning: No functions found in artifact");
329 const std::string internal_prefix =
"__aztec_nr_internals__";
330 for (
auto& function : artifact_json[
"functions"]) {
331 auto&
name = function[
"name"];
332 if (
name.is_string()) {
333 std::string fn_name =
name.get<std::string>();
334 if (fn_name.size() >= internal_prefix.size() &&
335 fn_name.compare(0, internal_prefix.size(), internal_prefix) == 0) {
336 name = fn_name.substr(internal_prefix.size());
343 for (
auto& function : artifact_json[
"functions"]) {
344 if (is_private_constrained_function(function)) {
345 private_functions.push_back(&function);
349 if (!private_functions.empty()) {
351 generate_vks_for_functions(cache_dir, private_functions, force);
353 info(
"No private constrained functions found");
357 std::ofstream out_file(output_path);
358 out_file << artifact_json.dump(2) <<
std::endl;
361 info(
"Successfully processed: ", input_path,
" -> ", output_path);
367 std::vector<std::string> artifacts;
370 for (
const auto& entry : std::filesystem::recursive_directory_iterator(search_path)) {
371 if (!entry.is_regular_file()) {
375 const auto& path = entry.path();
378 if (path.extension() !=
".json") {
383 std::string path_str = path.string();
384 if (path_str.find(
"/target/") == std::string::npos && path_str.find(
"\\target\\") == std::string::npos) {
389 if (path_str.find(
"/cache/") != std::string::npos || path_str.find(
"\\cache\\") != std::string::npos ||
390 path_str.find(
".function_artifact_") != std::string::npos) {
394 artifacts.push_back(path.string());
404 if (artifacts.empty()) {
405 info(
"No contract artifacts found in '", search_path,
"'.");
409 info(
"Found ", artifacts.size(),
" contract artifact(s) to process");
411 bool all_success =
true;
412 for (
const auto& artifact : artifacts) {
420 info(
"Contract postprocessing complete!");
430 if (!std::filesystem::exists(input_path)) {
435 auto artifact_content =
read_file(input_path);
436 std::string artifact_str(artifact_content.begin(), artifact_content.end());
437 auto artifact_json = nlohmann::json::parse(artifact_str);
439 if (!artifact_json.contains(
"functions")) {
445 auto cache_dir = get_cache_dir();
448 for (
const auto& function : artifact_json[
"functions"]) {
449 if (!is_private_constrained_function(function)) {
453 std::string fn_name = function[
"name"].get<std::string>();
454 auto bytecode = extract_bytecode(function);
455 std::string hash_str = compute_bytecode_hash(
bytecode);
456 std::filesystem::path vk_cache_path = cache_dir / (hash_str +
".vk");
459 std::cout << hash_str <<
":" << vk_cache_path.string() <<
":" << fn_name <<
std::endl;
463 }
catch (
const std::exception& e) {
464 info(
"Error getting cache paths: ", e.what());
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len, bool url)
Chonk-specific command definitions for the Barretenberg RPC API.
std::vector< uint8_t > bytecode
std::vector< uint8_t > decode_bytecode(const std::string &base64_bytecode)
Sha256Hash sha256(const ByteContainer &input)
SHA-256 hash function (FIPS 180-4)
Entry point for Barretenberg command-line interface.
bool transpile_artifact(const std::string &input_path, const std::string &output_path)
Transpile the artifact file (or copy if transpiler not enabled)
bool process_all_artifacts(const std::string &search_path, bool force)
Process all discovered contract artifacts in a directory tree.
bool get_cache_paths(const std::string &input_path)
Get cache paths for all verification keys in an artifact.
bool process_aztec_artifact(const std::string &input_path, const std::string &output_path, bool force)
Process Aztec contract artifacts: transpile and generate verification keys.
std::vector< std::string > find_contract_artifacts(const std::string &search_path)
Find all contract artifacts in target/ directories.
std::vector< uint8_t > read_file(const std::string &filename, size_t bytes=0)
void set_parallel_for_concurrency(size_t num_cores)
void write_file(const std::string &filename, std::span< const uint8_t > data)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
void throw_or_abort(std::string const &err)