@@ -21,14 +21,16 @@ void UltraComposer_<Flavor>::compute_circuit_size_parameters(CircuitBuilder& cir
2121 lookups_size += table.lookup_gates .size ();
2222 }
2323
24+ // Get num conventional gates, num public inputs and num Goblin style ECC op gates
25+ const size_t num_gates = circuit_constructor.num_gates ;
2426 num_public_inputs = circuit_constructor.public_inputs .size ();
27+ num_ecc_op_gates = circuit_constructor.num_ecc_op_gates ;
2528
2629 // minimum circuit size due to the length of lookups plus tables
27- const size_t minimum_circuit_size_due_to_lookups = tables_size + lookups_size + zero_row_offset ;
30+ const size_t minimum_circuit_size_due_to_lookups = tables_size + lookups_size + num_zero_rows ;
2831
2932 // number of populated rows in the execution trace
30- const size_t num_rows_populated_in_execution_trace =
31- circuit_constructor.num_gates + circuit_constructor.public_inputs .size () + zero_row_offset;
33+ size_t num_rows_populated_in_execution_trace = num_zero_rows + num_ecc_op_gates + num_public_inputs + num_gates;
3234
3335 // The number of gates is max(lookup gates + tables, rows already populated in trace) + 1, where the +1 is due to
3436 // addition of a "zero row" at top of the execution trace to ensure wires and other polys are shiftable.
@@ -48,40 +50,31 @@ template <UltraFlavor Flavor> void UltraComposer_<Flavor>::compute_witness(Circu
4850 return ;
4951 }
5052
51- // At this point, the wires have been populated with as many values as rows in the execution trace. We need to pad
52- // with zeros up to the full length, i.e. total_num_gates = already populated rows of execution trace + tables_size.
53- for (size_t i = 0 ; i < tables_size; ++i) {
54- circuit_constructor.w_l .emplace_back (circuit_constructor.zero_idx );
55- circuit_constructor.w_r .emplace_back (circuit_constructor.zero_idx );
56- circuit_constructor.w_o .emplace_back (circuit_constructor.zero_idx );
57- circuit_constructor.w_4 .emplace_back (circuit_constructor.zero_idx );
58- }
59-
53+ // Construct the conventional wire polynomials
6054 auto wire_polynomials = construct_wire_polynomials_base<Flavor>(circuit_constructor, dyadic_circuit_size);
6155
6256 proving_key->w_l = wire_polynomials[0 ];
6357 proving_key->w_r = wire_polynomials[1 ];
6458 proving_key->w_o = wire_polynomials[2 ];
6559 proving_key->w_4 = wire_polynomials[3 ];
6660
61+ // If Goblin, construct the ECC op queue wire polynomials
62+ if constexpr (IsGoblinFlavor<Flavor>) {
63+ construct_ecc_op_wire_polynomials (wire_polynomials);
64+ }
65+
66+ // Construct the sorted concatenated list polynomials for the lookup argument
6767 polynomial s_1 (dyadic_circuit_size);
6868 polynomial s_2 (dyadic_circuit_size);
6969 polynomial s_3 (dyadic_circuit_size);
7070 polynomial s_4 (dyadic_circuit_size);
71- // TODO(luke): The +1 size for z_lookup is not necessary and can lead to confusion. Resolve.
72- polynomial z_lookup (dyadic_circuit_size + 1 ); // Only instantiated in this function; nothing assigned.
73-
74- // TODO(kesha): Look at this once we figure out how we do ZK (previously we had roots cut out, so just added
75- // randomness)
76- // The size of empty space in sorted polynomials
77- size_t count = dyadic_circuit_size - tables_size - lookups_size;
78- ASSERT (count > 0 ); // We need at least 1 row of zeroes for the permutation argument
79- for (size_t i = 0 ; i < count; ++i) {
80- s_1[i] = 0 ;
81- s_2[i] = 0 ;
82- s_3[i] = 0 ;
83- s_4[i] = 0 ;
84- }
71+
72+ // The sorted list polynomials have (tables_size + lookups_size) populated entries. We define the index below so
73+ // that these entries are written into the last indices of the polynomials. The values on the first
74+ // dyadic_circuit_size - (tables_size + lookups_size) indices are automatically initialized to zero via the
75+ // polynomial constructor.
76+ size_t s_index = dyadic_circuit_size - tables_size - lookups_size;
77+ ASSERT (s_index > 0 ); // We need at least 1 row of zeroes for the permutation argument
8578
8679 for (auto & table : circuit_constructor.lookup_tables ) {
8780 const fr table_index (table.table_index );
@@ -120,11 +113,11 @@ template <UltraFlavor Flavor> void UltraComposer_<Flavor>::compute_witness(Circu
120113
121114 for (const auto & entry : lookup_gates) {
122115 const auto components = entry.to_sorted_list_components (table.use_twin_keys );
123- s_1[count ] = components[0 ];
124- s_2[count ] = components[1 ];
125- s_3[count ] = components[2 ];
126- s_4[count ] = table_index;
127- ++count ;
116+ s_1[s_index ] = components[0 ];
117+ s_2[s_index ] = components[1 ];
118+ s_3[s_index ] = components[2 ];
119+ s_4[s_index ] = table_index;
120+ ++s_index ;
128121 }
129122 }
130123
@@ -137,11 +130,10 @@ template <UltraFlavor Flavor> void UltraComposer_<Flavor>::compute_witness(Circu
137130 // Copy memory read/write record data into proving key. Prover needs to know which gates contain a read/write
138131 // 'record' witness on the 4th wire. This wire value can only be fully computed once the first 3 wire polynomials
139132 // have been committed to. The 4th wire on these gates will be a random linear combination of the first 3 wires,
140- // using the plookup challenge `eta`. We need to update the records with an offset Because we shift the gates by
141- // the number of public inputs plus an additional offset for a zero row.
142- auto add_public_inputs_offset = [this ](uint32_t gate_index) {
143- return gate_index + num_public_inputs + zero_row_offset;
144- };
133+ // using the plookup challenge `eta`. We need to update the records with an offset Because we shift the gates to
134+ // account for everything that comes before them in the execution trace, e.g. public inputs, a zero row, etc.
135+ size_t offset = num_ecc_op_gates + num_public_inputs + num_zero_rows;
136+ auto add_public_inputs_offset = [offset](uint32_t gate_index) { return gate_index + offset; };
145137 proving_key->memory_read_records = std::vector<uint32_t >();
146138 proving_key->memory_write_records = std::vector<uint32_t >();
147139
@@ -157,6 +149,38 @@ template <UltraFlavor Flavor> void UltraComposer_<Flavor>::compute_witness(Circu
157149 computed_witness = true ;
158150}
159151
152+ /* *
153+ * @brief Construct Goblin style ECC op wire polynomials
154+ * @details The Ecc op wire values are assumed to have already been stored in the corresponding block of the
155+ * conventional wire polynomials. The values for the ecc op wire polynomials are set based on those values.
156+ *
157+ * @tparam Flavor
158+ * @param wire_polynomials
159+ */
160+ template <UltraFlavor Flavor> void UltraComposer_<Flavor>::construct_ecc_op_wire_polynomials(auto & wire_polynomials)
161+ {
162+ std::array<polynomial, Flavor::NUM_WIRES> op_wire_polynomials;
163+ for (auto & poly : op_wire_polynomials) {
164+ poly = polynomial (dyadic_circuit_size);
165+ }
166+
167+ // The ECC op wires are constructed to contain the op data on the appropriate range and to vanish everywhere else.
168+ // The op data is assumed to have already been stored at the correct location in the convetional wires so the data
169+ // can simply be copied over directly.
170+ const size_t op_wire_offset = Flavor::has_zero_row ? 1 : 0 ;
171+ for (size_t poly_idx = 0 ; poly_idx < Flavor::NUM_WIRES; ++poly_idx) {
172+ for (size_t i = 0 ; i < num_ecc_op_gates; ++i) {
173+ size_t idx = i + op_wire_offset;
174+ op_wire_polynomials[poly_idx][idx] = wire_polynomials[poly_idx][idx];
175+ }
176+ }
177+
178+ proving_key->ecc_op_wire_1 = op_wire_polynomials[0 ];
179+ proving_key->ecc_op_wire_2 = op_wire_polynomials[1 ];
180+ proving_key->ecc_op_wire_3 = op_wire_polynomials[2 ];
181+ proving_key->ecc_op_wire_4 = op_wire_polynomials[3 ];
182+ }
183+
160184template <UltraFlavor Flavor>
161185UltraProver_<Flavor> UltraComposer_<Flavor>::create_prover(CircuitBuilder& circuit_constructor)
162186{
@@ -258,6 +282,10 @@ std::shared_ptr<typename Flavor::ProvingKey> UltraComposer_<Flavor>::compute_pro
258282
259283 proving_key->contains_recursive_proof = contains_recursive_proof;
260284
285+ if constexpr (IsGoblinFlavor<Flavor>) {
286+ proving_key->num_ecc_op_gates = num_ecc_op_gates;
287+ }
288+
261289 return proving_key;
262290}
263291
@@ -308,6 +336,12 @@ std::shared_ptr<typename Flavor::VerificationKey> UltraComposer_<Flavor>::comput
308336 verification_key->lagrange_first = commitment_key->commit (proving_key->lagrange_first );
309337 verification_key->lagrange_last = commitment_key->commit (proving_key->lagrange_last );
310338
339+ // TODO(luke): Similar to the lagrange_first/last polynomials, we dont really need to commit to this polynomial due
340+ // to its simple structure. Handling it in the same way as the lagrange polys for now for simplicity.
341+ if constexpr (IsGoblinFlavor<Flavor>) {
342+ verification_key->lagrange_ecc_op = commitment_key->commit (proving_key->lagrange_ecc_op );
343+ }
344+
311345 // // See `add_recusrive_proof()` for how this recursive data is assigned.
312346 // verification_key->recursive_proof_public_input_indices =
313347 // std::vector<uint32_t>(recursive_proof_public_input_indices.begin(),
@@ -319,5 +353,6 @@ std::shared_ptr<typename Flavor::VerificationKey> UltraComposer_<Flavor>::comput
319353}
320354template class UltraComposer_ <honk::flavor::Ultra>;
321355template class UltraComposer_ <honk::flavor::UltraGrumpkin>;
356+ template class UltraComposer_ <honk::flavor::GoblinUltra>;
322357
323358} // namespace proof_system::honk
0 commit comments