Skip to content

Commit

Permalink
feat: Add support for unlimited width in ACIR (#8960)
Browse files Browse the repository at this point in the history
Handle ACIR AssertZero opcodes of any width, by creating intermediate
quad gates using w4_omega.
'fixes' noir-lang/noir#6085, but we still need
to have Noir using the unlimited width.
  • Loading branch information
guipublic authored Oct 3, 2024
1 parent 830c6ab commit 3e05e22
Show file tree
Hide file tree
Showing 16 changed files with 414 additions and 12 deletions.
33 changes: 31 additions & 2 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,33 @@ void build_constraints(Builder& builder,
gate_counter.track_diff(constraint_system.gates_per_opcode,
constraint_system.original_opcode_indices.quad_constraints.at(i));
}
// Oversize gates are a vector of mul_quad gates.
for (size_t i = 0; i < constraint_system.big_quad_constraints.size(); ++i) {
auto& big_constraint = constraint_system.big_quad_constraints.at(i);
fr next_w4_wire_value = fr(0);
// Define the 4th wire of these mul_quad gates, which is implicitly used by the previous gate.
for (size_t j = 0; j < big_constraint.size() - 1; ++j) {
if (j == 0) {
next_w4_wire_value = builder.get_variable(big_constraint[0].d);
} else {
uint32_t next_w4_wire = builder.add_variable(next_w4_wire_value);
big_constraint[j].d = next_w4_wire;
big_constraint[j].d_scaling = fr(-1);
}
builder.create_big_mul_add_gate(big_constraint[j], true);
next_w4_wire_value = builder.get_variable(big_constraint[j].a) * builder.get_variable(big_constraint[j].b) *
big_constraint[j].mul_scaling +
builder.get_variable(big_constraint[j].a) * big_constraint[j].a_scaling +
builder.get_variable(big_constraint[j].b) * big_constraint[j].b_scaling +
builder.get_variable(big_constraint[j].c) * big_constraint[j].c_scaling +
next_w4_wire_value * big_constraint[j].d_scaling + big_constraint[j].const_scaling;
next_w4_wire_value = -next_w4_wire_value;
}
uint32_t next_w4_wire = builder.add_variable(next_w4_wire_value);
big_constraint.back().d = next_w4_wire;
big_constraint.back().d_scaling = fr(-1);
builder.create_big_mul_add_gate(big_constraint.back(), false);
}

// Add logic constraint
for (size_t i = 0; i < constraint_system.logic_constraints.size(); ++i) {
Expand Down Expand Up @@ -492,12 +519,14 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system,
for (auto [constraint, queue_entry] :
zip_view(constraint_system.ivc_recursion_constraints, ivc.stdlib_verification_queue)) {

// Reconstruct complete proof indices from acir constraint data (in which proof is stripped of public inputs)
// Reconstruct complete proof indices from acir constraint data (in which proof is
// stripped of public inputs)
std::vector<uint32_t> complete_proof_indices =
ProofSurgeon::create_indices_for_reconstructed_proof(constraint.proof, constraint.public_inputs);
ASSERT(complete_proof_indices.size() == queue_entry.proof.size());

// Assert equality between the proof indices from the constraint data and those of the internal proof
// Assert equality between the proof indices from the constraint data and those of the
// internal proof
for (auto [proof_value, proof_idx] : zip_view(queue_entry.proof, complete_proof_indices)) {
circuit.assert_equal(proof_value.get_witness_index(), proof_idx);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,13 @@ struct AcirFormat {
// for q_M,q_L,q_R,q_O,q_C and indices of three variables taking the role of left, right and output wire
// This could be a large vector so use slab allocator, we don't expect the blackbox implementations to be so large.
bb::SlabVector<PolyTripleConstraint> poly_triple_constraints;
// A standard ultra plonk arithmetic constraint, of width 4: q_Ma*b+q_A*a+q_B*b+q_C*c+q_d*d+q_const = 0
bb::SlabVector<bb::mul_quad_<bb::curve::BN254::ScalarField>> quad_constraints;
// A vector of vector of mul_quad gates (i.e arithmetic constraints of width 4)
// Each vector of gates represente a 'big' expression (a polynomial of degree 1 or 2 which does not fit inside one
// mul_gate) that has been splitted into multiple mul_gates, using w4_omega (the 4th wire of the next gate), to
// reduce the number of intermediate variables.
bb::SlabVector<std::vector<bb::mul_quad_<bb::curve::BN254::ScalarField>>> big_quad_constraints;
std::vector<BlockConstraint> block_constraints;

// Number of gates added to the circuit per original opcode.
Expand Down Expand Up @@ -153,6 +159,8 @@ struct AcirFormat {
avm_recursion_constraints,
ivc_recursion_constraints,
poly_triple_constraints,
quad_constraints,
big_quad_constraints,
block_constraints,
bigint_from_le_bytes_constraints,
bigint_to_le_bytes_constraints,
Expand Down
142 changes: 142 additions & 0 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs)
.assert_equalities = {},
.poly_triple_constraints = { constraint },
.quad_constraints = {},
.big_quad_constraints = {},
.block_constraints = {},
.original_opcode_indices = create_empty_original_opcode_indices(),
};
Expand Down Expand Up @@ -191,6 +192,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit)
.assert_equalities = {},
.poly_triple_constraints = { expr_a, expr_b, expr_c, expr_d },
.quad_constraints = {},
.big_quad_constraints = {},
.block_constraints = {},
.original_opcode_indices = create_empty_original_opcode_indices(),
};
Expand Down Expand Up @@ -282,6 +284,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass)
.q_c = fr::neg_one(),
} },
.quad_constraints = {},
.big_quad_constraints = {},
.block_constraints = {},
.original_opcode_indices = create_empty_original_opcode_indices(),
};
Expand Down Expand Up @@ -390,6 +393,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange)
.q_c = fr::neg_one(),
} },
.quad_constraints = {},
.big_quad_constraints = {},
.block_constraints = {},
.original_opcode_indices = create_empty_original_opcode_indices(),
};
Expand Down Expand Up @@ -502,6 +506,7 @@ TEST_F(AcirFormatTests, TestVarKeccak)
.assert_equalities = {},
.poly_triple_constraints = { dummy },
.quad_constraints = {},
.big_quad_constraints = {},
.block_constraints = {},
.original_opcode_indices = create_empty_original_opcode_indices(),
};
Expand Down Expand Up @@ -583,6 +588,7 @@ TEST_F(AcirFormatTests, TestKeccakPermutation)
.assert_equalities = {},
.poly_triple_constraints = {},
.quad_constraints = {},
.big_quad_constraints = {},
.block_constraints = {},
.original_opcode_indices = create_empty_original_opcode_indices(),
};
Expand Down Expand Up @@ -659,6 +665,7 @@ TEST_F(AcirFormatTests, TestCollectsGateCounts)
.assert_equalities = {},
.poly_triple_constraints = { first_gate, second_gate },
.quad_constraints = {},
.big_quad_constraints = {},
.block_constraints = {},
.original_opcode_indices = create_empty_original_opcode_indices(),
};
Expand All @@ -669,3 +676,138 @@ TEST_F(AcirFormatTests, TestCollectsGateCounts)

