src/proof/GeniusAssigner.cpp¶
Source file of the assigner from bytecode to circuit. More...
Namespaces¶
| Name |
|---|
| sgns |
Functions¶
| Name | |
|---|---|
| OUTCOME_CPP_DEFINE_CATEGORY_3(sgns , GeniusAssigner::AssignerError , e ) |
Detailed Description¶
Source file of the assigner from bytecode to circuit.
Date: 2024-09-09 Henrique A. Klein ([email protected])
Functions Documentation¶
function OUTCOME_CPP_DEFINE_CATEGORY_3¶
Source code¶
#include <boost/json/src.hpp>
#include "GeniusAssigner.hpp"
#include "NilFileHelper.hpp"
OUTCOME_CPP_DEFINE_CATEGORY_3( sgns, GeniusAssigner::AssignerError, e )
{
using AssignerError = sgns::GeniusAssigner::AssignerError;
switch ( e )
{
case AssignerError::EMPTY_BYTECODE:
return "No bytecode IR file provided. Please check the file path";
case AssignerError::BYTECODE_MISMATCH:
return "Bytecode IR doesn't match the public/private parameters";
case AssignerError::HAS_SIZE_ESTIMATION:
return "Has Size estimation. Don't know what that means";
case AssignerError::TABLE_PATH_ERROR:
return "The chosen file path for the table file can't be opened";
case AssignerError::CIRCUIT_PATH_ERROR:
return "The chosen file path for the circuit file can't be opened";
case AssignerError::SELECTOR_COLUMNS_INVALID:
return "Number of selector columns is invalid";
case AssignerError::WRONG_TARGET_PROVER:
return "The target prover is outside the prover's range";
case AssignerError::UNSATISFIED_CIRCUIT:
return "The generated circuit and table are not consistent with each other";
case AssignerError::TABLE_CIRCUIT_NUM_MISMATCH:
return "The number of circuit and tables don't match";
case AssignerError::CIRCUIT_NOT_FOUND:
return "The desired circuit wasn't found";
}
return "Unknown error";
}
namespace sgns
{
outcome::result<std::vector<GeniusAssigner::AssignerOutput>> GeniusAssigner::GenerateCircuitAndTable(
const boost::json::array &public_inputs_json,
const boost::json::array &private_inputs_json,
const std::string &bytecode_file_payload )
{
if ( !assigner_instance_.parse_ir_buffer( bytecode_file_payload.data() ) )
{
return outcome::failure( AssignerError::EMPTY_BYTECODE );
}
if ( !assigner_instance_.evaluate( public_inputs_json, private_inputs_json ) )
{
return outcome::failure( AssignerError::BYTECODE_MISMATCH );
}
if ( gen_mode_.has_size_estimation() )
{
return outcome::failure( AssignerError::HAS_SIZE_ESTIMATION );
}
// pack lookup tables
if ( assigner_instance_.circuits[0].get_reserved_tables().size() > 0 )
{
std::vector<std::size_t> lookup_columns_indices;
lookup_columns_indices.resize( LOOKUP_CONSTANT_COLUMNS );
// fill ComponentConstantColumns, ComponentConstantColumns + 1, ...
std::iota( lookup_columns_indices.begin(), lookup_columns_indices.end(), COMPONENT_CONSTANT_COLUMNS );
// check if lookup selectors were not used
auto max_used_selector_idx = assigner_instance_.assignments[0].selectors_amount() - 1;
while ( max_used_selector_idx > 0 )
{
max_used_selector_idx--;
if ( assigner_instance_.assignments[0].selector( max_used_selector_idx ).size() > 0 )
{
break;
}
}
if ( max_used_selector_idx >= COMPONENT_SELECTOR_COLUMNS )
{
return outcome::failure( AssignerError::SELECTOR_COLUMNS_INVALID );
}
std::uint32_t max_lookup_rows = 500000;
auto usable_rows_amount = crypto3::zk::snark::pack_lookup_tables_horizontal(
assigner_instance_.circuits[0].get_reserved_indices(),
assigner_instance_.circuits[0].get_reserved_tables(),
assigner_instance_.circuits[0].get(),
assigner_instance_.assignments[0].get(),
lookup_columns_indices,
max_used_selector_idx + 1,
0,
max_lookup_rows );
}
constexpr std::uint32_t invalid_target_prover = std::numeric_limits<std::uint32_t>::max();
std::vector<PlonkAssignTableType> AssignmentTableVector;
std::vector<PlonkConstraintSystemType> PlonkCircuitVector;
// print assignment tables and circuits
if ( assigner_instance_.assignments.size() == 1 &&
( TARGET_PROVER == 0 || TARGET_PROVER == invalid_target_prover ) )
{
// print assignment table
if ( gen_mode_.has_assignments() )
{
auto AssignmentTable = BuildPlonkAssignmentTable( assigner_instance_.assignments[0],
PrintTableKind::SINGLE_PROVER,
COMPONENT_CONSTANT_COLUMNS,
COMPONENT_SELECTOR_COLUMNS );
AssignmentTableVector.push_back( AssignmentTable );
}
// print circuit
if ( gen_mode_.has_circuit() )
{
auto PlonkCircuit = BuildPlonkConstraintSystem( assigner_instance_.circuits[0],
assigner_instance_.assignments[0],
false );
PlonkCircuitVector.push_back( PlonkCircuit );
}
if ( ( CHECK_VALIDITY == "check" ) && gen_mode_.has_assignments() && gen_mode_.has_circuit() )
{
if ( !nil::blueprint::is_satisfied( assigner_instance_.circuits[0].get(),
assigner_instance_.assignments[0].get() ) )
{
return outcome::failure( AssignerError::UNSATISFIED_CIRCUIT );
}
}
}
else if ( assigner_instance_.assignments.size() > 1 &&
( TARGET_PROVER < assigner_instance_.assignments.size() || TARGET_PROVER == invalid_target_prover ) )
{
std::uint32_t start_idx = ( TARGET_PROVER == invalid_target_prover ) ? 0 : TARGET_PROVER;
std::uint32_t end_idx = ( TARGET_PROVER == invalid_target_prover ) ? assigner_instance_.assignments.size()
: TARGET_PROVER + 1;
for ( std::uint32_t idx = start_idx; idx < end_idx; idx++ )
{
// print assignment table
if ( gen_mode_.has_assignments() )
{
auto AssignmentTable = BuildPlonkAssignmentTable( assigner_instance_.assignments[idx],
PrintTableKind::MULTI_PROVER,
COMPONENT_CONSTANT_COLUMNS,
COMPONENT_SELECTOR_COLUMNS );
AssignmentTableVector.push_back( AssignmentTable );
}
// print circuit
if ( gen_mode_.has_circuit() )
{
auto PlonkCircuit = BuildPlonkConstraintSystem( assigner_instance_.circuits[idx],
assigner_instance_.assignments[idx],
( idx > 0 ) );
PlonkCircuitVector.push_back( PlonkCircuit );
}
if ( ( CHECK_VALIDITY == "check" ) && gen_mode_.has_assignments() && gen_mode_.has_circuit() )
{
assigner_instance_.assignments[idx].set_check( true );
bool is_accessible = nil::blueprint::is_satisfied( assigner_instance_.circuits[idx],
assigner_instance_.assignments[idx] );
assigner_instance_.assignments[idx].set_check( false );
if ( !is_accessible )
{
return outcome::failure( AssignerError::UNSATISFIED_CIRCUIT );
}
}
}
}
else
{
return outcome::failure( AssignerError::WRONG_TARGET_PROVER );
}
if ( AssignmentTableVector.size() != PlonkCircuitVector.size() )
{
return outcome::failure( AssignerError::TABLE_CIRCUIT_NUM_MISMATCH );
}
std::vector<AssignerOutput> outputs;
for ( size_t i = 0; i < AssignmentTableVector.size(); ++i )
{
outputs.push_back( AssignerOutput{ PlonkCircuitVector.at( i ), AssignmentTableVector.at( i ) } );
}
return outputs;
}
outcome::result<std::vector<GeniusAssigner::AssignerOutput>> GeniusAssigner::GenerateCircuitAndTable(
const std::vector<int> &public_inputs,
const std::vector<int> &private_inputs,
const std::string &bytecode_file_payload )
{
boost::json::array public_inputs_json_array;
boost::json::array private_inputs_json_array;
// Loop through the regular_array and create boost::json::objects for each element
for ( const int &value : public_inputs )
{
boost::json::object obj;
obj["field"] = value; // Add {"field": value} to the object
public_inputs_json_array.push_back( obj ); // Add the object to the boost::json::array
}
for ( const int &value : private_inputs )
{
boost::json::object obj;
obj["field"] = value; // Add {"field": value} to the object
private_inputs_json_array.push_back( obj ); // Add the object to the boost::json::array
}
return GenerateCircuitAndTable( public_inputs_json_array, private_inputs_json_array, bytecode_file_payload );
}
outcome::result<void> GeniusAssigner::PrintCircuitAndTable( const std::vector<AssignerOutput> &assigner_outputs,
const std::string &table_path,
const std::string &circuit_path )
{
for ( size_t i = 0; i < assigner_outputs.size(); ++i )
{
std::ofstream otable;
otable.open( table_path + std::to_string( i ), std::ios_base::binary | std::ios_base::out );
if ( !otable )
{
return outcome::failure( AssignerError::TABLE_PATH_ERROR );
}
NilFileHelper::PrintMarshalledData<PlonkAssignTableType>( assigner_outputs.at( i ).table, otable );
otable.close();
std::ofstream ocircuit;
ocircuit.open( circuit_path + std::to_string( i ), std::ios_base::binary | std::ios_base::out );
if ( !ocircuit )
{
return outcome::failure( AssignerError::CIRCUIT_PATH_ERROR );
}
if ( i >= assigner_instance_.circuits.size() )
{
return outcome::failure( AssignerError::CIRCUIT_NOT_FOUND );
}
NilFileHelper::PrintMarshalledData<PlonkConstraintSystemType>( assigner_outputs.at( i ).constrains,
ocircuit );
ocircuit.close();
}
return outcome::success();
}
GeniusAssigner::PlonkAssignTableType GeniusAssigner::BuildPlonkAssignmentTable(
const AssignmentTableType &table_proxy,
PrintTableKind print_kind,
std::uint32_t ComponentConstantColumns,
std::uint32_t ComponentSelectorColumns )
{
std::uint32_t total_size;
std::uint32_t shared_size = static_cast<std::uint32_t>( print_kind );
std::uint32_t public_input_size = table_proxy.public_inputs_amount();
std::uint32_t usable_rows_amount = GetUsableRowsAmount( table_proxy, print_kind );
//std::uint32_t total_columns = table_proxy.witnesses_amount() + shared_size +
// table_proxy.public_inputs_amount() + table_proxy.selectors_amount() +
// table_proxy.selectors_amount();
std::uint32_t padded_rows_amount = std::pow( 2, std::ceil( std::log2( usable_rows_amount ) ) );
if ( padded_rows_amount == usable_rows_amount )
{
padded_rows_amount *= 2;
}
if ( padded_rows_amount < 8 )
{
padded_rows_amount = 8;
}
//total_size = padded_rows_amount * total_columns;
using TTypeBase = nil::marshalling::field_type<AssignerEndianess>;
auto table_vectors = GetTableVectors( table_proxy,
print_kind,
ComponentConstantColumns,
ComponentSelectorColumns,
padded_rows_amount );
auto filled_val = PlonkAssignTableType( std::make_tuple(
nil::marshalling::types::integral<TTypeBase, std::size_t>( table_proxy.witnesses_amount() ),
nil::marshalling::types::integral<TTypeBase, std::size_t>( table_proxy.public_inputs_amount() +
shared_size ),
nil::marshalling::types::integral<TTypeBase, std::size_t>( table_proxy.constants_amount() ),
nil::marshalling::types::integral<TTypeBase, std::size_t>( table_proxy.selectors_amount() ),
nil::marshalling::types::integral<TTypeBase, std::size_t>( usable_rows_amount ),
nil::marshalling::types::integral<TTypeBase, std::size_t>( padded_rows_amount ),
nil::crypto3::marshalling::types::
fill_field_element_vector<typename AssignmentTableType::field_type::value_type, AssignerEndianess>(
table_vectors.witness_values ),
nil::crypto3::marshalling::types::
fill_field_element_vector<typename AssignmentTableType::field_type::value_type, AssignerEndianess>(
table_vectors.public_input_values ),
nil::crypto3::marshalling::types::
fill_field_element_vector<typename AssignmentTableType::field_type::value_type, AssignerEndianess>(
table_vectors.constant_values ),
nil::crypto3::marshalling::types::
fill_field_element_vector<typename AssignmentTableType::field_type::value_type, AssignerEndianess>(
table_vectors.selector_values ) ) );
return filled_val;
}
std::uint32_t GeniusAssigner::GetUsableRowsAmount( const AssignmentTableType &table_proxy,
PrintTableKind print_kind )
{
std::uint32_t usable_rows_amount = 0;
std::uint32_t max_shared_size = 0;
std::uint32_t max_witness_size = 0;
std::uint32_t max_public_inputs_size = 0;
std::uint32_t max_constant_size = 0;
std::uint32_t max_selector_size = 0;
std::uint32_t shared_size = static_cast<std::uint32_t>( print_kind );
std::uint32_t witness_size = table_proxy.witnesses_amount();
std::uint32_t constant_size = table_proxy.constants_amount();
std::uint32_t selector_size = table_proxy.selectors_amount();
std::set<std::uint32_t> lookup_constant_cols = {};
std::set<std::uint32_t> lookup_selector_cols = {};
if ( print_kind == PrintTableKind::MULTI_PROVER )
{
witness_size = 0;
constant_size = 0;
selector_size = 0;
usable_rows_amount = table_proxy.get_used_rows().size();
lookup_constant_cols = table_proxy.get_lookup_constant_cols();
lookup_selector_cols = table_proxy.get_lookup_selector_cols();
}
for ( std::uint32_t i = 0; i < table_proxy.public_inputs_amount(); i++ )
{
max_public_inputs_size = std::max( max_public_inputs_size, table_proxy.public_input_column_size( i ) );
}
for ( std::uint32_t i = 0; i < shared_size; i++ )
{
max_shared_size = std::max( max_shared_size, table_proxy.shared_column_size( i ) );
}
for ( std::uint32_t i = 0; i < witness_size; i++ )
{
max_witness_size = std::max( max_witness_size, table_proxy.witness_column_size( i ) );
}
for ( std::uint32_t i = 0; i < constant_size; i++ )
{
max_constant_size = std::max( max_constant_size, table_proxy.constant_column_size( i ) );
}
for ( const auto &i : lookup_constant_cols )
{
max_constant_size = std::max( max_constant_size, table_proxy.constant_column_size( i ) );
}
for ( std::uint32_t i = 0; i < selector_size; i++ )
{
max_selector_size = std::max( max_selector_size, table_proxy.selector_column_size( i ) );
}
for ( const auto &i : lookup_selector_cols )
{
max_selector_size = std::max( max_selector_size, table_proxy.selector_column_size( i ) );
}
usable_rows_amount = std::max( { usable_rows_amount,
max_shared_size,
max_witness_size,
max_public_inputs_size,
max_constant_size,
max_selector_size } );
return usable_rows_amount;
}
GeniusAssigner::TableVectors GeniusAssigner::GetTableVectors( const AssignmentTableType &table_proxy,
PrintTableKind print_kind,
std::uint32_t ComponentConstantColumns,
std::uint32_t ComponentSelectorColumns,
std::uint32_t padded_rows_amount )
{
std::uint32_t shared_size = static_cast<std::uint32_t>( print_kind );
TableVectors table_vectors;
table_vectors.witness_values.resize( padded_rows_amount * table_proxy.witnesses_amount(), 0 );
table_vectors.public_input_values.resize( padded_rows_amount *
( table_proxy.public_inputs_amount() + shared_size ),
0 );
table_vectors.constant_values.resize( padded_rows_amount * table_proxy.constants_amount(), 0 );
table_vectors.selector_values.resize( padded_rows_amount * table_proxy.selectors_amount(), 0 );
if ( print_kind == PrintTableKind::SINGLE_PROVER )
{
auto it = table_vectors.witness_values.begin();
for ( std::uint32_t i = 0; i < table_proxy.witnesses_amount(); i++ )
{
FillVectorValue<typename AssignmentTableType::field_type::value_type,
typename crypto3::zk::snark::plonk_column<BlueprintFieldType>>(
table_vectors.witness_values,
table_proxy.witness( i ),
it );
it += padded_rows_amount;
}
it = table_vectors.public_input_values.begin();
for ( std::uint32_t i = 0; i < table_proxy.public_inputs_amount(); i++ )
{
FillVectorValue<typename AssignmentTableType::field_type::value_type,
typename crypto3::zk::snark::plonk_column<BlueprintFieldType>>(
table_vectors.public_input_values,
table_proxy.public_input( i ),
it );
it += padded_rows_amount;
}
it = table_vectors.constant_values.begin();
for ( std::uint32_t i = 0; i < table_proxy.constants_amount(); i++ )
{
FillVectorValue<typename AssignmentTableType::field_type::value_type,
typename crypto3::zk::snark::plonk_column<BlueprintFieldType>>(
table_vectors.constant_values,
table_proxy.constant( i ),
it );
it += padded_rows_amount;
}
it = table_vectors.selector_values.begin();
for ( std::uint32_t i = 0; i < table_proxy.selectors_amount(); i++ )
{
FillVectorValue<typename AssignmentTableType::field_type::value_type,
typename crypto3::zk::snark::plonk_column<BlueprintFieldType>>(
table_vectors.selector_values,
table_proxy.selector( i ),
it );
it += padded_rows_amount;
}
}
else
{
const auto &rows = table_proxy.get_used_rows();
const auto &selector_rows = table_proxy.get_used_selector_rows();
std::uint32_t witness_idx = 0;
// witness
for ( std::size_t i = 0; i < table_proxy.witnesses_amount(); i++ )
{
const auto column_size = table_proxy.witness_column_size( i );
std::uint32_t offset = 0;
for ( const auto &j : rows )
{
if ( j < column_size )
{
table_vectors.witness_values[witness_idx + offset] = table_proxy.witness( i, j );
offset++;
}
}
witness_idx += padded_rows_amount;
}
// public input
auto it_pub_inp = table_vectors.public_input_values.begin();
for ( std::uint32_t i = 0; i < table_proxy.public_inputs_amount(); i++ )
{
FillVectorValue<typename AssignmentTableType::field_type::value_type,
typename crypto3::zk::snark::plonk_column<BlueprintFieldType>>(
table_vectors.public_input_values,
table_proxy.public_input( i ),
it_pub_inp );
it_pub_inp += padded_rows_amount;
}
for ( std::uint32_t i = 0; i < shared_size; i++ )
{
FillVectorValue<typename AssignmentTableType::field_type::value_type,
typename crypto3::zk::snark::plonk_column<BlueprintFieldType>>(
table_vectors.public_input_values,
table_proxy.shared( i ),
it_pub_inp );
it_pub_inp += padded_rows_amount;
}
// constant
std::uint32_t constant_idx = 0;
for ( std::uint32_t i = 0; i < ComponentConstantColumns; i++ )
{
const auto column_size = table_proxy.constant_column_size( i );
std::uint32_t offset = 0;
for ( const auto &j : rows )
{
if ( j < column_size )
{
table_vectors.constant_values[constant_idx + offset] = table_proxy.constant( i, j );
offset++;
}
}
constant_idx += padded_rows_amount;
}
auto it_const = table_vectors.constant_values.begin() + constant_idx;
for ( std::uint32_t i = ComponentConstantColumns; i < table_proxy.constants_amount(); i++ )
{
FillVectorValue<typename AssignmentTableType::field_type::value_type,
typename crypto3::zk::snark::plonk_column<BlueprintFieldType>>(
table_vectors.constant_values,
table_proxy.constant( i ),
it_const );
it_const += padded_rows_amount;
constant_idx += padded_rows_amount;
}
// selector
std::uint32_t selector_idx = 0;
for ( std::uint32_t i = 0; i < ComponentSelectorColumns; i++ )
{
const auto column_size = table_proxy.selector_column_size( i );
std::uint32_t offset = 0;
for ( const auto &j : rows )
{
if ( j < column_size )
{
if ( selector_rows.find( j ) != selector_rows.end() )
{
table_vectors.selector_values[selector_idx + offset] = table_proxy.selector( i, j );
}
offset++;
}
}
selector_idx += padded_rows_amount;
}
auto it_selector = table_vectors.selector_values.begin();
for ( std::uint32_t i = ComponentSelectorColumns; i < table_proxy.selectors_amount(); i++ )
{
FillVectorValue<typename AssignmentTableType::field_type::value_type,
typename crypto3::zk::snark::plonk_column<BlueprintFieldType>>(
table_vectors.selector_values,
table_proxy.selector( i ),
it_selector );
it_selector += padded_rows_amount;
selector_idx += padded_rows_amount;
}
}
return table_vectors;
}
GeniusAssigner::PlonkConstraintSystemType GeniusAssigner::BuildPlonkConstraintSystem(
const nil::blueprint::circuit_proxy<ArithmetizationType> &circuit_proxy,
const AssignmentTableType &table_proxy,
bool rename_required )
{
using variable_type = crypto3::zk::snark::plonk_variable<typename AssignmentTableType::field_type::value_type>;
const auto &gates = circuit_proxy.gates();
const auto &used_gates_idx = circuit_proxy.get_used_gates();
typename ConstraintSystemType::gates_container_type used_gates;
for ( const auto &it : used_gates_idx )
{
used_gates.push_back( gates[it] );
}
const auto ©_constraints = circuit_proxy.copy_constraints();
typename ConstraintSystemType::copy_constraints_container_type used_copy_constraints;
const auto &used_copy_constraints_idx = circuit_proxy.get_used_copy_constraints();
for ( const auto &it : used_copy_constraints_idx )
{
used_copy_constraints.push_back( copy_constraints[it] );
}
if ( rename_required )
{
const auto &used_rows = table_proxy.get_used_rows();
std::uint32_t local_row = 0;
for ( const auto &row : used_rows )
{
for ( auto &constraint : used_copy_constraints )
{
const auto first_var = constraint.first;
const auto second_var = constraint.second;
if ( ( first_var.type == variable_type::column_type::witness ||
first_var.type == variable_type::column_type::constant ) &&
first_var.rotation == static_cast<int32_t>(row) )
{
constraint.first =
variable_type( first_var.index, local_row, first_var.relative, first_var.type );
}
if ( ( second_var.type == variable_type::column_type::witness ||
second_var.type == variable_type::column_type::constant ) &&
second_var.rotation == static_cast<int32_t>(row) )
{
constraint.second =
variable_type( second_var.index, local_row, second_var.relative, second_var.type );
}
}
local_row++;
}
}
const auto &lookup_gates = circuit_proxy.lookup_gates();
typename ConstraintSystemType::lookup_gates_container_type used_lookup_gates;
const auto &used_lookup_gates_idx = circuit_proxy.get_used_lookup_gates();
for ( const auto &it : used_lookup_gates_idx )
{
used_lookup_gates.push_back( lookup_gates[it] );
}
const auto &lookup_tables = circuit_proxy.lookup_tables();
typename ConstraintSystemType::lookup_tables_type used_lookup_tables;
const auto &used_lookup_tables_idx = circuit_proxy.get_used_lookup_tables();
for ( const auto &it : used_lookup_tables_idx )
{
used_lookup_tables.push_back( lookup_tables[it] );
}
auto filled_val = PlonkConstraintSystemType( std::make_tuple(
nil::crypto3::marshalling::types::
fill_plonk_gates<AssignerEndianess, typename ConstraintSystemType::gates_container_type::value_type>(
used_gates ),
nil::crypto3::marshalling::types::fill_plonk_copy_constraints<AssignerEndianess,
typename ConstraintSystemType::variable_type>(
used_copy_constraints ),
nil::crypto3::marshalling::types::fill_plonk_lookup_gates<
AssignerEndianess,
typename ConstraintSystemType::lookup_gates_container_type::value_type>( used_lookup_gates ),
nil::crypto3::marshalling::types::fill_plonk_lookup_tables<
AssignerEndianess,
typename ConstraintSystemType::lookup_tables_type::value_type>( used_lookup_tables ) ) );
return filled_val;
}
template <typename ValueType, typename ContainerType>
void GeniusAssigner::FillVectorValue( std::vector<ValueType> &table_values,
const ContainerType &table_col,
typename std::vector<ValueType>::iterator start )
{
std::copy( table_col.begin(), table_col.end(), start );
}
}
Updated on 2026-03-04 at 13:10:45 -0800