Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
aes128_constraint.test.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
8#include "acir_format.hpp"
14
15#include <cstdint>
16#include <gtest/gtest.h>
17#include <vector>
18
19using namespace acir_format;
20
21namespace {
23} // namespace
24
36template <typename Builder_, bool IsPlaintextConstant, bool IsKeyConstant, bool IsIVConstant>
38 public:
39 using Builder = Builder_;
41 using FF = typename Builder::FF;
42
44 public:
45 enum class Target : uint8_t {
46 None, // No invalidation - circuit should succeed
47 Plaintext, // Tamper with plaintext (only valid when plaintext is witness)
48 Key, // Tamper with encryption key (only valid when key is witness)
49 IV, // Tamper with initialization vector (only valid when IV is witness)
50 Output, // Tamper with expected output
51 };
52
54 {
56 if constexpr (!IsPlaintextConstant) {
57 targets.push_back(Target::Plaintext);
58 }
59 if constexpr (!IsKeyConstant) {
60 targets.push_back(Target::Key);
61 }
62 if constexpr (!IsIVConstant) {
63 targets.push_back(Target::IV);
64 }
65 targets.push_back(Target::Output);
66 return targets;
67 }
68
69 static std::vector<std::string> get_labels()
70 {
71 std::vector<std::string> labels = { "None" };
72 if constexpr (!IsPlaintextConstant) {
73 labels.push_back("Plaintext");
74 }
75 if constexpr (!IsKeyConstant) {
76 labels.push_back("Key");
77 }
78 if constexpr (!IsIVConstant) {
79 labels.push_back("IV");
80 }
81 labels.push_back("Output");
82 return labels;
83 }
84 };
85
87
97 static void generate_constraints(AcirConstraint& constraint, WitnessVector& witness_values)
98 {
99 // Generate random plaintext with 1-3 full blocks (16, 32, or 48 bytes)
100 size_t num_blocks = 1 + (engine.get_random_uint32() % 3);
101 size_t plaintext_size = num_blocks * 16;
102
103 std::vector<uint8_t> plaintext(plaintext_size);
104 for (auto& byte : plaintext) {
105 byte = static_cast<uint8_t>(engine.get_random_uint32() & 0xFF);
106 }
107
108 // Generate random key (16 bytes)
109 std::array<uint8_t, 16> key{};
110 for (auto& byte : key) {
111 byte = static_cast<uint8_t>(engine.get_random_uint32() & 0xFF);
112 }
113
114 // Generate random IV (16 bytes)
115 std::array<uint8_t, 16> iv{};
116 for (auto& byte : iv) {
117 byte = static_cast<uint8_t>(engine.get_random_uint32() & 0xFF);
118 }
119
120 // Compute the expected ciphertext using native AES-128-CBC (no padding for full blocks)
121 std::vector<uint8_t> ciphertext = native_aes128_cbc_encrypt(plaintext, key, iv);
122
123 // Lambda to create WitnessOrConstant based on template param
124 auto make_witness_or_constant = [&witness_values](FF value, bool is_constant) -> WitnessOrConstant<FF> {
125 if (is_constant) {
127 }
128 uint32_t witness_idx = add_to_witness_and_track_indices(witness_values, value);
129 return WitnessOrConstant<FF>::from_index(witness_idx);
130 };
131
132 // Add plaintext bytes to constraint
133 std::vector<WitnessOrConstant<FF>> input_witnesses;
134 for (const auto& byte : plaintext) {
135 input_witnesses.push_back(make_witness_or_constant(FF(byte), IsPlaintextConstant));
136 }
137
138 // Add key bytes to constraint
139 std::array<WitnessOrConstant<FF>, 16> key_witnesses{};
140 for (size_t i = 0; i < 16; ++i) {
141 key_witnesses[i] = make_witness_or_constant(FF(key[i]), IsKeyConstant);
142 }
143
144 // Add IV bytes to constraint
145 std::array<WitnessOrConstant<FF>, 16> iv_witnesses{};
146 for (size_t i = 0; i < 16; ++i) {
147 iv_witnesses[i] = make_witness_or_constant(FF(iv[i]), IsIVConstant);
148 }
149
150 // Add output (ciphertext) bytes to witness (always witness)
151 std::vector<uint32_t> output_indices;
152 for (const auto& byte : ciphertext) {
153 uint32_t witness_idx = add_to_witness_and_track_indices(witness_values, FF(byte));
154 output_indices.push_back(witness_idx);
155 }
156
157 // Build the constraint
158 constraint = AES128Constraint{
159 .inputs = std::move(input_witnesses),
160 .iv = iv_witnesses,
161 .key = key_witnesses,
162 .outputs = std::move(output_indices),
163 };
164 }
165
170 WitnessVector witness_values,
171 const typename InvalidWitness::Target& target)
172 {
173 switch (target) {
175 // No tampering
176 break;
177
179 // Tamper with the first plaintext byte
180 if constexpr (IsPlaintextConstant) {
181 if (!constraint.inputs.empty()) {
182 constraint.inputs[0] = WitnessOrConstant<FF>::from_constant(constraint.inputs[0].value + FF(1));
183 }
184 } else {
185 if (!constraint.inputs.empty()) {
186 witness_values[constraint.inputs[0].index] += FF(1);
187 }
188 }
189 break;
190
192 // Tamper with the first key byte
193 if constexpr (IsKeyConstant) {
194 constraint.key[0] = WitnessOrConstant<FF>::from_constant(constraint.key[0].value + FF(1));
195 } else {
196 witness_values[constraint.key[0].index] += FF(1);
197 }
198 break;
199
201 // Tamper with the first IV byte
202 if constexpr (IsIVConstant) {
203 constraint.iv[0] = WitnessOrConstant<FF>::from_constant(constraint.iv[0].value + FF(1));
204 } else {
205 witness_values[constraint.iv[0].index] += FF(1);
206 }
207 break;
208
210 // Tamper with the first output byte
211 if (!constraint.outputs.empty()) {
212 witness_values[constraint.outputs[0]] += FF(1);
213 }
214 break;
215 }
216
217 return { constraint, witness_values };
218 }
219
220 private:
227 static std::vector<uint8_t> native_aes128_cbc_encrypt(const std::vector<uint8_t>& plaintext,
228 const std::array<uint8_t, 16>& key,
229 const std::array<uint8_t, 16>& iv)
230 {
231 // Copy plaintext to output buffer (no padding for full blocks)
232 std::vector<uint8_t> buffer = plaintext;
233
234 // Create mutable copy of IV (the native function modifies it)
235 std::array<uint8_t, 16> iv_copy = iv;
236
237 // Encrypt in-place using the native crypto implementation
238 crypto::aes128_encrypt_buffer_cbc(buffer.data(), iv_copy.data(), key.data(), buffer.size());
239
240 return buffer;
241 }
242};
243
244using BuilderTypes = testing::Types<UltraCircuitBuilder, MegaCircuitBuilder>;
245
246// =============================================================================
247// Test Configuration 1: All witnesses (plaintext, key, and IV are witnesses)
248// =============================================================================
249template <typename Builder>
250class AES128TestAllWitness : public ::testing::Test,
251 public TestClass<AES128TestingFunctions<Builder, false, false, false>> {
252 protected:
254};
255
257
258TYPED_TEST(AES128TestAllWitness, GenerateVKFromConstraints)
259{
261 TestFixture::template test_vk_independence<Flavor>();
262}
263
265{
266 TestFixture::test_tampering();
267}
268// =============================================================================
269// Test Configuration 2: Constant plaintext (key and IV are witnesses)
270// =============================================================================
271template <typename Builder>
272class AES128TestConstantPlaintext : public ::testing::Test,
273 public TestClass<AES128TestingFunctions<Builder, true, false, false>> {
274 protected:
276};
277
279
280TYPED_TEST(AES128TestConstantPlaintext, GenerateVKFromConstraints)
281{
283 TestFixture::template test_vk_independence<Flavor>();
284}
285
287{
288 TestFixture::test_tampering();
289}
290
291// =============================================================================
292// Test Configuration 3: Constant key (plaintext and IV are witnesses)
293// =============================================================================
294template <typename Builder>
295class AES128TestConstantKey : public ::testing::Test,
296 public TestClass<AES128TestingFunctions<Builder, false, true, false>> {
297 protected:
299};
300
302
303TYPED_TEST(AES128TestConstantKey, GenerateVKFromConstraints)
304{
306 TestFixture::template test_vk_independence<Flavor>();
307}
308
310{
311 TestFixture::test_tampering();
312}
313
314// =============================================================================
315// Test Configuration 4: Constant IV (plaintext and key are witnesses)
316// =============================================================================
317template <typename Builder>
318class AES128TestConstantIV : public ::testing::Test,
319 public TestClass<AES128TestingFunctions<Builder, false, false, true>> {
320 protected:
322};
323
325
326TYPED_TEST(AES128TestConstantIV, GenerateVKFromConstraints)
327{
329 TestFixture::template test_vk_independence<Flavor>();
330}
331
333{
334 TestFixture::test_tampering();
335}
336
337// =============================================================================
338// Test Configuration 5: All constants (plaintext, key, and IV are constants)
339// =============================================================================
340template <typename Builder>
341class AES128TestAllConstant : public ::testing::Test,
342 public TestClass<AES128TestingFunctions<Builder, true, true, true>> {
343 protected:
345};
346
348
349TYPED_TEST(AES128TestAllConstant, GenerateVKFromConstraints)
350{
352 TestFixture::template test_vk_independence<Flavor>();
353}
354
356{
357 TestFixture::test_tampering();
358}
359
360// =============================================================================
361// Range Constraint Regression Tests
362// =============================================================================
363// These tests verify that the AES128 constraint properly enforces that all byte
364// values (input, key, IV, output) are in the range [0, 255]. Values outside this
365// range should cause circuit verification to fail.
366// =============================================================================
367
368class AES128RangeConstraintTest : public ::testing::Test {
369 protected:
371
374
375 // Valid test vectors for AES-128-CBC (16 bytes = 1 block)
376 static constexpr std::array<FF, 16> valid_plaintext = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
377 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a };
378
379 static constexpr std::array<FF, 16> valid_key = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
380 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
381
382 static constexpr std::array<FF, 16> valid_iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
383 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
384
388 static std::array<FF, 16> compute_ciphertext()
389 {
390 std::vector<uint8_t> buffer(16);
391 std::array<uint8_t, 16> key_bytes{};
392 std::array<uint8_t, 16> iv_bytes{};
393
394 for (size_t i = 0; i < 16; ++i) {
395 buffer[i] = static_cast<uint8_t>(uint256_t(valid_plaintext[i]));
396 key_bytes[i] = static_cast<uint8_t>(uint256_t(valid_key[i]));
397 iv_bytes[i] = static_cast<uint8_t>(uint256_t(valid_iv[i]));
398 }
399
400 crypto::aes128_encrypt_buffer_cbc(buffer.data(), iv_bytes.data(), key_bytes.data(), buffer.size());
401
402 std::array<FF, 16> result{};
403 for (size_t i = 0; i < 16; ++i) {
404 result[i] = FF(buffer[i]);
405 }
406 return result;
407 }
408
417 static std::pair<Builder, AES128Constraint> create_constraint(const std::array<FF, 16>& plaintext_vals,
418 const std::array<FF, 16>& key_vals,
419 const std::array<FF, 16>& iv_vals,
420 const std::array<FF, 16>& output_vals)
421 {
423
424 auto add_witness = [&builder](FF value) -> uint32_t { return builder.add_variable(value); };
425
426 // Add plaintext witnesses
427 std::vector<WitnessOrConstant<FF>> input_witnesses;
428 for (const auto& val : plaintext_vals) {
429 input_witnesses.push_back(WitnessOrConstant<FF>::from_index(add_witness(val)));
430 }
431
432 // Add key witnesses
433 std::array<WitnessOrConstant<FF>, 16> key_witnesses{};
434 for (size_t i = 0; i < 16; ++i) {
435 key_witnesses[i] = WitnessOrConstant<FF>::from_index(add_witness(key_vals[i]));
436 }
437
438 // Add IV witnesses
439 std::array<WitnessOrConstant<FF>, 16> iv_witnesses{};
440 for (size_t i = 0; i < 16; ++i) {
441 iv_witnesses[i] = WitnessOrConstant<FF>::from_index(add_witness(iv_vals[i]));
442 }
443
444 // Add output witnesses
445 std::vector<uint32_t> output_indices;
446 for (const auto& val : output_vals) {
447 output_indices.push_back(add_witness(val));
448 }
449
450 AES128Constraint constraint{
451 .inputs = std::move(input_witnesses),
452 .iv = iv_witnesses,
453 .key = key_witnesses,
454 .outputs = std::move(output_indices),
455 };
456
457 return { std::move(builder), std::move(constraint) };
458 }
459
467 {
468 // Don't catch exceptions - we want to verify CIRCUIT constraints catch the issue,
469 // not native checks that could be bypassed by a malicious prover
472 }
473};
474
488TEST_F(AES128RangeConstraintTest, PlaintextOutOfRangeFails)
489{
490 // The "overflowed" plaintext that AES would actually see after slicing
491 // attacker [..., 0, 256] becomes [..., 1, 0] when packed (256) and sliced
492 std::array<FF, 16> overflowed_plaintext = {};
493 overflowed_plaintext[14] = FF(1); // Carry from position 15
494 overflowed_plaintext[15] = FF(0); // 256 % 256 = 0
495 // rest are 0
496
497 // Compute the ciphertext for the OVERFLOWED plaintext
498 std::vector<uint8_t> buffer(16, 0);
499 buffer[14] = 1;
500 buffer[15] = 0;
501 std::array<uint8_t, 16> key_bytes{};
502 std::array<uint8_t, 16> iv_bytes{};
503 for (size_t i = 0; i < 16; ++i) {
504 key_bytes[i] = static_cast<uint8_t>(uint256_t(valid_key[i]));
505 iv_bytes[i] = static_cast<uint8_t>(uint256_t(valid_iv[i]));
506 }
507 crypto::aes128_encrypt_buffer_cbc(buffer.data(), iv_bytes.data(), key_bytes.data(), buffer.size());
508
509 std::array<FF, 16> overflowed_ciphertext{};
510 for (size_t i = 0; i < 16; ++i) {
511 overflowed_ciphertext[i] = FF(buffer[i]);
512 }
513
514 // PART 1: Verify that [..., 1, 0] with matching ciphertext PASSES
515 // This proves the lookups work fine - there's no issue with the data itself
516 {
517 auto [builder, constraint] =
518 create_constraint(overflowed_plaintext, valid_key, valid_iv, overflowed_ciphertext);
520 EXPECT_TRUE(CircuitChecker::check(builder))
521 << "Sanity check: [..., 1, 0] with correct ciphertext should pass (lookups work)";
522 }
523
524 // PART 2: Verify that [..., 0, 256] FAILS due to range constraint
525 // The attacker's plaintext has 256 in LSB position, overflows to [..., 1, 0]
526 std::array<FF, 16> attacker_plaintext = {};
527 attacker_plaintext[15] = FF(256); // Out of range in LSB position!
528 // rest are 0
529
530 // Attacker provides the ciphertext that matches the overflowed interpretation
531 auto [builder, constraint] = create_constraint(attacker_plaintext, valid_key, valid_iv, overflowed_ciphertext);
533 EXPECT_TRUE(builder.failed()) << "Circuit should fail when plaintext has byte > 255";
534 EXPECT_FALSE(CircuitChecker::check(builder));
535}
536
544{
545 // The "overflowed" key that AES would see: [..., 1, 0]
546 std::array<FF, 16> overflowed_key = {};
547 overflowed_key[14] = FF(1); // Carry from position 15
548 overflowed_key[15] = FF(0); // 256 % 256 = 0
549
550 // Compute ciphertext with the overflowed key
551 std::vector<uint8_t> buffer(16);
552 std::array<uint8_t, 16> key_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 };
553 std::array<uint8_t, 16> iv_bytes{};
554 for (size_t i = 0; i < 16; ++i) {
555 buffer[i] = static_cast<uint8_t>(uint256_t(valid_plaintext[i]));
556 iv_bytes[i] = static_cast<uint8_t>(uint256_t(valid_iv[i]));
557 }
558 crypto::aes128_encrypt_buffer_cbc(buffer.data(), iv_bytes.data(), key_bytes.data(), buffer.size());
559
560 std::array<FF, 16> overflowed_ciphertext{};
561 for (size_t i = 0; i < 16; ++i) {
562 overflowed_ciphertext[i] = FF(buffer[i]);
563 }
564
565 // PART 1: Verify lookups work with valid key [..., 1, 0]
566 {
567 auto [builder, constraint] =
568 create_constraint(valid_plaintext, overflowed_key, valid_iv, overflowed_ciphertext);
570 EXPECT_TRUE(CircuitChecker::check(builder)) << "Sanity check: key [..., 1, 0] should pass (lookups work)";
571 }
572
573 // PART 2: Verify [..., 0, 256] key FAILS due to range constraint
574 std::array<FF, 16> attacker_key = {};
575 attacker_key[15] = FF(256); // Out of range in LSB position!
576
577 auto [builder, constraint] = create_constraint(valid_plaintext, attacker_key, valid_iv, overflowed_ciphertext);
579 EXPECT_TRUE(builder.failed()) << "Circuit should fail when key has byte > 255";
580 EXPECT_FALSE(CircuitChecker::check(builder));
581}
582
589{
590 // The "overflowed" IV that AES would see: [..., 1, 0]
591 std::array<FF, 16> overflowed_iv = {};
592 overflowed_iv[14] = FF(1); // Carry from position 15
593 overflowed_iv[15] = FF(0); // 256 % 256 = 0
594
595 // Compute ciphertext with the overflowed IV
596 std::vector<uint8_t> buffer(16);
597 std::array<uint8_t, 16> key_bytes{};
598 std::array<uint8_t, 16> iv_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 };
599 for (size_t i = 0; i < 16; ++i) {
600 buffer[i] = static_cast<uint8_t>(uint256_t(valid_plaintext[i]));
601 key_bytes[i] = static_cast<uint8_t>(uint256_t(valid_key[i]));
602 }
603 crypto::aes128_encrypt_buffer_cbc(buffer.data(), iv_bytes.data(), key_bytes.data(), buffer.size());
604
605 std::array<FF, 16> overflowed_ciphertext{};
606 for (size_t i = 0; i < 16; ++i) {
607 overflowed_ciphertext[i] = FF(buffer[i]);
608 }
609
610 // PART 1: Verify lookups work with valid IV [..., 1, 0]
611 {
612 auto [builder, constraint] =
613 create_constraint(valid_plaintext, valid_key, overflowed_iv, overflowed_ciphertext);
615 EXPECT_TRUE(CircuitChecker::check(builder)) << "Sanity check: IV [..., 1, 0] should pass (lookups work)";
616 }
617
618 // PART 2: Verify [..., 0, 256] IV FAILS due to range constraint
619 std::array<FF, 16> attacker_iv = {};
620 attacker_iv[15] = FF(256); // Out of range in LSB position!
621
622 auto [builder, constraint] = create_constraint(valid_plaintext, valid_key, attacker_iv, overflowed_ciphertext);
624 EXPECT_TRUE(builder.failed()) << "Circuit should fail when IV has byte > 255";
625 EXPECT_FALSE(CircuitChecker::check(builder));
626}
627
635TEST_F(AES128RangeConstraintTest, OutputOutOfRangeFails)
636{
637 // Compute the valid ciphertext
638 auto valid_ciphertext = compute_ciphertext();
639
640 // PART 1: Verify circuit passes with valid output
641 {
642 auto [builder, constraint] = create_constraint(valid_plaintext, valid_key, valid_iv, valid_ciphertext);
644 EXPECT_TRUE(CircuitChecker::check(builder)) << "Sanity check: valid ciphertext should pass";
645 }
646
647 // PART 2: Create attacker output that packs to the same value using LSB positions
648 // [..., X-1, Y+256] packs same as [..., X, Y] due to overflow
649 std::array<FF, 16> attacker_output = valid_ciphertext;
650 uint64_t second_last_byte = static_cast<uint64_t>(uint256_t(valid_ciphertext[14])); // X
651 uint64_t last_byte = static_cast<uint64_t>(uint256_t(valid_ciphertext[15])); // Y
652
653 // Need second_last_byte >= 1 to subtract 1 from it
654 ASSERT_GE(second_last_byte, 1u) << "Test requires ciphertext[14] >= 1";
655
656 attacker_output[14] = FF(second_last_byte - 1); // X - 1
657 attacker_output[15] = FF(last_byte + 256); // Y + 256 (out of range!)
658
659 auto [builder, constraint] = create_constraint(valid_plaintext, valid_key, valid_iv, attacker_output);
661 EXPECT_TRUE(builder.failed()) << "Circuit should fail when output has byte > 255";
662 EXPECT_FALSE(CircuitChecker::check(builder));
663}
static constexpr std::array< FF, 16 > valid_iv
static std::pair< Builder, AES128Constraint > create_constraint(const std::array< FF, 16 > &plaintext_vals, const std::array< FF, 16 > &key_vals, const std::array< FF, 16 > &iv_vals, const std::array< FF, 16 > &output_vals)
Build an AES128 constraint with specified values.
static constexpr std::array< FF, 16 > valid_plaintext
static std::array< FF, 16 > compute_ciphertext()
Compute valid ciphertext for the test vectors.
static bool circuit_rejects_bad_input(Builder &builder, AES128Constraint &constraint)
Helper to test that out-of-range bytes are rejected BY THE CIRCUIT, not by native checks.
static constexpr std::array< FF, 16 > valid_key
static std::vector< std::string > get_labels()
Testing functions to generate the AES128Test test suite.
static ProgramMetadata generate_metadata()
static std::vector< uint8_t > native_aes128_cbc_encrypt(const std::vector< uint8_t > &plaintext, const std::array< uint8_t, 16 > &key, const std::array< uint8_t, 16 > &iv)
Native AES-128-CBC encryption for generating expected outputs.
static void generate_constraints(AcirConstraint &constraint, WitnessVector &witness_values)
Generate valid AES128 encryption constraints with random inputs.
static std::pair< AcirConstraint, WitnessVector > invalidate_witness(AcirConstraint constraint, WitnessVector witness_values, const typename InvalidWitness::Target &target)
Invalidate witness values to test circuit failure detection.
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
typename ExecutionTrace::FF FF
virtual uint32_t get_random_uint32()=0
AluTraceBuilder builder
Definition alu.test.cpp:124
numeric::RNG & engine
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:50
std::vector< bb::fr > WitnessVector
std::vector< uint32_t > add_to_witness_and_track_indices(std::vector< bb::fr > &witness, const T &input)
Append values to a witness vector and track their indices.
Definition utils.hpp:90
void create_aes128_constraints(Builder &builder, const AES128Constraint &constraint)
AvmFlavorSettings::FF FF
Definition field.hpp:10
void aes128_encrypt_buffer_cbc(uint8_t *buffer, uint8_t *iv, const uint8_t *key, const size_t length)
Definition aes128.cpp:232
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:217
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
TEST_F(BoomerangGoblinRecursiveVerifierTests, graph_description_basic)
Construct and check a goblin recursive verification circuit.
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
::testing::Types< UltraCircuitBuilder, MegaCircuitBuilder > BuilderTypes
std::array< WitnessOrConstant< bb::fr >, 16 > iv
std::vector< uint32_t > outputs
std::vector< WitnessOrConstant< bb::fr > > inputs
std::array< WitnessOrConstant< bb::fr >, 16 > key
Metadata required to create a circuit.
static WitnessOrConstant from_index(uint32_t index)
static WitnessOrConstant from_constant(FF value)