@@ -21,14 +21,16 @@ void UltraComposer_<Flavor>::compute_circuit_size_parameters(CircuitBuilder& cir
21
21
lookups_size += table.lookup_gates .size ();
22
22
}
23
23
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 ;
24
26
num_public_inputs = circuit_constructor.public_inputs .size ();
27
+ num_ecc_op_gates = circuit_constructor.num_ecc_op_gates ;
25
28
26
29
// 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 ;
28
31
29
32
// 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;
32
34
33
35
// The number of gates is max(lookup gates + tables, rows already populated in trace) + 1, where the +1 is due to
34
36
// 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
48
50
return ;
49
51
}
50
52
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
60
54
auto wire_polynomials = construct_wire_polynomials_base<Flavor>(circuit_constructor, dyadic_circuit_size);
61
55
62
56
proving_key->w_l = wire_polynomials[0 ];
63
57
proving_key->w_r = wire_polynomials[1 ];
64
58
proving_key->w_o = wire_polynomials[2 ];
65
59
proving_key->w_4 = wire_polynomials[3 ];
66
60
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
67
67
polynomial s_1 (dyadic_circuit_size);
68
68
polynomial s_2 (dyadic_circuit_size);
69
69
polynomial s_3 (dyadic_circuit_size);
70
70
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
85
78
86
79
for (auto & table : circuit_constructor.lookup_tables ) {
87
80
const fr table_index (table.table_index );
@@ -120,11 +113,11 @@ template <UltraFlavor Flavor> void UltraComposer_<Flavor>::compute_witness(Circu
120
113
121
114
for (const auto & entry : lookup_gates) {
122
115
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 ;
128
121
}
129
122
}
130
123
@@ -137,11 +130,10 @@ template <UltraFlavor Flavor> void UltraComposer_<Flavor>::compute_witness(Circu
137
130
// Copy memory read/write record data into proving key. Prover needs to know which gates contain a read/write
138
131
// 'record' witness on the 4th wire. This wire value can only be fully computed once the first 3 wire polynomials
139
132
// 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; };
145
137
proving_key->memory_read_records = std::vector<uint32_t >();
146
138
proving_key->memory_write_records = std::vector<uint32_t >();
147
139
@@ -157,6 +149,38 @@ template <UltraFlavor Flavor> void UltraComposer_<Flavor>::compute_witness(Circu
157
149
computed_witness = true ;
158
150
}
159
151
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
+
160
184
template <UltraFlavor Flavor>
161
185
UltraProver_<Flavor> UltraComposer_<Flavor>::create_prover(CircuitBuilder& circuit_constructor)
162
186
{
@@ -258,6 +282,10 @@ std::shared_ptr<typename Flavor::ProvingKey> UltraComposer_<Flavor>::compute_pro
258
282
259
283
proving_key->contains_recursive_proof = contains_recursive_proof;
260
284
285
+ if constexpr (IsGoblinFlavor<Flavor>) {
286
+ proving_key->num_ecc_op_gates = num_ecc_op_gates;
287
+ }
288
+
261
289
return proving_key;
262
290
}
263
291
@@ -308,6 +336,12 @@ std::shared_ptr<typename Flavor::VerificationKey> UltraComposer_<Flavor>::comput
308
336
verification_key->lagrange_first = commitment_key->commit (proving_key->lagrange_first );
309
337
verification_key->lagrange_last = commitment_key->commit (proving_key->lagrange_last );
310
338
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
+
311
345
// // See `add_recusrive_proof()` for how this recursive data is assigned.
312
346
// verification_key->recursive_proof_public_input_indices =
313
347
// std::vector<uint32_t>(recursive_proof_public_input_indices.begin(),
@@ -319,5 +353,6 @@ std::shared_ptr<typename Flavor::VerificationKey> UltraComposer_<Flavor>::comput
319
353
}
320
354
template class UltraComposer_ <honk::flavor::Ultra>;
321
355
template class UltraComposer_ <honk::flavor::UltraGrumpkin>;
356
+ template class UltraComposer_ <honk::flavor::GoblinUltra>;
322
357
323
358
} // namespace proof_system::honk
0 commit comments