Skip to content

Commit

Permalink
Added basic zkevm circuit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Iluvmagick committed Jun 6, 2024
1 parent 8a3c405 commit 7736a60
Show file tree
Hide file tree
Showing 26 changed files with 2,889 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
${{ github.workspace }}/**
!${{ github.workspace }}/
!${{ github.workspace }}/**/.git/**
# nix is taken from the cloud-init template, no need to install it.
- name: Build and run tests
env:
Expand Down
2 changes: 0 additions & 2 deletions include/nil/blueprint/blueprint/plonk/assignment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,8 @@ namespace nil {
using value_type = typename BlueprintFieldType::value_type;
using column_type = typename crypto3::zk::snark::plonk_column<BlueprintFieldType>;
using shared_container_type = typename std::array<column_type, 1>;

using constant_set_compare_type = detail::constant_batch_ref_compare<BlueprintFieldType>;

std::size_t next_selector_index = 0;
std::uint32_t assignment_allocated_rows = 0;
std::vector<value_type> assignment_private_storage;
// for variables used in component batching
Expand Down
10 changes: 8 additions & 2 deletions include/nil/blueprint/gate_id.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ namespace nil {

std::array<std::array<std::vector<value_type>, 3>, 2> witnesses;
std::array<std::array<std::vector<value_type>, 3>, 2> constants;
std::array<std::array<std::vector<value_type>, 3>, 2> selectors;
// Used to separate constraints from each other in ids.
std::vector<value_type> constraint_mults;
// Used to separate lookup variables from each other in ids.
Expand Down Expand Up @@ -124,6 +125,10 @@ namespace nil {
return get_value_helper(constants, point, index, rotation);
}

value_type get_selector(std::size_t point, std::size_t index, std::size_t rotation) {
return get_value_helper(selectors, point, index, rotation);
}

value_type get_power(std::size_t index) {
return get_power_helper(constraint_mults, index);
}
Expand All @@ -144,9 +149,10 @@ namespace nil {
return this->get_witness(point, var.index, var.rotation);
case var::column_type::constant:
return this->get_constant(point, var.index, var.rotation);
case var::column_type::public_input:
case var::column_type::selector:
BOOST_ASSERT_MSG(false, "Public input/selectors should not be in a gate.");
return this->get_selector(point, var.index, var.rotation);
case var::column_type::public_input:
BOOST_ASSERT_MSG(false, "Public input variables should not be in a gate.");
case var::column_type::uninitialized:
BOOST_ASSERT_MSG(false, "Uninitialized variable should not be inside a gate.");
}
Expand Down
19 changes: 18 additions & 1 deletion include/nil/blueprint/lookup_library.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,14 +244,31 @@ namespace nil {
virtual std::size_t get_columns_number(){return 2;}
virtual std::size_t get_rows_number(){return 5764801;}
};

class chunk_16_bits_table: public lookup_table_definition{
public:
chunk_16_bits_table(): lookup_table_definition("chunk_16_bits"){
this->subtables["full"] = {{0}, 0, 65535};
};
virtual void generate(){
this->_table.resize(1);
for (std::size_t i = 0; i < 65536; i++) {
this->_table[0].push_back(i);
}
}

virtual std::size_t get_columns_number(){return 1;}
virtual std::size_t get_rows_number(){return 65536;}
};
public:
using bimap_type = boost::bimap<boost::bimaps::set_of<std::string>, boost::bimaps::set_of<std::size_t>>;
using left_reserved_type = typename bimap_type::left_map;
using right_reserved_type = typename bimap_type::right_map;

lookup_library(){
lookup_library() {
tables = {};
reserved_all = false;
tables["chunk_16_bits"] = std::shared_ptr<lookup_table_definition>(new chunk_16_bits_table());
tables["binary_xor_table"] = std::shared_ptr<lookup_table_definition>(new binary_xor_table_type());
tables["binary_and_table"] = std::shared_ptr<lookup_table_definition>(new binary_and_table_type());
tables["sha256_sparse_base4"] = std::shared_ptr<lookup_table_definition>(new sparse_values_base4_table());
Expand Down
169 changes: 169 additions & 0 deletions include/nil/blueprint/zkevm/operations/add_sub.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
//---------------------------------------------------------------------------//
// Copyright (c) 2024 Dmitrii Tabalin <[email protected]>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//---------------------------------------------------------------------------//

#pragma once

#include <numeric>
#include <algorithm>

#include <nil/blueprint/zkevm/zkevm_word.hpp>
#include <nil/blueprint/zkevm/zkevm_operation.hpp>

namespace nil {
namespace blueprint {

template<typename BlueprintFieldType>
class zkevm_add_sub_operation : public zkevm_operation<BlueprintFieldType> {
public:
using op_type = zkevm_operation<BlueprintFieldType>;
using gate_class = typename op_type::gate_class;
using constraint_type = typename op_type::constraint_type;
using zkevm_circuit_type = typename op_type::zkevm_circuit_type;
using assignment_type = typename op_type::assignment_type;
using value_type = typename BlueprintFieldType::value_type;
using var = typename op_type::var;

zkevm_add_sub_operation(bool _is_add) : is_add(_is_add) {}

bool is_add;

constexpr static const std::size_t carry_amount = 16 / 3 + 1;
constexpr static const value_type two_16 = 65536;
constexpr static const value_type two_32 = 4294967296;
constexpr static const value_type two_48 = 281474976710656;

std::map<gate_class, std::vector<constraint_type>> generate_gates(zkevm_circuit_type &zkevm_circuit) override {
std::vector<constraint_type> constraints;
constexpr const std::size_t chunk_amount = 16;
const std::vector<std::size_t> &witness_cols = zkevm_circuit.get_opcode_cols();
auto var_gen = [&witness_cols](std::size_t i, int32_t offset = 0) {
return zkevm_operation<BlueprintFieldType>::var_gen(witness_cols, i, offset);
};
constraint_type position = zkevm_circuit.get_opcode_row_constraint(1, this->rows_amount());
auto constraint_gen = [&constraints, &position]
(var a_0, var a_1, var a_2,
var b_0, var b_1, var b_2,
var r_0, var r_1, var r_2,
var last_carry, var result_carry, bool first_constraint = false) {
if (first_constraint) {
// no last carry for first constraint
constraints.push_back(
position * (
(a_0 + b_0) + (a_1 + b_1) * two_16 + (a_2 + b_2) * two_32
- r_0 - r_1 * two_16 - r_2 * two_32 - result_carry * two_48));

} else {
constraints.push_back(
position * (
last_carry + (a_0 + b_0) + (a_1 + b_1) * two_16 + (a_2 + b_2) * two_32
- r_0 - r_1 * two_16 - r_2 * two_32 - result_carry * two_48));
}
constraints.push_back(position * result_carry * (result_carry - 1));
};
auto last_constraint_gen = [&constraints, &position]
(var a_0, var b_0, var r_0, var last_carry, var result_carry) {
constraints.push_back(
position * (last_carry + a_0 + b_0 - r_0 - result_carry * two_16));
constraints.push_back(position * result_carry * (result_carry - 1));
};
std::vector<var> a_chunks;
std::vector<var> b_chunks;
std::vector<var> r_chunks;
for (std::size_t i = 0; i < chunk_amount; i++) {
a_chunks.push_back(var_gen(i, -1));
b_chunks.push_back(var_gen(i, 0));
r_chunks.push_back(var_gen(i, +1));
}
std::vector<var> r_carry;
for (std::size_t i = 0; i < carry_amount; i++) {
r_carry.push_back(var_gen(i + chunk_amount, +1));
}
// special first constraint
constraint_gen(a_chunks[0], a_chunks[1], a_chunks[2],
b_chunks[0], b_chunks[1], b_chunks[2],
r_chunks[0], r_chunks[1], r_chunks[2],
r_carry[0], r_carry[0], true);
for (std::size_t i = 1; i < carry_amount - 1; i++) {
constraint_gen(a_chunks[3 * i], a_chunks[3 * i + 1], a_chunks[3 * i + 2],
b_chunks[3 * i], b_chunks[3 * i + 1], b_chunks[3 * i + 2],
r_chunks[3 * i], r_chunks[3 * i + 1], r_chunks[3 * i + 2],
r_carry[i - 1], r_carry[i]);
}
last_constraint_gen(a_chunks[3 * (carry_amount - 1)], b_chunks[3 * (carry_amount - 1)],
r_chunks[3 * (carry_amount - 1)],
r_carry[carry_amount - 2], r_carry[carry_amount - 1]);
return {{gate_class::MIDDLE_OP, constraints}};
}

void generate_assignments(zkevm_circuit_type &zkevm_circuit, zkevm_machine_interface &machine) override {
zkevm_stack &stack = machine.stack;
using word_type = typename zkevm_stack::word_type;
word_type a = stack.pop();
word_type b = stack.pop();
word_type result = is_add ? a + b : a - b;
// TODO: after memory logic would become more complicated here
if (!is_add) {
std::swap(result, a);
}
const std::vector<value_type> a_chunks = zkevm_word_to_field_element<BlueprintFieldType>(a);
const std::vector<value_type> b_chunks = zkevm_word_to_field_element<BlueprintFieldType>(b);
const std::vector<value_type> r_chunks = zkevm_word_to_field_element<BlueprintFieldType>(result);
const std::vector<std::size_t> &witness_cols = zkevm_circuit.get_opcode_cols();
assignment_type &assignment = zkevm_circuit.get_assignment();
const std::size_t curr_row = zkevm_circuit.get_current_row();
// TODO: replace with memory access, which would also do range checks!
for (std::size_t i = 0; i < a_chunks.size(); i++) {
assignment.witness(witness_cols[i], curr_row) = a_chunks[i];
}
for (std::size_t i = 0; i < b_chunks.size(); i++) {
assignment.witness(witness_cols[i], curr_row + 1) = b_chunks[i];
}
for (std::size_t i = 0; i < r_chunks.size(); i++) {
assignment.witness(witness_cols[i], curr_row + 2) = r_chunks[i];
}
// we might want to pack carries more efficiently?
bool carry = 0;
for (std::size_t i = 0; i < carry_amount - 1; i++) {
carry = (carry + a_chunks[3 * i ] + b_chunks[3 * i ] +
(a_chunks[3 * i + 1] + b_chunks[3 * i + 1]) * two_16 +
(a_chunks[3 * i + 2] + b_chunks[3 * i + 2]) * two_32 ) >= two_48;
assignment.witness(witness_cols[i + a_chunks.size()], curr_row + 2) = carry;
}
carry = (carry + a_chunks[3 * (carry_amount - 1)] + b_chunks[3 * (carry_amount - 1)]) >= two_16;
assignment.witness(witness_cols[a_chunks.size() + carry_amount - 1], curr_row + 2) = carry;
// reset the machine state; hope that we won't have to do this manually
stack.push(b);
if (is_add) {
stack.push(a);
} else {
stack.push(result);
}
}

std::size_t rows_amount() override {
return 3;
}
};
} // namespace blueprint
} // namespace nil
Loading

0 comments on commit 7736a60

Please sign in to comment.