Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
get_bn254_crs.cpp
Go to the documentation of this file.
1#include "get_bn254_crs.hpp"
9#include "bn254_crs_data.hpp"
11#include "http_download.hpp"
12#include <algorithm>
13#include <atomic>
14#include <span>
15
16namespace {
17// Primary CRS URL (Cloudflare R2)
18constexpr const char* CRS_PRIMARY_URL = "http://crs.aztec-cdn.foundation/g1.dat";
19// Fallback CRS URL (AWS S3)
20constexpr const char* CRS_FALLBACK_URL = "http://crs.aztec-labs.com/g1.dat";
21
26size_t round_up_to_chunk_boundary(size_t num_points)
27{
28 if (num_points >= bb::srs::SRS_TOTAL_POINTS) {
29 return bb::srs::SRS_TOTAL_POINTS;
30 }
31 size_t rounded = ((num_points + bb::srs::SRS_CHUNK_SIZE_POINTS - 1) / bb::srs::SRS_CHUNK_SIZE_POINTS) *
32 bb::srs::SRS_CHUNK_SIZE_POINTS;
33 return std::min(rounded, bb::srs::SRS_TOTAL_POINTS);
34}
35
43void verify_bn254_crs_integrity(const std::vector<uint8_t>& data)
44{
45 size_t num_full_chunks = data.size() / bb::srs::SRS_CHUNK_SIZE_BYTES;
46 size_t chunks_to_verify = std::min(num_full_chunks, static_cast<size_t>(bb::srs::SRS_NUM_FULL_CHUNKS));
47
48 // Sentinel value means "no failure found yet"
49 const size_t sentinel = bb::srs::SRS_NUM_CHUNKS;
50 std::atomic<size_t> failed_chunk{ sentinel };
51
52 // Verify all complete 8MB chunks in parallel
53 if (chunks_to_verify > 0) {
54 bb::parallel_for([&](const bb::ThreadChunk& tc) {
55 for (size_t i : tc.range(chunks_to_verify)) {
56 // Early exit if another thread already found a mismatch
57 if (failed_chunk.load(std::memory_order_relaxed) < sentinel) {
58 return;
59 }
60 size_t offset = i * bb::srs::SRS_CHUNK_SIZE_BYTES;
61 auto chunk = std::span<const uint8_t>(data.data() + offset, bb::srs::SRS_CHUNK_SIZE_BYTES);
62 auto hash = bb::crypto::sha256(chunk);
63 if (hash != bb::srs::BN254_G1_CHUNK_HASHES[i]) {
64 size_t expected = sentinel;
65 failed_chunk.compare_exchange_strong(expected, i, std::memory_order_relaxed);
66 }
67 }
68 });
69 }
70
71 // Verify partial last chunk (e.g. the 64-byte tail of the full CRS)
72 size_t tail_offset = chunks_to_verify * bb::srs::SRS_CHUNK_SIZE_BYTES;
73 size_t tail_size = data.size() - tail_offset;
74 if (tail_size > 0 && chunks_to_verify < bb::srs::SRS_NUM_CHUNKS) {
75 auto tail = std::span<const uint8_t>(data.data() + tail_offset, tail_size);
76 auto hash = bb::crypto::sha256(tail);
77 if (hash != bb::srs::BN254_G1_CHUNK_HASHES[chunks_to_verify]) {
78 size_t expected = sentinel;
79 failed_chunk.compare_exchange_strong(expected, chunks_to_verify, std::memory_order_relaxed);
80 }
81 }
82
83 size_t bad = failed_chunk.load();
84 if (bad < sentinel) {
85 size_t offset = bad * bb::srs::SRS_CHUNK_SIZE_BYTES;
86 throw_or_abort("CRS integrity check failed: SHA-256 mismatch at chunk " + std::to_string(bad) + " (bytes " +
87 std::to_string(offset) + "+)");
88 }
89
90 vinfo("verified ", chunks_to_verify + (tail_size > 0 ? 1 : 0), " BN254 G1 CRS chunks via SHA-256");
91}
92
93std::vector<uint8_t> download_bn254_g1_data(size_t num_points,
94 const std::string& primary_url,
95 const std::string& fallback_url)
96{
97 size_t g1_end = (num_points * sizeof(bb::g1::affine_element)) - 1;
98
99 // Try primary URL first, with fallback on failure.
100 // Note: WASM is compiled with -fno-exceptions, so try/catch is not available.
101 // In practice, WASM never calls this function - it initializes CRS via srs_init_srs from JavaScript.
102 std::vector<uint8_t> data;
103#ifndef __wasm__
104 try {
105 data = bb::srs::http_download(primary_url, 0, g1_end);
106 } catch (const std::exception& e) {
107 vinfo("Primary CRS download failed: ", e.what(), ". Trying fallback...");
108 data = bb::srs::http_download(fallback_url, 0, g1_end);
109 }
110#else
111 // WASM fallback: just try primary (will abort on failure)
112 data = bb::srs::http_download(primary_url, 0, g1_end);
113 static_cast<void>(fallback_url);
114#endif
115
116 if (data.size() < sizeof(bb::g1::affine_element)) {
117 throw_or_abort("Downloaded g1 data is too small");
118 }
119
120 // Quick sanity check: verify the first G1 point is the expected generator
121 auto first_element = from_buffer<bb::g1::affine_element>(data, 0);
122 if (first_element != bb::srs::BN254_G1_FIRST_ELEMENT) {
123 throw_or_abort("Downloaded BN254 G1 CRS first element does not match expected point.");
124 }
125
126 // Full integrity verification: SHA-256 chunk hashes in parallel
127 verify_bn254_crs_integrity(data);
128
129 return data;
130}
131} // namespace
132
133namespace bb {
134
135// Main implementation with configurable URLs
136std::vector<g1::affine_element> get_bn254_g1_data(const std::filesystem::path& path,
137 size_t num_points,
138 bool allow_download,
139 const std::string& primary_url,
140 const std::string& fallback_url)
141{
142 BB_BENCH_NAME("get_bn254_g1_data");
143 std::filesystem::create_directories(path);
144
145 auto g1_path = path / "bn254_g1.dat";
146 auto lock_path = path / "crs.lock";
147 // Acquire exclusive lock to prevent simultaneous downloads
148 FileLockGuard lock(lock_path.string());
149
150 size_t g1_downloaded_points = get_file_size(g1_path) / sizeof(g1::affine_element);
151
152 auto deserialize_points = [](const std::vector<uint8_t>& data, size_t n) {
153 auto points = std::vector<g1::affine_element>(n);
154 parallel_for([&](const ThreadChunk& tc) {
155 for (size_t i : tc.range(n)) {
156 points[i] = from_buffer<g1::affine_element>(data, i * sizeof(g1::affine_element));
157 }
158 });
159 return points;
160 };
161
162 if (g1_downloaded_points >= num_points) {
163 vinfo("using cached bn254 crs with num points ", std::to_string(g1_downloaded_points), " at ", g1_path);
164 auto data = read_file(g1_path, num_points * sizeof(g1::affine_element));
165 return deserialize_points(data, num_points);
166 }
167
168 if (!allow_download && g1_downloaded_points == 0) {
169 throw_or_abort("bn254 g1 data not found and download not allowed in this context");
170 } else if (!allow_download) {
171 throw_or_abort(format("bn254 g1 data had ",
172 g1_downloaded_points,
173 " points and ",
174 num_points,
175 " were requested but download not allowed in this context"));
176 }
177
178 // Double-check after acquiring lock (another process may have downloaded while we waited)
179 g1_downloaded_points = get_file_size(g1_path) / sizeof(g1::affine_element);
180 if (g1_downloaded_points >= num_points) {
181 vinfo("using cached bn254 crs with num points ", std::to_string(g1_downloaded_points), " at ", g1_path);
182 auto data = read_file(g1_path, num_points * sizeof(g1::affine_element));
183 return deserialize_points(data, num_points);
184 }
185
186 // Round up to chunk boundary so every downloaded byte is hash-verified
187 size_t download_points = round_up_to_chunk_boundary(num_points);
188 vinfo("downloading bn254 crs (", num_points, " points requested, downloading ", download_points, ")...");
189 auto data = download_bn254_g1_data(download_points, primary_url, fallback_url);
190 write_file(g1_path, data);
191
192 return deserialize_points(data, num_points);
193}
194
195// Default overload using production URLs
196std::vector<g1::affine_element> get_bn254_g1_data(const std::filesystem::path& path,
197 size_t num_points,
198 bool allow_download)
199{
200 return get_bn254_g1_data(path, num_points, allow_download, CRS_PRIMARY_URL, CRS_FALLBACK_URL);
201}
202
203} // namespace bb
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:225
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:42
std::string format(Args... args)
Definition log.hpp:23
#define vinfo(...)
Definition log.hpp:94
const std::vector< MemoryValue > data
ssize_t offset
Definition engine.cpp:52
Sha256Hash sha256(const ByteContainer &input)
SHA-256 hash function (FIPS 180-4)
Definition sha256.cpp:150
const size_t num_points
std::vector< uint8_t > http_download(const std::string &url, size_t start_byte=0, size_t end_byte=0)
Download data from a URL with optional Range header support.
constexpr g1::affine_element BN254_G1_FIRST_ELEMENT
Expected first G1 element from BN254 CRS.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::vector< g1::affine_element > get_bn254_g1_data(const std::filesystem::path &path, size_t num_points, bool allow_download, const std::string &primary_url, const std::string &fallback_url)
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::span< const uint8_t > data)
Definition file_io.hpp:99
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:111
size_t get_file_size(std::string const &filename)
Definition file_io.hpp:17
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
auto range(size_t size, size_t offset=0) const
Definition thread.hpp:152
void throw_or_abort(std::string const &err)