Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
batched_honk_translator.test.cpp
Go to the documentation of this file.
1
16
25
26#include <gtest/gtest.h>
27
28using namespace bb;
29
30class BatchedHonkTranslatorTests : public ::testing::Test {
31 public:
33 using Fq = TranslatorFlavor::BF; // BN254 base field (= Grumpkin scalar field)
36
38
39 // -------------------------------------------------------------------------
40 // Translator helpers (adapted from translator.test.cpp)
41 // -------------------------------------------------------------------------
42
43 static void add_random_ops(std::shared_ptr<ECCOpQueue>& op_queue, size_t count)
44 {
45 for (size_t i = 0; i < count; i++) {
46 op_queue->random_op_ultra_only();
47 }
48 }
49
50 static void add_mixed_ops(std::shared_ptr<ECCOpQueue>& op_queue, size_t count = 100)
51 {
52 auto P1 = G1::random_element();
53 auto P2 = G1::random_element();
54 auto z = FF::random_element();
55 for (size_t i = 0; i < count; i++) {
56 op_queue->add_accumulate(P1);
57 op_queue->mul_accumulate(P2, z);
58 }
59 op_queue->eq_and_reset();
60 }
61
67 const Fq& evaluation_input_x,
68 size_t circuit_size_param = 500)
69 {
70 auto op_queue = std::make_shared<ECCOpQueue>();
71 op_queue->no_op_ultra_only();
73 add_mixed_ops(op_queue, circuit_size_param / 2);
74 op_queue->merge();
75 add_mixed_ops(op_queue, circuit_size_param / 2);
77 op_queue->merge(MergeSettings::APPEND, ECCOpQueue::OP_QUEUE_SIZE - op_queue->get_current_subtable_size());
78
79 TranslatorCircuitBuilder circuit(batching_challenge_v, evaluation_input_x, op_queue);
81 }
82
88 {
89 const size_t RESULT_ROW = TranslatorFlavor::RESULT_ROW;
90 auto& polys = key->proving_key->polynomials;
91 return Fq(uint256_t(polys.accumulators_binary_limbs_0[RESULT_ROW]) +
92 (uint256_t(polys.accumulators_binary_limbs_1[RESULT_ROW]) << 68) +
93 (uint256_t(polys.accumulators_binary_limbs_2[RESULT_ROW]) << 136) +
94 (uint256_t(polys.accumulators_binary_limbs_3[RESULT_ROW]) << 204));
95 }
96
119 static TranscriptManifest build_expected_batched_manifest(const size_t mega_zk_log_n,
120 const size_t num_mega_zk_pub_inputs)
121 {
122 using MZK = MegaZKFlavor;
123 using Trans = TranslatorFlavor;
124
125 constexpr size_t G = FrCodec::calc_num_fields<MZK::Commitment>(); // 4
126 constexpr size_t Fr = 1;
127 constexpr size_t JOINT_LOG_N = Trans::CONST_TRANSLATOR_LOG_N; // 17
128 constexpr size_t LOG_MINI = Trans::LOG_MINI_CIRCUIT_SIZE; // 13
129
130 // Joint univariate size = max batched partial length across both circuits.
131 constexpr size_t UNI = Trans::BATCHED_RELATION_PARTIAL_LENGTH; // 9
132
134 size_t round = 0;
135
136 // ── Round 0: MegaZK Oink ──────────────────────────────────────────────────
137 m.add_entry(round, "vk_hash", Fr);
138 for (size_t i = 0; i < num_mega_zk_pub_inputs; ++i) {
139 m.add_entry(round, "public_input_" + std::to_string(i), Fr);
140 }
141 m.add_entry(round, "Gemini:masking_poly_comm", G);
142 m.add_entry(round, "W_L", G);
143 m.add_entry(round, "W_R", G);
144 m.add_entry(round, "W_O", G);
145 for (size_t i = 1; i <= 4; ++i) {
146 m.add_entry(round, "ECC_OP_WIRE_" + std::to_string(i), G);
147 }
148 // DataBus entities: calldata, calldata_read_counts, calldata_read_tags,
149 // secondary_calldata, secondary_calldata_read_counts, secondary_calldata_read_tags,
150 // return_data, return_data_read_counts, return_data_read_tags
151 for (const auto& label : { "CALLDATA",
152 "CALLDATA_READ_COUNTS",
153 "CALLDATA_READ_TAGS",
154 "SECONDARY_CALLDATA",
155 "SECONDARY_CALLDATA_READ_COUNTS",
156 "SECONDARY_CALLDATA_READ_TAGS",
157 "RETURN_DATA",
158 "RETURN_DATA_READ_COUNTS",
159 "RETURN_DATA_READ_TAGS" }) {
160 m.add_entry(round, label, G);
161 }
162 m.add_challenge(round, "eta");
163 round++;
164
165 // ── Round 1: MegaZK lookup counts/tags/W_4 ───────────────────────────────
166 m.add_entry(round, "LOOKUP_READ_COUNTS", G);
167 m.add_entry(round, "LOOKUP_READ_TAGS", G);
168 m.add_entry(round, "W_4", G);
169 m.add_challenge(round, std::array<std::string, 2>{ "beta", "gamma" });
170 round++;
171
172 // ── Round 2: MegaZK logderiv inverses + Z_PERM + translator Oink ─────────
173 m.add_entry(round, "LOOKUP_INVERSES", G);
174 m.add_entry(round, "CALLDATA_INVERSES", G);
175 m.add_entry(round, "SECONDARY_CALLDATA_INVERSES", G);
176 m.add_entry(round, "RETURN_DATA_INVERSES", G);
177 m.add_entry(round, "Z_PERM", G);
178 // Translator Oink: vk_hash, masking commitment, 10 wire commitments
179 m.add_entry(round, "vk_hash", Fr);
180 m.add_entry(round, "Gemini:masking_poly_comm", G);
181 for (size_t i = 0; i < 4; ++i) {
182 m.add_entry(round, "CONCATENATED_RANGE_CONSTRAINTS_" + std::to_string(i), G);
183 }
184 m.add_entry(round, "CONCATENATED_NON_RANGE", G);
185 for (size_t i = 0; i < 5; ++i) {
186 m.add_entry(round, "ORDERED_RANGE_CONSTRAINTS_" + std::to_string(i), G);
187 }
188 m.add_challenge(round, std::array<std::string, 2>{ "beta", "gamma" });
189 round++;
190
191 // ── Round 3: translator Z_PERM, then joint alpha + gate challenges ────────
192 m.add_entry(round, "Z_PERM", G);
193 m.add_challenge(round, "Sumcheck:alpha");
194 for (size_t i = 0; i < JOINT_LOG_N; ++i) {
195 m.add_challenge(round, "Sumcheck:gate_challenge_" + std::to_string(i));
196 }
197 round++;
198
199 // ── Round 4: Libra masking commitment + Sum ───────────────────────────────
200 m.add_entry(round, "Libra:concatenation_commitment", G);
201 m.add_entry(round, "Libra:Sum", Fr);
202 m.add_challenge(round, "Libra:Challenge");
203 round++;
204
205 // ── Rounds 5..4+JOINT_LOG_N: joint sumcheck ──────────────────────────────
206 // Loop runs one extra iteration (i == JOINT_LOG_N) to place MegaZK evaluations in the
207 // post-sumcheck round when mega_zk_log_n == JOINT_LOG_N (no virtual rounds).
208 for (size_t i = 0; i <= JOINT_LOG_N; ++i) {
209 // MegaZK evaluations are sent immediately after the real rounds, before the first virtual
210 // round's univariate. In the transcript manifest they appear at the start of the round
211 // that contains univariate_{mega_zk_log_n} (or, when mega_zk_log_n == JOINT_LOG_N, in
212 // the post-sumcheck round before evaluations_translator).
213 if (i == mega_zk_log_n) {
214 m.add_entry(round, "Sumcheck:evaluations", MZK::NUM_ALL_ENTITIES);
215 }
216 if (i == JOINT_LOG_N) {
217 break; // No univariate/challenge for the extra iteration
218 }
219 // Translator mini-circuit evaluations are sent after round LOG_MINI_CIRCUIT_SIZE-1
220 // and appear before univariate_{LOG_MINI} in the manifest.
221 if (i == LOG_MINI) {
222 m.add_entry(round, "Sumcheck:minicircuit_evaluations", Trans::NUM_MINICIRCUIT_EVALUATIONS);
223 }
224 m.add_entry(round, "Sumcheck:univariate_" + std::to_string(i), UNI);
225 m.add_challenge(round, "Sumcheck:u_" + std::to_string(i));
226 round++;
227 }
228
229 // ── Post-sumcheck evaluations ─────────────────────────────────────────────
230 m.add_entry(round, "Sumcheck:evaluations_translator", Trans::NUM_FULL_CIRCUIT_EVALUATIONS);
231 m.add_entry(round, "Libra:claimed_evaluation", Fr);
232 m.add_entry(round, "Libra:grand_sum_commitment", G);
233 m.add_entry(round, "Libra:quotient_commitment", G);
234 m.add_challenge(round, "rho");
235 round++;
236
237 // ── Gemini fold commitments ───────────────────────────────────────────────
238 for (size_t i = 1; i < JOINT_LOG_N; ++i) {
239 m.add_entry(round, "Gemini:FOLD_" + std::to_string(i), G);
240 }
241 m.add_challenge(round, "Gemini:r");
242 round++;
243
244 // ── Gemini evaluations + Libra evals ─────────────────────────────────────
245 for (size_t i = 1; i <= JOINT_LOG_N; ++i) {
246 m.add_entry(round, "Gemini:a_" + std::to_string(i), Fr);
247 }
248 m.add_entry(round, "Libra:concatenation_eval", Fr);
249 m.add_entry(round, "Libra:shifted_grand_sum_eval", Fr);
250 m.add_entry(round, "Libra:grand_sum_eval", Fr);
251 m.add_entry(round, "Libra:quotient_eval", Fr);
252 m.add_challenge(round, "Shplonk:nu");
253 round++;
254
255 // ── Shplonk:Q ────────────────────────────────────────────────────────────
256 m.add_entry(round, "Shplonk:Q", G);
257 m.add_challenge(round, "Shplonk:z");
258 round++;
259
260 // ── KZG opening ──────────────────────────────────────────────────────────
261 m.add_entry(round, "KZG:W", G);
262
263 return m;
264 }
265
272 {
273 auto& ck = key->proving_key->commitment_key;
274 auto& polys = key->proving_key->polynomials;
275 return {
276 ck.commit(polys.op), ck.commit(polys.x_lo_y_hi), ck.commit(polys.x_hi_z_1), ck.commit(polys.y_lo_z_2)
277 };
278 }
279};
280
281// =============================================================================
282// BatchedHonkTranslatorTests::ProveAndVerify
283// =============================================================================
285{
287 using MegaZKProverInst = ProverInstance_<MegaZKFlavor>;
288 using MegaZKVK = MegaZKFlavor::VerificationKey;
289 using MegaZKVKAndHash = MegaZKFlavor::VKAndHash;
290
291 // -------------------------------------------------------------------------
292 // 1. Translator inputs (random translation challenges — no ECCVM needed).
293 // -------------------------------------------------------------------------
294 const Fq batching_challenge_v = Fq::random_element();
295 const Fq evaluation_input_x = Fq::random_element();
296
297 auto translator_key = build_translator_key(batching_challenge_v, evaluation_input_x);
298
299 // Initialise the translator commitment key (normally done by TranslatorProver ctor).
300 {
301 auto tmp = std::make_shared<Transcript>();
302 TranslatorProver init_prover(translator_key, tmp); // side-effect: initialises commitment_key
303 }
304 const Fq accumulated_result = get_accumulated_result(translator_key);
305 const auto op_queue_wire_commitments = commit_op_queue_wires(translator_key);
306
307 // -------------------------------------------------------------------------
308 // 2. Hiding kernel inputs: pad to JOINT_LOG_N = 17 so hiding_log_n == JOINT_LOG_N.
309 // -------------------------------------------------------------------------
310 MegaCircuitBuilder mega_zk_circuit;
312 // Pad so that hiding_log_n == JOINT_LOG_N. We aim for JOINT_LOG_N-1 as the arithmetic
313 // target because MegaCircuitBuilder's execution-trace overhead grows the dyadic size by one.
314 static constexpr size_t JOINT_LOG_N = BatchedHonkTranslatorProver::JOINT_LOG_N;
315 MockCircuits::construct_arithmetic_circuit(mega_zk_circuit, JOINT_LOG_N - 1, /*include_public_inputs=*/false);
316
317 auto mega_zk_inst = std::make_shared<MegaZKProverInst>(mega_zk_circuit);
318 auto mega_zk_vk = std::make_shared<MegaZKVK>(mega_zk_inst->get_precomputed());
319 auto mega_zk_vk_and_hash = std::make_shared<MegaZKVKAndHash>(mega_zk_vk);
320
321 // -------------------------------------------------------------------------
322 // 3. Prove.
323 // -------------------------------------------------------------------------
324 auto prover_transcript = std::make_shared<Transcript>();
325 BatchedHonkTranslatorProver prover(mega_zk_inst, mega_zk_vk, prover_transcript);
326
327 auto mega_zk_proof = prover.prove_mega_zk_oink();
328 auto joint_proof = prover.prove(translator_key);
329
330 // -------------------------------------------------------------------------
331 // 4. Verify.
332 // -------------------------------------------------------------------------
333 auto verifier_transcript = std::make_shared<Transcript>();
334 BatchedHonkTranslatorVerifier verifier(mega_zk_vk_and_hash, verifier_transcript);
335 verifier.verify_mega_zk_oink(mega_zk_proof);
336 auto result = verifier.verify(
337 joint_proof, evaluation_input_x, batching_challenge_v, accumulated_result, op_queue_wire_commitments);
338
339 EXPECT_TRUE(result.reduction_succeeded);
340 EXPECT_TRUE(result.pairing_points.check());
341}
342
343// =============================================================================
344// BatchedHonkTranslatorTests::VerifierManifestConsistency
345// Checks that the prover and verifier Fiat-Shamir transcripts produce the same
346// manifest (same sequence of send/receive/challenge entries), which pins the
347// joint proof structure and detects any prover/verifier protocol divergence.
348// =============================================================================
349TEST_F(BatchedHonkTranslatorTests, VerifierManifestConsistency)
350{
351 using MegaZKProverInst = ProverInstance_<MegaZKFlavor>;
352 using MegaZKVK = MegaZKFlavor::VerificationKey;
353 using MegaZKVKAndHash = MegaZKFlavor::VKAndHash;
354
355 const Fq batching_challenge_v = Fq::random_element();
356 const Fq evaluation_input_x = Fq::random_element();
357
358 auto translator_key = build_translator_key(batching_challenge_v, evaluation_input_x);
359 {
360 auto tmp = std::make_shared<Transcript>();
361 TranslatorProver init_prover(translator_key, tmp);
362 }
363 const Fq accumulated_result = get_accumulated_result(translator_key);
364 const auto op_queue_wire_commitments = commit_op_queue_wires(translator_key);
365
366 MegaCircuitBuilder mega_zk_circuit;
368
369 auto mega_zk_inst = std::make_shared<MegaZKProverInst>(mega_zk_circuit);
370 auto mega_zk_vk = std::make_shared<MegaZKVK>(mega_zk_inst->get_precomputed());
371 auto mega_zk_vk_and_hash = std::make_shared<MegaZKVKAndHash>(mega_zk_vk);
372
373 // Prove with manifest tracking enabled.
374 auto prover_transcript = std::make_shared<Transcript>();
375 prover_transcript->enable_manifest();
376 BatchedHonkTranslatorProver prover(mega_zk_inst, mega_zk_vk, prover_transcript);
377 auto mega_zk_proof = prover.prove_mega_zk_oink();
378 auto joint_proof = prover.prove(translator_key);
379
380 // Verify with manifest tracking enabled.
381 auto verifier_transcript = std::make_shared<Transcript>();
382 verifier_transcript->enable_manifest();
383 BatchedHonkTranslatorVerifier verifier(mega_zk_vk_and_hash, verifier_transcript);
384 verifier.verify_mega_zk_oink(mega_zk_proof);
385 [[maybe_unused]] auto _ = verifier.verify(
386 joint_proof, evaluation_input_x, batching_challenge_v, accumulated_result, op_queue_wire_commitments);
387
388 auto prover_manifest = prover_transcript->get_manifest();
389 auto verifier_manifest = verifier_transcript->get_manifest();
390
391 ASSERT_GT(prover_manifest.size(), 0);
392 for (size_t round = 0; round < prover_manifest.size(); ++round) {
393 ASSERT_EQ(prover_manifest[round], verifier_manifest[round])
394 << "Prover/verifier manifest discrepancy in round " << round;
395 }
396}
397
398// =============================================================================
399// BatchedHonkTranslatorTests::ProverManifestConsistency
400// Pins the joint transcript structure by comparing the prover manifest against
401// a hard-coded expected manifest built from flavor constants. Detects any
402// structural change in the Fiat-Shamir transcript ordering.
403// =============================================================================
404TEST_F(BatchedHonkTranslatorTests, ProverManifestConsistency)
405{
406 using MegaZKProverInst = ProverInstance_<MegaZKFlavor>;
407 using MegaZKVK = MegaZKFlavor::VerificationKey;
408
409 const Fq batching_challenge_v = Fq::random_element();
410 const Fq evaluation_input_x = Fq::random_element();
411
412 auto translator_key = build_translator_key(batching_challenge_v, evaluation_input_x);
413 {
414 auto tmp = std::make_shared<Transcript>();
415 TranslatorProver init_prover(translator_key, tmp);
416 }
417
418 MegaCircuitBuilder mega_zk_circuit;
420 auto mega_zk_inst = std::make_shared<MegaZKProverInst>(mega_zk_circuit);
421 auto mega_zk_vk = std::make_shared<MegaZKVK>(mega_zk_inst->get_precomputed());
422
423 const size_t mega_zk_log_n = mega_zk_inst->log_dyadic_size();
424 const size_t num_mega_zk_pub_inputs = mega_zk_inst->num_public_inputs();
425
426 // Prove with manifest tracking enabled.
427 auto prover_transcript = std::make_shared<Transcript>();
428 prover_transcript->enable_manifest();
429 BatchedHonkTranslatorProver prover(mega_zk_inst, mega_zk_vk, prover_transcript);
430 [[maybe_unused]] auto _ = prover.prove_mega_zk_oink();
431 [[maybe_unused]] auto __ = prover.prove(translator_key);
432
433 auto prover_manifest = prover_transcript->get_manifest();
434 auto expected_manifest = build_expected_batched_manifest(mega_zk_log_n, num_mega_zk_pub_inputs);
435
436 ASSERT_EQ(prover_manifest.size(), expected_manifest.size()) << "Manifest round count mismatch";
437 for (size_t round = 0; round < expected_manifest.size(); ++round) {
438 ASSERT_EQ(prover_manifest[round], expected_manifest[round]) << "Manifest discrepancy in round " << round;
439 }
440}
441
442// =============================================================================
443// BatchedHonkTranslatorTests::ProveAndVerifySmallHiding
444// Tests the variable circuit size path: hiding_log_n < JOINT_LOG_N.
445// =============================================================================
446TEST_F(BatchedHonkTranslatorTests, ProveAndVerifySmallHiding)
447{
449 using MegaZKProverInst = ProverInstance_<MegaZKFlavor>;
450 using MegaZKVK = MegaZKFlavor::VerificationKey;
451 using MegaZKVKAndHash = MegaZKFlavor::VKAndHash;
452
453 const Fq batching_challenge_v = Fq::random_element();
454 const Fq evaluation_input_x = Fq::random_element();
455
456 auto translator_key = build_translator_key(batching_challenge_v, evaluation_input_x);
457 {
458 auto tmp = std::make_shared<Transcript>();
459 TranslatorProver init_prover(translator_key, tmp);
460 }
461 const Fq accumulated_result = get_accumulated_result(translator_key);
462 const auto op_queue_wire_commitments = commit_op_queue_wires(translator_key);
463
464 // Use a small hiding circuit — NOT padded to JOINT_LOG_N.
465 // hiding_log_n will be well below 17, exercising the variable-size code paths.
466 MegaCircuitBuilder mega_zk_circuit;
468
469 auto mega_zk_inst = std::make_shared<MegaZKProverInst>(mega_zk_circuit);
470 auto mega_zk_vk = std::make_shared<MegaZKVK>(mega_zk_inst->get_precomputed());
471 auto mega_zk_vk_and_hash = std::make_shared<MegaZKVKAndHash>(mega_zk_vk);
472
473 auto prover_transcript = std::make_shared<Transcript>();
474 BatchedHonkTranslatorProver prover(mega_zk_inst, mega_zk_vk, prover_transcript);
475 auto mega_zk_proof = prover.prove_mega_zk_oink();
476 auto joint_proof = prover.prove(translator_key);
477
478 auto verifier_transcript = std::make_shared<Transcript>();
479 BatchedHonkTranslatorVerifier verifier(mega_zk_vk_and_hash, verifier_transcript);
480 verifier.verify_mega_zk_oink(mega_zk_proof);
481 auto result = verifier.verify(
482 joint_proof, evaluation_input_x, batching_challenge_v, accumulated_result, op_queue_wire_commitments);
483
484 EXPECT_TRUE(result.reduction_succeeded);
485 EXPECT_TRUE(result.pairing_points.check());
486}
TEST_F(BatchedHonkTranslatorTests, ProveAndVerify)
static std::array< TranslatorFlavor::Commitment, TranslatorFlavor::NUM_OP_QUEUE_WIRES > commit_op_queue_wires(const std::shared_ptr< TranslatorProvingKey > &key)
Commit to the four op-queue wire polynomials.
static void add_mixed_ops(std::shared_ptr< ECCOpQueue > &op_queue, size_t count=100)
static TranscriptManifest build_expected_batched_manifest(const size_t mega_zk_log_n, const size_t num_mega_zk_pub_inputs)
Build the expected transcript manifest for a BatchedHonkTranslator proof.
static Fq get_accumulated_result(const std::shared_ptr< TranslatorProvingKey > &key)
Read accumulated_result from the translator witness polynomials.
static std::shared_ptr< TranslatorProvingKey > build_translator_key(const Fq &batching_challenge_v, const Fq &evaluation_input_x, size_t circuit_size_param=500)
Build a translator circuit on a fresh op queue with random challenges.
static void add_random_ops(std::shared_ptr< ECCOpQueue > &op_queue, size_t count)
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
Prover for the batched MegaZK circuit + translator sumcheck and PCS.
HonkProof prove(std::shared_ptr< TranslatorProvingKey > translator_proving_key)
Verifier for the batched MegaZK circuit + translator sumcheck and PCS.
ReductionResult verify(const Proof &joint_proof, const TransBF &evaluation_input_x, const TransBF &batching_challenge_v, const TransBF &accumulated_result, const std::array< Commitment, TranslatorFlavor::NUM_OP_QUEUE_WIRES > &op_queue_wire_commitments)
Phase 2: Verify translator Oink + joint sumcheck + joint PCS.
OinkResult verify_mega_zk_oink(const Proof &mega_zk_proof)
Phase 1: Verify the MegaZK Oink phase on the shared transcript.
static const size_t OP_QUEUE_SIZE
static void construct_simple_circuit(MegaBuilder &builder)
Generate a simple test circuit with some ECC op gates and conventional arithmetic gates.
Child class of MegaFlavor that runs with ZK Sumcheck.
MegaFlavor::VKAndHash VKAndHash
static void construct_arithmetic_circuit(Builder &builder, const size_t target_log2_dyadic_size=4, bool include_public_inputs=true)
Populate a builder with a specified number of arithmetic gates; includes a PI.
Base Native verification key class.
Definition flavor.hpp:135
Contains all the information required by a Honk prover to create a proof, constructed from a finalize...
void add_entry(size_t round, const std::string &element_label, size_t element_size)
void add_challenge(size_t round, const std::string &label)
Add a single challenge label to the manifest for the given round.
TranslatorCircuitBuilder creates a circuit that evaluates the correctness of the evaluation of EccOpQ...
Curve::ScalarField FF
Curve::AffineElement Commitment
static constexpr size_t RESULT_ROW
#define G(r, i, a, b, c, d)
Definition blake2s.cpp:116
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
BaseTranscript< FrCodec, bb::crypto::Poseidon2< bb::crypto::Poseidon2Bn254ScalarFieldParams > > NativeTranscript
CommitmentKey< Curve > ck
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
Curve::ScalarField Fr
static field random_element(numeric::RNG *engine=nullptr) noexcept