EXPECT_EQ(constraint_system.gates_per_opcode, std::vector<size_t>({ 2, 1 }));
}

TEST_F(AcirFormatTests, TestBigAdd)
{

WitnessVector witness_values;
witness_values.emplace_back(fr(0));

witness_values = {
fr(0), fr(1), fr(2), fr(3), fr(4), fr(5), fr(6), fr(7), fr(8), fr(9), fr(10), fr(11), fr(12), fr(13), fr(-91),
};

bb::mul_quad_<fr> quad1 = {
.a = 0,
.b = 1,
.c = 2,
.d = 3,
.mul_scaling = 0,
.a_scaling = fr::one(),
.b_scaling = fr::one(),
.c_scaling = fr::one(),
.d_scaling = fr::one(),
.const_scaling = fr(0),
};

bb::mul_quad_<fr> quad2 = {
.a = 4,
.b = 5,
.c = 6,
.d = 0,
.mul_scaling = 0,
.a_scaling = fr::one(),
.b_scaling = fr::one(),
.c_scaling = fr::one(),
.d_scaling = fr(0),
.const_scaling = fr(0),
};

bb::mul_quad_<fr> quad3 = {
.a = 7,
.b = 8,
.c = 9,
.d = 0,
.mul_scaling = 0,
.a_scaling = fr::one(),
.b_scaling = fr::one(),
.c_scaling = fr::one(),
.d_scaling = fr(0),
.const_scaling = fr(0),
};
bb::mul_quad_<fr> quad4 = {
.a = 10,
.b = 11,
.c = 12,
.d = 0,
.mul_scaling = 0,
.a_scaling = fr::one(),
.b_scaling = fr::one(),
.c_scaling = fr::one(),
.d_scaling = fr(0),
.const_scaling = fr(0),
};
bb::mul_quad_<fr> quad5 = {
.a = 13,
.b = 14,
.c = 0,
.d = 18,
.mul_scaling = 0,
.a_scaling = fr::one(),
.b_scaling = fr::one(),
.c_scaling = fr(0),
.d_scaling = fr(-1),
.const_scaling = fr(0),
};

auto res_x = fr(91);
auto assert_equal = poly_triple{
.a = 14,
.b = 0,
.c = 0,
.q_m = 0,
.q_l = fr::one(),
.q_r = 0,
.q_o = 0,
.q_c = res_x,
};
auto quad_constraint = { quad1, quad2, quad3, quad4, quad5 };
size_t num_variables = witness_values.size();
AcirFormat constraint_system{
.varnum = static_cast<uint32_t>(num_variables + 1),
.recursive = false,
.num_acir_opcodes = 1,
.public_inputs = {},
.logic_constraints = {},
.range_constraints = {},
.aes128_constraints = {},
.sha256_compression = {},
.schnorr_constraints = {},
.ecdsa_k1_constraints = {},
.ecdsa_r1_constraints = {},
.blake2s_constraints = {},
.blake3_constraints = {},
.keccak_constraints = {},
.keccak_permutations = {},
.pedersen_constraints = {},
.pedersen_hash_constraints = {},
.poseidon2_constraints = {},
.multi_scalar_mul_constraints = {},
.ec_add_constraints = {},
.recursion_constraints = {},
.honk_recursion_constraints = {},
.avm_recursion_constraints = {},
.ivc_recursion_constraints = {},
.bigint_from_le_bytes_constraints = {},
.bigint_to_le_bytes_constraints = {},
.bigint_operations = {},
.assert_equalities = {},
.poly_triple_constraints = { assert_equal },
.quad_constraints = {},
.big_quad_constraints = { quad_constraint },
.block_constraints = {},
.original_opcode_indices = create_empty_original_opcode_indices(),
};
mock_opcode_indices(constraint_system);

auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values);

auto composer = Composer();
auto prover = composer.create_prover(builder);

auto proof = prover.construct_proof();

EXPECT_TRUE(CircuitChecker::check(builder));
auto verifier = composer.create_verifier(builder);
EXPECT_EQ(verifier.verify_proof(proof), true);
}
Loading

0 comments on commit 3e05e22

Please sign in to comment.