Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
aztec_process.cpp
Go to the documentation of this file.
1#ifndef __wasm__
2#include "aztec_process.hpp"
11#include <filesystem>
12#include <fstream>
13#include <iomanip>
14#include <nlohmann/json.hpp>
15#include <sstream>
16
17#ifdef ENABLE_AVM_TRANSPILER
18// Include avm_transpiler header
19#include <avm_transpiler.h>
20#endif
21
22namespace bb {
23
24namespace {
25
29std::vector<uint8_t> extract_bytecode(const nlohmann::json& function)
30{
31 if (!function.contains("bytecode")) {
32 throw_or_abort("Function missing bytecode field");
33 }
34
35 const auto& base64_bytecode = function["bytecode"].get<std::string>();
36 return decode_bytecode(base64_bytecode);
37}
38
42std::string compute_bytecode_hash(const std::vector<uint8_t>& bytecode)
43{
44 auto hash = crypto::sha256(bytecode);
45 std::ostringstream oss;
46 for (auto byte : hash) {
47 oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
48 }
49 return oss.str();
50}
51
55std::filesystem::path get_cache_dir()
56{
57 const char* home = std::getenv("HOME");
58 if (!home) {
59 home = ".";
60 }
61 std::filesystem::path cache_dir = std::filesystem::path(home) / ".bb" / BB_VERSION_PLACEHOLDER / "vk_cache";
62 std::filesystem::create_directories(cache_dir);
63 return cache_dir;
64}
65
69bool is_private_constrained_function(const nlohmann::json& function)
70{
71 bool is_public = false;
72 bool is_unconstrained = false;
73
74 // Check custom_attributes for "public"
75 if (function.contains("custom_attributes") && function["custom_attributes"].is_array()) {
76 for (const auto& attr : function["custom_attributes"]) {
77 if (attr.is_string() && attr.get<std::string>() == "public") {
78 is_public = true;
79 break;
80 }
81 }
82 }
83
84 // Check is_unconstrained
85 if (function.contains("is_unconstrained") && function["is_unconstrained"].is_boolean()) {
86 is_unconstrained = function["is_unconstrained"].get<bool>();
87 }
88
89 return !is_public && !is_unconstrained;
90}
91
95std::vector<uint8_t> get_or_generate_cached_vk(const std::filesystem::path& cache_dir,
96 const std::string& circuit_name,
97 const std::vector<uint8_t>& bytecode,
98 bool force)
99{
100 std::string hash_str = compute_bytecode_hash(bytecode);
101 std::filesystem::path vk_cache_path = cache_dir / (hash_str + ".vk");
102
103 // Check cache unless force is true
104 if (!force && std::filesystem::exists(vk_cache_path)) {
105 info("Verification key already in cache: ", hash_str);
106 return read_file(vk_cache_path);
107 }
108
109 // Generate new VK
110 info("Generating verification key: ", hash_str);
111 auto response =
112 bbapi::ClientIvcComputeStandaloneVk{ .circuit = { .name = circuit_name, .bytecode = bytecode } }.execute();
113
114 // Cache the VK
115 write_file(vk_cache_path, response.bytes);
116
117 return response.bytes;
118}
119
123void generate_vks_for_functions(const std::filesystem::path& cache_dir,
125 bool force)
126{
127 // Generate VKs in parallel (logging removed to avoid data races)
128 parallel_for(functions.size(), [&](size_t i) {
129 auto* function = functions[i];
130 std::string fn_name = (*function)["name"].get<std::string>();
131
132 // Get bytecode from function
133 auto bytecode = extract_bytecode(*function);
134
135 // Generate and cache VK (this will log internally if needed)
136 get_or_generate_cached_vk(cache_dir, fn_name, bytecode, force);
137 });
138
139 // Update JSON with VKs from cache (sequential is fine here, it's fast)
140 for (auto* function : functions) {
141 std::string fn_name = (*function)["name"].get<std::string>();
142
143 // Get bytecode to compute hash
144 auto bytecode = extract_bytecode(*function);
145
146 // Read VK from cache
147 std::string hash_str = compute_bytecode_hash(bytecode);
148 std::filesystem::path vk_cache_path = cache_dir / (hash_str + ".vk");
149 auto vk_data = read_file(vk_cache_path);
150
151 // Encode to base64 and store in JSON
152 std::string encoded_vk = base64_encode(vk_data.data(), vk_data.size(), false);
153 (*function)["verification_key"] = encoded_vk;
154 }
155}
156
157} // anonymous namespace
158
162bool transpile_artifact([[maybe_unused]] const std::string& input_path, [[maybe_unused]] const std::string& output_path)
163{
164#ifdef ENABLE_AVM_TRANSPILER
165 info("Transpiling: ", input_path, " -> ", output_path);
166
167 auto result = avm_transpile_file(input_path.c_str(), output_path.c_str());
168
169 if (result.success == 0) {
170 if (result.error_message) {
171 std::string error_msg(result.error_message);
172 if (error_msg == "Contract already transpiled") {
173 // Already transpiled, copy if different paths
174 if (input_path != output_path) {
175 std::filesystem::copy_file(
176 input_path, output_path, std::filesystem::copy_options::overwrite_existing);
177 }
178 } else {
179 info("Transpilation failed: ", error_msg);
180 avm_free_result(&result);
181 return false;
182 }
183 } else {
184 info("Transpilation failed");
185 avm_free_result(&result);
186 return false;
187 }
188 }
189
190 avm_free_result(&result);
191
192 info("Transpiled: ", input_path, " -> ", output_path);
193#else
194 throw_or_abort("AVM Transpiler is not enabled. Please enable it to use bb aztec_process.");
195#endif
196 return true;
197}
198
199bool process_aztec_artifact(const std::string& input_path, const std::string& output_path, bool force)
200{
201 if (!transpile_artifact(input_path, output_path)) {
202 return false;
203 }
204
205 // Verify output exists
206 if (!std::filesystem::exists(output_path)) {
207 throw_or_abort("Output file does not exist after transpilation");
208 }
209
210 // Step 2: Generate verification keys
211 auto cache_dir = get_cache_dir();
212 info("Generating verification keys for functions in ", std::filesystem::path(output_path).filename().string());
213 info("Cache directory: ", cache_dir.string());
214
215 // Read and parse artifact JSON
216 auto artifact_content = read_file(output_path);
217 std::string artifact_str(artifact_content.begin(), artifact_content.end());
218 auto artifact_json = nlohmann::json::parse(artifact_str);
219
220 if (!artifact_json.contains("functions")) {
221 info("Warning: No functions found in artifact");
222 return true;
223 }
224
225 // Filter to private constrained functions
226 std::vector<nlohmann::json*> private_functions;
227 for (auto& function : artifact_json["functions"]) {
228 if (is_private_constrained_function(function)) {
229 private_functions.push_back(&function);
230 }
231 }
232
233 if (private_functions.empty()) {
234 info("No private constrained functions found");
235 return true;
236 }
237
238 // Generate VKs
239 generate_vks_for_functions(cache_dir, private_functions, force);
240
241 // Write updated JSON back to file
242 std::ofstream out_file(output_path);
243 out_file << artifact_json.dump(2) << std::endl;
244 out_file.close();
245
246 info("Successfully processed: ", input_path, " -> ", output_path);
247 return true;
248}
249
250std::vector<std::string> find_contract_artifacts(const std::string& search_path)
251{
252 std::vector<std::string> artifacts;
253
254 // Recursively search for .json files in target/ directories, excluding cache/
255 for (const auto& entry : std::filesystem::recursive_directory_iterator(search_path)) {
256 if (!entry.is_regular_file()) {
257 continue;
258 }
259
260 const auto& path = entry.path();
261
262 // Must be a .json file
263 if (path.extension() != ".json") {
264 continue;
265 }
266
267 // Must be in a target/ directory
268 std::string path_str = path.string();
269 if (path_str.find("/target/") == std::string::npos && path_str.find("\\target\\") == std::string::npos) {
270 continue;
271 }
272
273 // Exclude cache directories and function artifact temporaries
274 if (path_str.find("/cache/") != std::string::npos || path_str.find("\\cache\\") != std::string::npos ||
275 path_str.find(".function_artifact_") != std::string::npos) {
276 continue;
277 }
278
279 artifacts.push_back(path.string());
280 }
281
282 return artifacts;
283}
284
285bool process_all_artifacts(const std::string& search_path, bool force)
286{
287 auto artifacts = find_contract_artifacts(search_path);
288
289 if (artifacts.empty()) {
290 info("No contract artifacts found. Please compile your contracts first with 'nargo compile'.");
291 return false;
292 }
293
294 info("Found ", artifacts.size(), " contract artifact(s) to process");
295
296 bool all_success = true;
297 for (const auto& artifact : artifacts) {
298 // Process in-place (input == output)
299 if (!process_aztec_artifact(artifact, artifact, force)) {
300 all_success = false;
301 }
302 }
303
304 if (all_success) {
305 info("Contract postprocessing complete!");
306 }
307
308 return all_success;
309}
310
311} // namespace bb
312#endif
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len, bool url)
Definition base64.cpp:117
ClientIVC-specific command definitions for the Barretenberg RPC API.
void info(Args... args)
Definition log.hpp:74
std::vector< uint8_t > decode_bytecode(const std::string &base64_bytecode)
void hash(State &state) noexcept
Sha256Hash sha256(const ByteContainer &input)
Definition sha256.cpp:142
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)
const char *const BB_VERSION_PLACEHOLDER
Definition version.cpp:6
bool process_all_artifacts(const std::string &search_path, bool force)
Process all discovered contract artifacts in a directory tree.
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)
Definition file_io.hpp:29
void write_file(const std::string &filename, std::vector< uint8_t > const &data)
Definition file_io.hpp:58
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:111
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
void throw_or_abort(std::string const &err)