Skip to content
This repository was archived by the owner on Feb 17, 2025. It is now read-only.

Commit 660c54e

Browse files
committed
Reworked gates generation, now it is possible to split on constraint boundary. #36
1 parent 162ae63 commit 660c54e

File tree

3 files changed

+134
-64
lines changed

3 files changed

+134
-64
lines changed

include/nil/blueprint/transpiler/evm_verifier_gen.hpp

+116-61
Original file line numberDiff line numberDiff line change
@@ -200,23 +200,26 @@ namespace nil {
200200
++it;
201201
--degree;
202202
while (degree != 0) {
203-
if (degree == it->get_vars().size()) {
203+
if ((degree == it->get_vars().size()) && (it->get_coeff() != 0) ) {
204204
result << "\t\tsum = addmod(sum, " << it->get_coeff() << ", modulus);" << std::endl;
205205
++it;
206206
} else {
207-
result << "/* term with zero coeficient is skipped */" << std::endl;
207+
result << "\t\t/* term with zero coeficient is skipped */" << std::endl;
208208
}
209209
result << "\t\tsum = mulmod(sum, x, modulus);" << std::endl;
210210
--degree;
211211
}
212-
if (it != std::cend(comb)) {
212+
if (it != std::cend(comb) && (it->get_coeff() != 0) ) {
213213
result << "/* last term */" << std::endl;
214214
result << "\t\tsum = addmod(sum, " << it->get_coeff() << ", modulus);" << std::endl;
215215
}
216216
result << "\t\t/* End using Horner's formula */" << std::endl;
217217
} else {
218218
result << "\t\tsum = 0;" << std::endl;
219219
for (auto term = std::cbegin(comb); term != std::cend(comb); ++term) {
220+
if ( term->get_coeff() == 0) {
221+
continue;
222+
}
220223
const auto &vars = term->get_vars();
221224
std::size_t power;
222225

@@ -280,10 +283,9 @@ namespace nil {
280283
const typename PlaceholderParams::commitment_scheme_type &lpc_scheme,
281284
std::size_t permutation_size,
282285
std::string folder_name,
283-
std::size_t gates_library_size_threshold = 1400,
284-
std::size_t lookups_library_size_threshold = 1400,
285-
std::size_t gates_contract_size_threshold = 1400,
286-
std::size_t lookups_contract_size_threshold = 1400,
286+
std::size_t gates_contract_size_threshold = 800,
287+
std::size_t lookups_library_size_threshold = 1000,
288+
std::size_t lookups_contract_size_threshold = 1000,
287289
bool deduce_horner = true,
288290
bool optimize_powers = true
289291
) :
@@ -292,7 +294,6 @@ namespace nil {
292294
_lpc_scheme(lpc_scheme),
293295
_permutation_size(permutation_size),
294296
_folder_name(folder_name),
295-
_gates_library_size_threshold(gates_library_size_threshold),
296297
_lookups_library_size_threshold(lookups_library_size_threshold),
297298
_gates_contract_size_threshold(gates_contract_size_threshold),
298299
_lookups_contract_size_threshold(lookups_contract_size_threshold),
@@ -417,6 +418,15 @@ namespace nil {
417418
return out.str();
418419
}
419420

421+
std::size_t estimate_constraint_cost(std::string const& code) {
422+
/* proof-of-concept: cost = number of lines */
423+
std::size_t lines = 0;
424+
for(auto &ch: code) {
425+
lines += ch == '\n';
426+
}
427+
return lines;
428+
}
429+
420430
std::size_t estimate_gate_cost(std::string const& code) {
421431
/* proof-of-concept: cost = number of lines */
422432
std::size_t lines = 0;
@@ -491,6 +501,55 @@ namespace nil {
491501
return result.str();
492502
}
493503

504+
struct constraint_info {
505+
std::string code;
506+
std::size_t cost;
507+
std::size_t gate_index;
508+
std::size_t constraint_index;
509+
std::size_t selector_index;
510+
};
511+
512+
std::string print_constraint_series(typename std::vector<constraint_info>::iterator &it,
513+
typename std::vector<constraint_info>::iterator const& last) {
514+
std::stringstream result;
515+
std::size_t printed_cost = 0;
516+
std::size_t prev_sel = 0;
517+
518+
bool first_constraint = true;
519+
520+
while ((printed_cost < _gates_contract_size_threshold) && (it != last) ) {
521+
522+
if (first_constraint) {
523+
result << "// gate === " << it->gate_index << " ===" << std::endl;
524+
result << "\t\tgate = 0;" << std::endl;
525+
first_constraint = false;
526+
prev_sel = it->selector_index;
527+
} else if (prev_sel != it->selector_index) {
528+
result << "\t\tgate = mulmod(gate, basic_marshalling.get_uint256_be(blob, "<<prev_sel<<"), modulus);" << std::endl;
529+
result << "\t\tF = addmod(F, gate, modulus);" << std::endl;
530+
result << "// gate === " << it->gate_index << " ===" << std::endl;
531+
result << "\t\tgate = 0;" << std::endl;
532+
prev_sel = it->selector_index;
533+
}
534+
result << "// constraint " << it->constraint_index << std::endl;
535+
result << it->code;
536+
result << "\t\tsum = mulmod(sum, theta_acc, modulus);" << std::endl;
537+
result << "\t\ttheta_acc = mulmod(theta, theta_acc, modulus);" << std::endl;
538+
result << "\t\tgate = addmod(gate, sum, modulus);" << std::endl;
539+
540+
printed_cost += it->cost;
541+
++it;
542+
}
543+
544+
if (it != last) {
545+
result << "// gate computation code ended prematurely. continue in next library" << std::endl;
546+
}
547+
result << "\t\tgate = mulmod(gate, basic_marshalling.get_uint256_be(blob, "<<prev_sel<<"), modulus);" << std::endl;
548+
result << "\t\tF = addmod(F, gate, modulus);" << std::endl;
549+
550+
return result.str();
551+
}
552+
494553
std::string print_gate_argument(){
495554
std::size_t gates_count = _constraint_system.gates().size();
496555
if (gates_count == 0)
@@ -501,46 +560,57 @@ namespace nil {
501560
std::unordered_map<std::size_t, std::string> gate_codes;
502561
std::vector<std::pair<std::size_t, std::size_t>> gate_costs(gates_count);
503562
std::vector<std::size_t> gate_ids(gates_count);
563+
564+
std::vector<constraint_info> constraints;
565+
std::size_t total_cost = 0;
504566

505567
i = 0;
506-
for(const auto &gate: _constraint_system.gates()) {
507-
std::string code = gate_computation_code(gate);
508-
gate_costs[i] = std::make_pair(i, estimate_gate_cost(code));
509-
gate_codes[i] = code;
510-
++i;
511-
}
568+
for (const auto& gate: _constraint_system.gates()) {
569+
variable_type sel_var(gate.selector_index, 0, true, variable_type::column_type::selector);
570+
std::size_t j = 0;
571+
for (const auto& constraint: gate.constraints) {
572+
std::string code = constraint_computation_code_optimized(_var_indices, constraint);
573+
std::size_t cost = estimate_constraint_cost(code);
574+
std::size_t selector_index = _var_indices.at(sel_var)*0x20;
512575

513-
std::sort(gate_costs.begin(), gate_costs.end(),
514-
[](const std::pair<std::size_t, std::size_t> &a,
515-
const std::pair<std::size_t, std::size_t> &b) {
516-
return a.second > b.second;
517-
});
576+
constraints.push_back( {code, cost, i, j, selector_index} );
518577

519-
/* Fill contract inline gate computation, inline small gates first */
520-
std::unordered_set<std::size_t> inlined_gate_codes;
521-
std::size_t inlined_gate_codes_size = 0;
522-
for (auto gate=gate_costs.rbegin(); gate != gate_costs.rend(); ++gate) {
523-
if (gate->second + inlined_gate_codes_size < _gates_contract_size_threshold) {
524-
inlined_gate_codes.insert(gate->first);
525-
inlined_gate_codes_size += gate->second;
578+
total_cost += cost;
579+
++j;
526580
}
581+
++i;
527582
}
528583

529-
auto inlined_gates_end = std::remove_if(gate_costs.begin(), gate_costs.end(),
530-
[&inlined_gate_codes](const std::pair<std::size_t, std::size_t>& cost) {
531-
return inlined_gate_codes.count(cost.first) == 1 ;
532-
});
533-
gate_costs.erase(inlined_gates_end, gate_costs.end());
534584

535-
auto library_gates_buckets = split_items_into_buckets(gate_costs, _gates_library_size_threshold);
536-
std::vector<std::size_t> gate_lib(gates_count);
585+
std::size_t gate_modules_count = 0;
537586

538-
for(auto const& lib: library_gates_buckets) {
539-
_gate_includes += "import \"./gate_" + to_string(lib.first) + ".sol\";\n";
540-
for(auto g: lib.second) {
541-
gate_lib[g] = lib.first;
587+
588+
std::size_t current_selector = 0;
589+
if (total_cost <= _gates_contract_size_threshold) {
590+
auto it = constraints.begin();
591+
gate_argument_str << "\t\tuint256 prod;" << std::endl;
592+
gate_argument_str << "\t\tuint256 sum;" << std::endl;
593+
gate_argument_str << "\t\tuint256 gate;" << std::endl;
594+
gate_argument_str << print_constraint_series(it, constraints.end());
595+
} else {
596+
auto it = constraints.begin();
597+
while (it != constraints.end()) {
598+
std::string code = print_constraint_series(it, constraints.end());
599+
600+
std::string result = modular_external_gate_library_template;
601+
boost::replace_all(result, "$TEST_NAME$", _test_name);
602+
boost::replace_all(result, "$GATE_LIB_ID$", to_string(gate_modules_count));
603+
boost::replace_all(result, "$CONSTRAINT_SERIES_CODE$", code);
604+
boost::replace_all(result, "$MODULUS$", to_string(PlaceholderParams::field_type::modulus));
605+
606+
std::ofstream out;
607+
out.open(_folder_name + "/gate_" + to_string(gate_modules_count) + ".sol");
608+
out << result;
609+
out.close();
610+
_gate_includes += "import \"./gate_" + to_string(gate_modules_count) + ".sol\";\n";
611+
612+
++gate_modules_count;
542613
}
543-
print_gates_library_file(lib.first, lib.second, gate_codes);
544614
}
545615

546616
std::stringstream power_functions;
@@ -557,36 +627,22 @@ namespace nil {
557627
utils << utils_library;
558628
utils.close();
559629

560-
if (inlined_gate_codes.size() > 0) {
561-
gate_argument_str << "\t\tuint256 sum;" << std::endl;
562-
gate_argument_str << "\t\tuint256 prod;" << std::endl;
563-
gate_argument_str << "\t\tuint256 gate;" << std::endl;
630+
for ( i = 0; i < gate_modules_count; ++i ) {
631+
std::string gate_eval_string = gate_call_template;
632+
boost::replace_all(gate_eval_string, "$TEST_NAME$", _test_name);
633+
boost::replace_all(gate_eval_string, "$GATE_LIB_ID$", to_string(i));
634+
gate_argument_str << gate_eval_string << std::endl;
564635
}
565-
566636
i = 0;
567-
for(const auto &gate: _constraint_system.gates()){
568-
if (inlined_gate_codes.count(i) == 1) {
569-
gate_argument_str << "/* -- gate " << i << " is inlined -- */" << std::endl;
570-
gate_argument_str << gate_codes[i] << std::endl;
571-
} else {
572-
std::string gate_eval_string = gate_call_template;
573-
boost::replace_all(gate_eval_string, "$TEST_NAME$", _test_name);
574-
boost::replace_all(gate_eval_string, "$GATE_LIB_ID$", to_string(gate_lib[i]));
575-
boost::replace_all(gate_eval_string, "$GATE_ID$", to_string(i));
576-
boost::replace_all(gate_eval_string, "$MODULUS$", to_string(PlaceholderParams::field_type::modulus));
577-
gate_argument_str << gate_eval_string << std::endl;
578-
}
579-
++i;
580-
}
581637

582-
if (library_gates_buckets.size() > 0) {
638+
if ( gate_modules_count > 0) {
583639
std::ofstream out;
584640
out.open(_folder_name + "/gate_libs_list.json");
585641
out << "[" << std::endl;
586-
for(i = 0; i < library_gates_buckets.size()-1; ++i ) {
642+
for(i = 0; i < gate_modules_count-1; ++i ) {
587643
out << "\"" << "gate_" << _test_name << "_" << i << "\"," << std::endl;
588644
}
589-
out << "\"" << "gate_" << _test_name << "_" << library_gates_buckets.size()-1 << "\"" << std::endl;
645+
out << "\"" << "gate_" << _test_name << "_" << gate_modules_count-1 << "\"" << std::endl;
590646
out << "]" << std::endl;
591647
out.close();
592648
}
@@ -794,7 +850,6 @@ namespace nil {
794850
std::string _lookup_includes;
795851
std::size_t _gates_contract_size_threshold;
796852
std::size_t _lookups_contract_size_threshold;
797-
std::size_t _gates_library_size_threshold;
798853
std::size_t _lookups_library_size_threshold;
799854
};
800855
}

include/nil/blueprint/transpiler/templates/external_gate.hpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,21 @@ import "./utils.sol";
4646
library gate_$TEST_NAME$_$GATE_LIB_ID${
4747
uint256 constant modulus = $MODULUS$;
4848
49-
$GATES_COMPUTATION_CODE$
49+
function evaluate_constraint_series_be(
50+
bytes calldata blob,
51+
uint256 theta,
52+
uint256 theta_acc
53+
) external pure returns (uint256 F, uint256) {
54+
uint256 sum;
55+
uint256 gate;
56+
uint256 prod;
57+
uint256 x;
58+
59+
$CONSTRAINT_SERIES_CODE$
60+
61+
return( F, theta_acc );
62+
}
63+
5064
}
5165
)";
5266
}

include/nil/blueprint/transpiler/templates/gate_argument.hpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55

66
namespace nil {
77
namespace blueprint {
8-
std::string gate_call_template =
9-
"\t\t(eval, theta_acc) = gate_$TEST_NAME$_$GATE_LIB_ID$.evaluate_gate_$GATE_ID$_be( blob, theta, theta_acc ); F = addmod(F, eval, modulus);";
8+
std::string gate_call_template =
9+
"\t\t(eval, theta_acc) = gate_$TEST_NAME$_$GATE_LIB_ID$.evaluate_constraint_series_be( blob, theta, theta_acc);\n"
10+
"\t\tF = addmod(F, eval, modulus);\n";
1011

1112
std::string modular_gate_argument_library_template = R"(
1213
// SPDX-License-Identifier: Apache-2.0.

0 commit comments

Comments
 (0)