Fix libsnark dependency build.
This changes libsnark to build in-place, instead of copying first to a build directory. Previously, modifications made to the original sources wouldn't get rebuilt without a 'make clean' because users would be pointing to the copies. This closes #2689.
This commit is contained in:
48
src/snark/libsnark/gadgetlib1/constraint_profiling.cpp
Normal file
48
src/snark/libsnark/gadgetlib1/constraint_profiling.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Implementation of interfaces for profiling constraints.
|
||||
|
||||
See constraint_profiling.hpp .
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#include "gadgetlib1/constraint_profiling.hpp"
|
||||
#include "common/profiling.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
size_t constraint_profiling_indent = 0;
|
||||
std::vector<constraint_profiling_entry> constraint_profiling_table;
|
||||
|
||||
size_t PRINT_CONSTRAINT_PROFILING()
|
||||
{
|
||||
size_t accounted = 0;
|
||||
print_indent();
|
||||
printf("Constraint profiling:\n");
|
||||
for (constraint_profiling_entry &ent : constraint_profiling_table)
|
||||
{
|
||||
if (ent.indent == 0)
|
||||
{
|
||||
accounted += ent.count;
|
||||
}
|
||||
|
||||
print_indent();
|
||||
for (size_t i = 0; i < ent.indent; ++i)
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
printf("* Number of constraints in [%s]: %zu\n", ent.annotation.c_str(), ent.count);
|
||||
}
|
||||
|
||||
constraint_profiling_table.clear();
|
||||
constraint_profiling_indent = 0;
|
||||
|
||||
return accounted;
|
||||
}
|
||||
|
||||
}
|
||||
42
src/snark/libsnark/gadgetlib1/constraint_profiling.hpp
Normal file
42
src/snark/libsnark/gadgetlib1/constraint_profiling.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Declaration of interfaces for profiling constraints.
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CONSTRAINT_PROFILING_HPP_
|
||||
#define CONSTRAINT_PROFILING_HPP_
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
extern size_t constraint_profiling_indent;
|
||||
|
||||
struct constraint_profiling_entry {
|
||||
size_t indent;
|
||||
std::string annotation;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
extern std::vector<constraint_profiling_entry> constraint_profiling_table;
|
||||
|
||||
#define PROFILE_CONSTRAINTS(pb, annotation) \
|
||||
for (size_t _num_constraints_before = pb.num_constraints(), _iter = (++constraint_profiling_indent, 0), _cp_pos = constraint_profiling_table.size(); \
|
||||
_iter == 0; \
|
||||
constraint_profiling_table.insert(constraint_profiling_table.begin() + _cp_pos, constraint_profiling_entry{--constraint_profiling_indent, annotation, pb.num_constraints() - _num_constraints_before}), \
|
||||
_iter = 1)
|
||||
|
||||
size_t PRINT_CONSTRAINT_PROFILING(); // returns # of top level constraints
|
||||
|
||||
} // libsnark
|
||||
|
||||
#endif // CONSTRAINT_PROFILING_HPP_
|
||||
23
src/snark/libsnark/gadgetlib1/examples/simple_example.hpp
Normal file
23
src/snark/libsnark/gadgetlib1/examples/simple_example.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SIMPLE_EXAMPLE_HPP_
|
||||
#define SIMPLE_EXAMPLE_HPP_
|
||||
|
||||
#include "examples/r1cs_examples.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
r1cs_example<FieldT> gen_r1cs_example_from_protoboard(const size_t num_constraints,
|
||||
const size_t num_inputs);
|
||||
|
||||
} // libsnark
|
||||
|
||||
#include "gadgetlib1/examples/simple_example.tcc"
|
||||
|
||||
#endif // SIMPLE_EXAMPLE_HPP_
|
||||
54
src/snark/libsnark/gadgetlib1/examples/simple_example.tcc
Normal file
54
src/snark/libsnark/gadgetlib1/examples/simple_example.tcc
Normal file
@@ -0,0 +1,54 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SIMPLE_EXAMPLE_TCC_
|
||||
#define SIMPLE_EXAMPLE_TCC_
|
||||
|
||||
#include <cassert>
|
||||
#include "gadgetlib1/gadgets/basic_gadgets.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
/* NOTE: all examples here actually generate one constraint less to account for soundness constraint in QAP */
|
||||
|
||||
template<typename FieldT>
|
||||
r1cs_example<FieldT> gen_r1cs_example_from_protoboard(const size_t num_constraints,
|
||||
const size_t num_inputs)
|
||||
{
|
||||
const size_t new_num_constraints = num_constraints - 1;
|
||||
|
||||
/* construct dummy example: inner products of two vectors */
|
||||
protoboard<FieldT> pb;
|
||||
pb_variable_array<FieldT> A;
|
||||
pb_variable_array<FieldT> B;
|
||||
pb_variable<FieldT> res;
|
||||
|
||||
// the variables on the protoboard are (ONE (constant 1 term), res, A[0], ..., A[num_constraints-1], B[0], ..., B[num_constraints-1])
|
||||
res.allocate(pb, "res");
|
||||
A.allocate(pb, new_num_constraints, "A");
|
||||
B.allocate(pb, new_num_constraints, "B");
|
||||
|
||||
inner_product_gadget<FieldT> compute_inner_product(pb, A, B, res, "compute_inner_product");
|
||||
compute_inner_product.generate_r1cs_constraints();
|
||||
|
||||
/* fill in random example */
|
||||
for (size_t i = 0; i < new_num_constraints; ++i)
|
||||
{
|
||||
pb.val(A[i]) = FieldT::random_element();
|
||||
pb.val(B[i]) = FieldT::random_element();
|
||||
}
|
||||
|
||||
compute_inner_product.generate_r1cs_witness();
|
||||
|
||||
pb.constraint_system.num_inputs = num_inputs;
|
||||
const r1cs_variable_assignment<FieldT> va = pb.values;
|
||||
const r1cs_variable_assignment<FieldT> input(va.begin(), va.begin() + num_inputs);
|
||||
return r1cs_example<FieldT>(pb.constraint_system, input, va, num_inputs);
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // R1CS_EXAMPLES_TCC_
|
||||
27
src/snark/libsnark/gadgetlib1/gadget.hpp
Normal file
27
src/snark/libsnark/gadgetlib1/gadget.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef GADGET_HPP_
|
||||
#define GADGET_HPP_
|
||||
|
||||
#include "gadgetlib1/protoboard.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
class gadget {
|
||||
protected:
|
||||
protoboard<FieldT> &pb;
|
||||
const std::string annotation_prefix;
|
||||
public:
|
||||
gadget(protoboard<FieldT> &pb, const std::string &annotation_prefix="");
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
#include "gadgetlib1/gadget.tcc"
|
||||
|
||||
#endif // GADGET_HPP_
|
||||
23
src/snark/libsnark/gadgetlib1/gadget.tcc
Normal file
23
src/snark/libsnark/gadgetlib1/gadget.tcc
Normal file
@@ -0,0 +1,23 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef GADGET_TCC_
|
||||
#define GADGET_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
gadget<FieldT>::gadget(protoboard<FieldT> &pb, const std::string &annotation_prefix) :
|
||||
pb(pb), annotation_prefix(annotation_prefix)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assert(annotation_prefix != "");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // GADGET_TCC_
|
||||
351
src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.hpp
Normal file
351
src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.hpp
Normal file
@@ -0,0 +1,351 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef BASIC_GADGETS_HPP_
|
||||
#define BASIC_GADGETS_HPP_
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "gadgetlib1/gadget.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
/* forces lc to take value 0 or 1 by adding constraint lc * (1-lc) = 0 */
|
||||
template<typename FieldT>
|
||||
void generate_boolean_r1cs_constraint(protoboard<FieldT> &pb, const pb_linear_combination<FieldT> &lc, const std::string &annotation_prefix="");
|
||||
|
||||
template<typename FieldT>
|
||||
void generate_r1cs_equals_const_constraint(protoboard<FieldT> &pb, const pb_linear_combination<FieldT> &lc, const FieldT& c, const std::string &annotation_prefix="");
|
||||
|
||||
template<typename FieldT>
|
||||
class packing_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
/* no internal variables */
|
||||
public:
|
||||
const pb_linear_combination_array<FieldT> bits;
|
||||
const pb_linear_combination<FieldT> packed;
|
||||
|
||||
packing_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &bits,
|
||||
const pb_linear_combination<FieldT> &packed,
|
||||
const std::string &annotation_prefix="") :
|
||||
gadget<FieldT>(pb, annotation_prefix), bits(bits), packed(packed) {}
|
||||
|
||||
void generate_r1cs_constraints(const bool enforce_bitness);
|
||||
/* adds constraint result = \sum bits[i] * 2^i */
|
||||
|
||||
void generate_r1cs_witness_from_packed();
|
||||
void generate_r1cs_witness_from_bits();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
class multipacking_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
std::vector<packing_gadget<FieldT> > packers;
|
||||
public:
|
||||
const pb_linear_combination_array<FieldT> bits;
|
||||
const pb_linear_combination_array<FieldT> packed_vars;
|
||||
|
||||
const size_t chunk_size;
|
||||
const size_t num_chunks;
|
||||
// const size_t last_chunk_size;
|
||||
|
||||
multipacking_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &bits,
|
||||
const pb_linear_combination_array<FieldT> &packed_vars,
|
||||
const size_t chunk_size,
|
||||
const std::string &annotation_prefix="");
|
||||
void generate_r1cs_constraints(const bool enforce_bitness);
|
||||
void generate_r1cs_witness_from_packed();
|
||||
void generate_r1cs_witness_from_bits();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
class field_vector_copy_gadget : public gadget<FieldT> {
|
||||
public:
|
||||
const pb_variable_array<FieldT> source;
|
||||
const pb_variable_array<FieldT> target;
|
||||
const pb_linear_combination<FieldT> do_copy;
|
||||
|
||||
field_vector_copy_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable_array<FieldT> &source,
|
||||
const pb_variable_array<FieldT> &target,
|
||||
const pb_linear_combination<FieldT> &do_copy,
|
||||
const std::string &annotation_prefix="");
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
class bit_vector_copy_gadget : public gadget<FieldT> {
|
||||
public:
|
||||
const pb_variable_array<FieldT> source_bits;
|
||||
const pb_variable_array<FieldT> target_bits;
|
||||
const pb_linear_combination<FieldT> do_copy;
|
||||
|
||||
pb_variable_array<FieldT> packed_source;
|
||||
pb_variable_array<FieldT> packed_target;
|
||||
|
||||
std::shared_ptr<multipacking_gadget<FieldT> > pack_source;
|
||||
std::shared_ptr<multipacking_gadget<FieldT> > pack_target;
|
||||
std::shared_ptr<field_vector_copy_gadget<FieldT> > copier;
|
||||
|
||||
const size_t chunk_size;
|
||||
const size_t num_chunks;
|
||||
|
||||
bit_vector_copy_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable_array<FieldT> &source_bits,
|
||||
const pb_variable_array<FieldT> &target_bits,
|
||||
const pb_linear_combination<FieldT> &do_copy,
|
||||
const size_t chunk_size,
|
||||
const std::string &annotation_prefix="");
|
||||
void generate_r1cs_constraints(const bool enforce_source_bitness, const bool enforce_target_bitness);
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
class dual_variable_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
std::shared_ptr<packing_gadget<FieldT> > consistency_check;
|
||||
public:
|
||||
pb_variable<FieldT> packed;
|
||||
pb_variable_array<FieldT> bits;
|
||||
|
||||
dual_variable_gadget(protoboard<FieldT> &pb,
|
||||
const size_t width,
|
||||
const std::string &annotation_prefix="") :
|
||||
gadget<FieldT>(pb, annotation_prefix)
|
||||
{
|
||||
packed.allocate(pb, FMT(this->annotation_prefix, " packed"));
|
||||
bits.allocate(pb, width, FMT(this->annotation_prefix, " bits"));
|
||||
consistency_check.reset(new packing_gadget<FieldT>(pb,
|
||||
bits,
|
||||
packed,
|
||||
FMT(this->annotation_prefix, " consistency_check")));
|
||||
}
|
||||
|
||||
dual_variable_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable_array<FieldT> &bits,
|
||||
const std::string &annotation_prefix="") :
|
||||
gadget<FieldT>(pb, annotation_prefix), bits(bits)
|
||||
{
|
||||
packed.allocate(pb, FMT(this->annotation_prefix, " packed"));
|
||||
consistency_check.reset(new packing_gadget<FieldT>(pb,
|
||||
bits,
|
||||
packed,
|
||||
FMT(this->annotation_prefix, " consistency_check")));
|
||||
}
|
||||
|
||||
dual_variable_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable<FieldT> &packed,
|
||||
const size_t width,
|
||||
const std::string &annotation_prefix="") :
|
||||
gadget<FieldT>(pb, annotation_prefix), packed(packed)
|
||||
{
|
||||
bits.allocate(pb, width, FMT(this->annotation_prefix, " bits"));
|
||||
consistency_check.reset(new packing_gadget<FieldT>(pb,
|
||||
bits,
|
||||
packed,
|
||||
FMT(this->annotation_prefix, " consistency_check")));
|
||||
}
|
||||
|
||||
void generate_r1cs_constraints(const bool enforce_bitness);
|
||||
void generate_r1cs_witness_from_packed();
|
||||
void generate_r1cs_witness_from_bits();
|
||||
};
|
||||
|
||||
/*
|
||||
the gadgets below are Fp specific:
|
||||
I * X = R
|
||||
(1-R) * X = 0
|
||||
|
||||
if X = 0 then R = 0
|
||||
if X != 0 then R = 1 and I = X^{-1}
|
||||
*/
|
||||
|
||||
template<typename FieldT>
|
||||
class disjunction_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
pb_variable<FieldT> inv;
|
||||
public:
|
||||
const pb_variable_array<FieldT> inputs;
|
||||
const pb_variable<FieldT> output;
|
||||
|
||||
disjunction_gadget(protoboard<FieldT>& pb,
|
||||
const pb_variable_array<FieldT> &inputs,
|
||||
const pb_variable<FieldT> &output,
|
||||
const std::string &annotation_prefix="") :
|
||||
gadget<FieldT>(pb, annotation_prefix), inputs(inputs), output(output)
|
||||
{
|
||||
assert(inputs.size() >= 1);
|
||||
inv.allocate(pb, FMT(this->annotation_prefix, " inv"));
|
||||
}
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
void test_disjunction_gadget(const size_t n);
|
||||
|
||||
template<typename FieldT>
|
||||
class conjunction_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
pb_variable<FieldT> inv;
|
||||
public:
|
||||
const pb_variable_array<FieldT> inputs;
|
||||
const pb_variable<FieldT> output;
|
||||
|
||||
conjunction_gadget(protoboard<FieldT>& pb,
|
||||
const pb_variable_array<FieldT> &inputs,
|
||||
const pb_variable<FieldT> &output,
|
||||
const std::string &annotation_prefix="") :
|
||||
gadget<FieldT>(pb, annotation_prefix), inputs(inputs), output(output)
|
||||
{
|
||||
assert(inputs.size() >= 1);
|
||||
inv.allocate(pb, FMT(this->annotation_prefix, " inv"));
|
||||
}
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
void test_conjunction_gadget(const size_t n);
|
||||
|
||||
template<typename FieldT>
|
||||
class comparison_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
pb_variable_array<FieldT> alpha;
|
||||
pb_variable<FieldT> alpha_packed;
|
||||
std::shared_ptr<packing_gadget<FieldT> > pack_alpha;
|
||||
|
||||
std::shared_ptr<disjunction_gadget<FieldT> > all_zeros_test;
|
||||
pb_variable<FieldT> not_all_zeros;
|
||||
public:
|
||||
const size_t n;
|
||||
const pb_linear_combination<FieldT> A;
|
||||
const pb_linear_combination<FieldT> B;
|
||||
const pb_variable<FieldT> less;
|
||||
const pb_variable<FieldT> less_or_eq;
|
||||
|
||||
comparison_gadget(protoboard<FieldT>& pb,
|
||||
const size_t n,
|
||||
const pb_linear_combination<FieldT> &A,
|
||||
const pb_linear_combination<FieldT> &B,
|
||||
const pb_variable<FieldT> &less,
|
||||
const pb_variable<FieldT> &less_or_eq,
|
||||
const std::string &annotation_prefix="") :
|
||||
gadget<FieldT>(pb, annotation_prefix), n(n), A(A), B(B), less(less), less_or_eq(less_or_eq)
|
||||
{
|
||||
alpha.allocate(pb, n, FMT(this->annotation_prefix, " alpha"));
|
||||
alpha.emplace_back(less_or_eq); // alpha[n] is less_or_eq
|
||||
|
||||
alpha_packed.allocate(pb, FMT(this->annotation_prefix, " alpha_packed"));
|
||||
not_all_zeros.allocate(pb, FMT(this->annotation_prefix, " not_all_zeros"));
|
||||
|
||||
pack_alpha.reset(new packing_gadget<FieldT>(pb, alpha, alpha_packed,
|
||||
FMT(this->annotation_prefix, " pack_alpha")));
|
||||
|
||||
all_zeros_test.reset(new disjunction_gadget<FieldT>(pb,
|
||||
pb_variable_array<FieldT>(alpha.begin(), alpha.begin() + n),
|
||||
not_all_zeros,
|
||||
FMT(this->annotation_prefix, " all_zeros_test")));
|
||||
};
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
void test_comparison_gadget(const size_t n);
|
||||
|
||||
template<typename FieldT>
|
||||
class inner_product_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
/* S_i = \sum_{k=0}^{i+1} A[i] * B[i] */
|
||||
pb_variable_array<FieldT> S;
|
||||
public:
|
||||
const pb_linear_combination_array<FieldT> A;
|
||||
const pb_linear_combination_array<FieldT> B;
|
||||
const pb_variable<FieldT> result;
|
||||
|
||||
inner_product_gadget(protoboard<FieldT>& pb,
|
||||
const pb_linear_combination_array<FieldT> &A,
|
||||
const pb_linear_combination_array<FieldT> &B,
|
||||
const pb_variable<FieldT> &result,
|
||||
const std::string &annotation_prefix="") :
|
||||
gadget<FieldT>(pb, annotation_prefix), A(A), B(B), result(result)
|
||||
{
|
||||
assert(A.size() >= 1);
|
||||
assert(A.size() == B.size());
|
||||
|
||||
S.allocate(pb, A.size()-1, FMT(this->annotation_prefix, " S"));
|
||||
}
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
void test_inner_product_gadget(const size_t n);
|
||||
|
||||
template<typename FieldT>
|
||||
class loose_multiplexing_gadget : public gadget<FieldT> {
|
||||
/*
|
||||
this implements loose multiplexer:
|
||||
index not in bounds -> success_flag = 0
|
||||
index in bounds && success_flag = 1 -> result is correct
|
||||
however if index is in bounds we can also set success_flag to 0 (and then result will be forced to be 0)
|
||||
*/
|
||||
public:
|
||||
pb_variable_array<FieldT> alpha;
|
||||
private:
|
||||
std::shared_ptr<inner_product_gadget<FieldT> > compute_result;
|
||||
public:
|
||||
const pb_linear_combination_array<FieldT> arr;
|
||||
const pb_variable<FieldT> index;
|
||||
const pb_variable<FieldT> result;
|
||||
const pb_variable<FieldT> success_flag;
|
||||
|
||||
loose_multiplexing_gadget(protoboard<FieldT>& pb,
|
||||
const pb_linear_combination_array<FieldT> &arr,
|
||||
const pb_variable<FieldT> &index,
|
||||
const pb_variable<FieldT> &result,
|
||||
const pb_variable<FieldT> &success_flag,
|
||||
const std::string &annotation_prefix="") :
|
||||
gadget<FieldT>(pb, annotation_prefix), arr(arr), index(index), result(result), success_flag(success_flag)
|
||||
{
|
||||
alpha.allocate(pb, arr.size(), FMT(this->annotation_prefix, " alpha"));
|
||||
compute_result.reset(new inner_product_gadget<FieldT>(pb, alpha, arr, result, FMT(this->annotation_prefix, " compute_result")));
|
||||
};
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
void test_loose_multiplexing_gadget(const size_t n);
|
||||
|
||||
template<typename FieldT, typename VarT>
|
||||
void create_linear_combination_constraints(protoboard<FieldT> &pb,
|
||||
const std::vector<FieldT> &base,
|
||||
const std::vector<std::pair<VarT, FieldT> > &v,
|
||||
const VarT &target,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
template<typename FieldT, typename VarT>
|
||||
void create_linear_combination_witness(protoboard<FieldT> &pb,
|
||||
const std::vector<FieldT> &base,
|
||||
const std::vector<std::pair<VarT, FieldT> > &v,
|
||||
const VarT &target);
|
||||
|
||||
} // libsnark
|
||||
#include "gadgetlib1/gadgets/basic_gadgets.tcc"
|
||||
|
||||
#endif // BASIC_GADGETS_HPP_
|
||||
705
src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.tcc
Normal file
705
src/snark/libsnark/gadgetlib1/gadgets/basic_gadgets.tcc
Normal file
@@ -0,0 +1,705 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef BASIC_GADGETS_TCC_
|
||||
#define BASIC_GADGETS_TCC_
|
||||
|
||||
#include "common/profiling.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
void generate_boolean_r1cs_constraint(protoboard<FieldT> &pb, const pb_linear_combination<FieldT> &lc, const std::string &annotation_prefix)
|
||||
/* forces lc to take value 0 or 1 by adding constraint lc * (1-lc) = 0 */
|
||||
{
|
||||
pb.add_r1cs_constraint(r1cs_constraint<FieldT>(lc, 1-lc, 0),
|
||||
FMT(annotation_prefix, " boolean_r1cs_constraint"));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void generate_r1cs_equals_const_constraint(protoboard<FieldT> &pb, const pb_linear_combination<FieldT> &lc, const FieldT& c, const std::string &annotation_prefix)
|
||||
{
|
||||
pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1, lc, c),
|
||||
FMT(annotation_prefix, " constness_constraint"));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void packing_gadget<FieldT>::generate_r1cs_constraints(const bool enforce_bitness)
|
||||
/* adds constraint result = \sum bits[i] * 2^i */
|
||||
{
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1, pb_packing_sum<FieldT>(bits), packed), FMT(this->annotation_prefix, " packing_constraint"));
|
||||
|
||||
if (enforce_bitness)
|
||||
{
|
||||
for (size_t i = 0; i < bits.size(); ++i)
|
||||
{
|
||||
generate_boolean_r1cs_constraint<FieldT>(this->pb, bits[i], FMT(this->annotation_prefix, " bitness_%zu", i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void packing_gadget<FieldT>::generate_r1cs_witness_from_packed()
|
||||
{
|
||||
packed.evaluate(this->pb);
|
||||
assert(this->pb.lc_val(packed).as_bigint().num_bits() <= bits.size());
|
||||
bits.fill_with_bits_of_field_element(this->pb, this->pb.lc_val(packed));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void packing_gadget<FieldT>::generate_r1cs_witness_from_bits()
|
||||
{
|
||||
bits.evaluate(this->pb);
|
||||
this->pb.lc_val(packed) = bits.get_field_element_from_bits(this->pb);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
multipacking_gadget<FieldT>::multipacking_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &bits,
|
||||
const pb_linear_combination_array<FieldT> &packed_vars,
|
||||
const size_t chunk_size,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix), bits(bits), packed_vars(packed_vars),
|
||||
chunk_size(chunk_size),
|
||||
num_chunks(div_ceil(bits.size(), chunk_size))
|
||||
// last_chunk_size(bits.size() - (num_chunks-1) * chunk_size)
|
||||
{
|
||||
assert(packed_vars.size() == num_chunks);
|
||||
for (size_t i = 0; i < num_chunks; ++i)
|
||||
{
|
||||
packers.emplace_back(packing_gadget<FieldT>(this->pb, pb_linear_combination_array<FieldT>(bits.begin() + i * chunk_size,
|
||||
bits.begin() + std::min((i+1) * chunk_size, bits.size())),
|
||||
packed_vars[i], FMT(this->annotation_prefix, " packers_%zu", i)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void multipacking_gadget<FieldT>::generate_r1cs_constraints(const bool enforce_bitness)
|
||||
{
|
||||
for (size_t i = 0; i < num_chunks; ++i)
|
||||
{
|
||||
packers[i].generate_r1cs_constraints(enforce_bitness);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void multipacking_gadget<FieldT>::generate_r1cs_witness_from_packed()
|
||||
{
|
||||
for (size_t i = 0; i < num_chunks; ++i)
|
||||
{
|
||||
packers[i].generate_r1cs_witness_from_packed();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void multipacking_gadget<FieldT>::generate_r1cs_witness_from_bits()
|
||||
{
|
||||
for (size_t i = 0; i < num_chunks; ++i)
|
||||
{
|
||||
packers[i].generate_r1cs_witness_from_bits();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
size_t multipacking_num_chunks(const size_t num_bits)
|
||||
{
|
||||
return div_ceil(num_bits, FieldT::capacity());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
field_vector_copy_gadget<FieldT>::field_vector_copy_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable_array<FieldT> &source,
|
||||
const pb_variable_array<FieldT> &target,
|
||||
const pb_linear_combination<FieldT> &do_copy,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix), source(source), target(target), do_copy(do_copy)
|
||||
{
|
||||
assert(source.size() == target.size());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void field_vector_copy_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < source.size(); ++i)
|
||||
{
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(do_copy, source[i] - target[i], 0),
|
||||
FMT(this->annotation_prefix, " copying_check_%zu", i));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void field_vector_copy_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
do_copy.evaluate(this->pb);
|
||||
assert(this->pb.lc_val(do_copy) == FieldT::one() || this->pb.lc_val(do_copy) == FieldT::zero());
|
||||
if (this->pb.lc_val(do_copy) != FieldT::zero())
|
||||
{
|
||||
for (size_t i = 0; i < source.size(); ++i)
|
||||
{
|
||||
this->pb.val(target[i]) = this->pb.val(source[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector_copy_gadget<FieldT>::bit_vector_copy_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable_array<FieldT> &source_bits,
|
||||
const pb_variable_array<FieldT> &target_bits,
|
||||
const pb_linear_combination<FieldT> &do_copy,
|
||||
const size_t chunk_size,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix), source_bits(source_bits), target_bits(target_bits), do_copy(do_copy),
|
||||
chunk_size(chunk_size), num_chunks(div_ceil(source_bits.size(), chunk_size))
|
||||
{
|
||||
assert(source_bits.size() == target_bits.size());
|
||||
|
||||
packed_source.allocate(pb, num_chunks, FMT(annotation_prefix, " packed_source"));
|
||||
pack_source.reset(new multipacking_gadget<FieldT>(pb, source_bits, packed_source, chunk_size, FMT(annotation_prefix, " pack_source")));
|
||||
|
||||
packed_target.allocate(pb, num_chunks, FMT(annotation_prefix, " packed_target"));
|
||||
pack_target.reset(new multipacking_gadget<FieldT>(pb, target_bits, packed_target, chunk_size, FMT(annotation_prefix, " pack_target")));
|
||||
|
||||
copier.reset(new field_vector_copy_gadget<FieldT>(pb, packed_source, packed_target, do_copy, FMT(annotation_prefix, " copier")));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void bit_vector_copy_gadget<FieldT>::generate_r1cs_constraints(const bool enforce_source_bitness, const bool enforce_target_bitness)
|
||||
{
|
||||
pack_source->generate_r1cs_constraints(enforce_source_bitness);
|
||||
pack_target->generate_r1cs_constraints(enforce_target_bitness);
|
||||
|
||||
copier->generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void bit_vector_copy_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
do_copy.evaluate(this->pb);
|
||||
assert(this->pb.lc_val(do_copy) == FieldT::zero() || this->pb.lc_val(do_copy) == FieldT::one());
|
||||
if (this->pb.lc_val(do_copy) == FieldT::one())
|
||||
{
|
||||
for (size_t i = 0; i < source_bits.size(); ++i)
|
||||
{
|
||||
this->pb.val(target_bits[i]) = this->pb.val(source_bits[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pack_source->generate_r1cs_witness_from_bits();
|
||||
pack_target->generate_r1cs_witness_from_bits();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void dual_variable_gadget<FieldT>::generate_r1cs_constraints(const bool enforce_bitness)
|
||||
{
|
||||
consistency_check->generate_r1cs_constraints(enforce_bitness);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void dual_variable_gadget<FieldT>::generate_r1cs_witness_from_packed()
|
||||
{
|
||||
consistency_check->generate_r1cs_witness_from_packed();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void dual_variable_gadget<FieldT>::generate_r1cs_witness_from_bits()
|
||||
{
|
||||
consistency_check->generate_r1cs_witness_from_bits();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void disjunction_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
/* inv * sum = output */
|
||||
linear_combination<FieldT> a1, b1, c1;
|
||||
a1.add_term(inv);
|
||||
for (size_t i = 0; i < inputs.size(); ++i)
|
||||
{
|
||||
b1.add_term(inputs[i]);
|
||||
}
|
||||
c1.add_term(output);
|
||||
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(a1, b1, c1), FMT(this->annotation_prefix, " inv*sum=output"));
|
||||
|
||||
/* (1-output) * sum = 0 */
|
||||
linear_combination<FieldT> a2, b2, c2;
|
||||
a2.add_term(ONE);
|
||||
a2.add_term(output, -1);
|
||||
for (size_t i = 0; i < inputs.size(); ++i)
|
||||
{
|
||||
b2.add_term(inputs[i]);
|
||||
}
|
||||
c2.add_term(ONE, 0);
|
||||
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(a2, b2, c2), FMT(this->annotation_prefix, " (1-output)*sum=0"));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void disjunction_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
FieldT sum = FieldT::zero();
|
||||
|
||||
for (size_t i = 0; i < inputs.size(); ++i)
|
||||
{
|
||||
sum += this->pb.val(inputs[i]);
|
||||
}
|
||||
|
||||
if (sum.is_zero())
|
||||
{
|
||||
this->pb.val(inv) = FieldT::zero();
|
||||
this->pb.val(output) = FieldT::zero();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->pb.val(inv) = sum.inverse();
|
||||
this->pb.val(output) = FieldT::one();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_disjunction_gadget(const size_t n)
|
||||
{
|
||||
printf("testing disjunction_gadget on all %zu bit strings\n", n);
|
||||
|
||||
protoboard<FieldT> pb;
|
||||
pb_variable_array<FieldT> inputs;
|
||||
inputs.allocate(pb, n, "inputs");
|
||||
|
||||
pb_variable<FieldT> output;
|
||||
output.allocate(pb, "output");
|
||||
|
||||
disjunction_gadget<FieldT> d(pb, inputs, output, "d");
|
||||
d.generate_r1cs_constraints();
|
||||
|
||||
for (size_t w = 0; w < 1ul<<n; ++w)
|
||||
{
|
||||
for (size_t j = 0; j < n; ++j)
|
||||
{
|
||||
pb.val(inputs[j]) = FieldT((w & (1ul<<j)) ? 1 : 0);
|
||||
}
|
||||
|
||||
d.generate_r1cs_witness();
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("positive test for %zu\n", w);
|
||||
#endif
|
||||
assert(pb.val(output) == (w ? FieldT::one() : FieldT::zero()));
|
||||
assert(pb.is_satisfied());
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("negative test for %zu\n", w);
|
||||
#endif
|
||||
pb.val(output) = (w ? FieldT::zero() : FieldT::one());
|
||||
assert(!pb.is_satisfied());
|
||||
}
|
||||
|
||||
print_time("disjunction tests successful");
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void conjunction_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
/* inv * (n-sum) = 1-output */
|
||||
linear_combination<FieldT> a1, b1, c1;
|
||||
a1.add_term(inv);
|
||||
b1.add_term(ONE, inputs.size());
|
||||
for (size_t i = 0; i < inputs.size(); ++i)
|
||||
{
|
||||
b1.add_term(inputs[i], -1);
|
||||
}
|
||||
c1.add_term(ONE);
|
||||
c1.add_term(output, -1);
|
||||
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(a1, b1, c1), FMT(this->annotation_prefix, " inv*(n-sum)=(1-output)"));
|
||||
|
||||
/* output * (n-sum) = 0 */
|
||||
linear_combination<FieldT> a2, b2, c2;
|
||||
a2.add_term(output);
|
||||
b2.add_term(ONE, inputs.size());
|
||||
for (size_t i = 0; i < inputs.size(); ++i)
|
||||
{
|
||||
b2.add_term(inputs[i], -1);
|
||||
}
|
||||
c2.add_term(ONE, 0);
|
||||
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(a2, b2, c2), FMT(this->annotation_prefix, " output*(n-sum)=0"));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void conjunction_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
FieldT sum = FieldT(inputs.size());
|
||||
|
||||
for (size_t i = 0; i < inputs.size(); ++i)
|
||||
{
|
||||
sum -= this->pb.val(inputs[i]);
|
||||
}
|
||||
|
||||
if (sum.is_zero())
|
||||
{
|
||||
this->pb.val(inv) = FieldT::zero();
|
||||
this->pb.val(output) = FieldT::one();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->pb.val(inv) = sum.inverse();
|
||||
this->pb.val(output) = FieldT::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_conjunction_gadget(const size_t n)
|
||||
{
|
||||
printf("testing conjunction_gadget on all %zu bit strings\n", n);
|
||||
|
||||
protoboard<FieldT> pb;
|
||||
pb_variable_array<FieldT> inputs;
|
||||
inputs.allocate(pb, n, "inputs");
|
||||
|
||||
pb_variable<FieldT> output;
|
||||
output.allocate(pb, "output");
|
||||
|
||||
conjunction_gadget<FieldT> c(pb, inputs, output, "c");
|
||||
c.generate_r1cs_constraints();
|
||||
|
||||
for (size_t w = 0; w < 1ul<<n; ++w)
|
||||
{
|
||||
for (size_t j = 0; j < n; ++j)
|
||||
{
|
||||
pb.val(inputs[j]) = (w & (1ul<<j)) ? FieldT::one() : FieldT::zero();
|
||||
}
|
||||
|
||||
c.generate_r1cs_witness();
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("positive test for %zu\n", w);
|
||||
#endif
|
||||
assert(pb.val(output) == (w == (1ul<<n) - 1 ? FieldT::one() : FieldT::zero()));
|
||||
assert(pb.is_satisfied());
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("negative test for %zu\n", w);
|
||||
#endif
|
||||
pb.val(output) = (w == (1ul<<n) - 1 ? FieldT::zero() : FieldT::one());
|
||||
assert(!pb.is_satisfied());
|
||||
}
|
||||
|
||||
print_time("conjunction tests successful");
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void comparison_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
/*
|
||||
packed(alpha) = 2^n + B - A
|
||||
|
||||
not_all_zeros = \bigvee_{i=0}^{n-1} alpha_i
|
||||
|
||||
if B - A > 0, then 2^n + B - A > 2^n,
|
||||
so alpha_n = 1 and not_all_zeros = 1
|
||||
if B - A = 0, then 2^n + B - A = 2^n,
|
||||
so alpha_n = 1 and not_all_zeros = 0
|
||||
if B - A < 0, then 2^n + B - A \in {0, 1, \ldots, 2^n-1},
|
||||
so alpha_n = 0
|
||||
|
||||
therefore alpha_n = less_or_eq and alpha_n * not_all_zeros = less
|
||||
*/
|
||||
|
||||
/* not_all_zeros to be Boolean, alpha_i are Boolean by packing gadget */
|
||||
generate_boolean_r1cs_constraint<FieldT>(this->pb, not_all_zeros,
|
||||
FMT(this->annotation_prefix, " not_all_zeros"));
|
||||
|
||||
/* constraints for packed(alpha) = 2^n + B - A */
|
||||
pack_alpha->generate_r1cs_constraints(true);
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1, (FieldT(2)^n) + B - A, alpha_packed), FMT(this->annotation_prefix, " main_constraint"));
|
||||
|
||||
/* compute result */
|
||||
all_zeros_test->generate_r1cs_constraints();
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(less_or_eq, not_all_zeros, less),
|
||||
FMT(this->annotation_prefix, " less"));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void comparison_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
A.evaluate(this->pb);
|
||||
B.evaluate(this->pb);
|
||||
|
||||
/* unpack 2^n + B - A into alpha_packed */
|
||||
this->pb.val(alpha_packed) = (FieldT(2)^n) + this->pb.lc_val(B) - this->pb.lc_val(A);
|
||||
pack_alpha->generate_r1cs_witness_from_packed();
|
||||
|
||||
/* compute result */
|
||||
all_zeros_test->generate_r1cs_witness();
|
||||
this->pb.val(less) = this->pb.val(less_or_eq) * this->pb.val(not_all_zeros);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_comparison_gadget(const size_t n)
|
||||
{
|
||||
printf("testing comparison_gadget on all %zu bit inputs\n", n);
|
||||
|
||||
protoboard<FieldT> pb;
|
||||
|
||||
pb_variable<FieldT> A, B, less, less_or_eq;
|
||||
A.allocate(pb, "A");
|
||||
B.allocate(pb, "B");
|
||||
less.allocate(pb, "less");
|
||||
less_or_eq.allocate(pb, "less_or_eq");
|
||||
|
||||
comparison_gadget<FieldT> cmp(pb, n, A, B, less, less_or_eq, "cmp");
|
||||
cmp.generate_r1cs_constraints();
|
||||
|
||||
for (size_t a = 0; a < 1ul<<n; ++a)
|
||||
{
|
||||
for (size_t b = 0; b < 1ul<<n; ++b)
|
||||
{
|
||||
pb.val(A) = FieldT(a);
|
||||
pb.val(B) = FieldT(b);
|
||||
|
||||
cmp.generate_r1cs_witness();
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("positive test for %zu < %zu\n", a, b);
|
||||
#endif
|
||||
assert(pb.val(less) == (a < b ? FieldT::one() : FieldT::zero()));
|
||||
assert(pb.val(less_or_eq) == (a <= b ? FieldT::one() : FieldT::zero()));
|
||||
assert(pb.is_satisfied());
|
||||
}
|
||||
}
|
||||
|
||||
print_time("comparison tests successful");
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void inner_product_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
/*
|
||||
S_i = \sum_{k=0}^{i+1} A[i] * B[i]
|
||||
S[0] = A[0] * B[0]
|
||||
S[i+1] - S[i] = A[i] * B[i]
|
||||
*/
|
||||
for (size_t i = 0; i < A.size(); ++i)
|
||||
{
|
||||
this->pb.add_r1cs_constraint(
|
||||
r1cs_constraint<FieldT>(A[i], B[i],
|
||||
(i == A.size()-1 ? result : S[i]) + (i == 0 ? 0 * ONE : -S[i-1])),
|
||||
FMT(this->annotation_prefix, " S_%zu", i));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void inner_product_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
FieldT total = FieldT::zero();
|
||||
for (size_t i = 0; i < A.size(); ++i)
|
||||
{
|
||||
A[i].evaluate(this->pb);
|
||||
B[i].evaluate(this->pb);
|
||||
|
||||
total += this->pb.lc_val(A[i]) * this->pb.lc_val(B[i]);
|
||||
this->pb.val(i == A.size()-1 ? result : S[i]) = total;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_inner_product_gadget(const size_t n)
|
||||
{
|
||||
printf("testing inner_product_gadget on all %zu bit strings\n", n);
|
||||
|
||||
protoboard<FieldT> pb;
|
||||
pb_variable_array<FieldT> A;
|
||||
A.allocate(pb, n, "A");
|
||||
pb_variable_array<FieldT> B;
|
||||
B.allocate(pb, n, "B");
|
||||
|
||||
pb_variable<FieldT> result;
|
||||
result.allocate(pb, "result");
|
||||
|
||||
inner_product_gadget<FieldT> g(pb, A, B, result, "g");
|
||||
g.generate_r1cs_constraints();
|
||||
|
||||
for (size_t i = 0; i < 1ul<<n; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < 1ul<<n; ++j)
|
||||
{
|
||||
size_t correct = 0;
|
||||
for (size_t k = 0; k < n; ++k)
|
||||
{
|
||||
pb.val(A[k]) = (i & (1ul<<k) ? FieldT::one() : FieldT::zero());
|
||||
pb.val(B[k]) = (j & (1ul<<k) ? FieldT::one() : FieldT::zero());
|
||||
correct += ((i & (1ul<<k)) && (j & (1ul<<k)) ? 1 : 0);
|
||||
}
|
||||
|
||||
g.generate_r1cs_witness();
|
||||
#ifdef DEBUG
|
||||
printf("positive test for (%zu, %zu)\n", i, j);
|
||||
#endif
|
||||
assert(pb.val(result) == FieldT(correct));
|
||||
assert(pb.is_satisfied());
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("negative test for (%zu, %zu)\n", i, j);
|
||||
#endif
|
||||
pb.val(result) = FieldT(100*n+19);
|
||||
assert(!pb.is_satisfied());
|
||||
}
|
||||
}
|
||||
|
||||
print_time("inner_product_gadget tests successful");
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void loose_multiplexing_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
/* \alpha_i (index - i) = 0 */
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
this->pb.add_r1cs_constraint(
|
||||
r1cs_constraint<FieldT>(alpha[i], index - i, 0),
|
||||
FMT(this->annotation_prefix, " alpha_%zu", i));
|
||||
}
|
||||
|
||||
/* 1 * (\sum \alpha_i) = success_flag */
|
||||
linear_combination<FieldT> a, b, c;
|
||||
a.add_term(ONE);
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
b.add_term(alpha[i]);
|
||||
}
|
||||
c.add_term(success_flag);
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(a, b, c), FMT(this->annotation_prefix, " main_constraint"));
|
||||
|
||||
/* now success_flag is constrained to either 0 (if index is out of
|
||||
range) or \alpha_i. constrain it and \alpha_i to zero */
|
||||
generate_boolean_r1cs_constraint<FieldT>(this->pb, success_flag, FMT(this->annotation_prefix, " success_flag"));
|
||||
|
||||
/* compute result */
|
||||
compute_result->generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void loose_multiplexing_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
/* assumes that idx can be fit in ulong; true for our purposes for now */
|
||||
const bigint<FieldT::num_limbs> valint = this->pb.val(index).as_bigint();
|
||||
unsigned long idx = valint.as_ulong();
|
||||
const bigint<FieldT::num_limbs> arrsize(arr.size());
|
||||
|
||||
if (idx >= arr.size() || mpn_cmp(valint.data, arrsize.data, FieldT::num_limbs) >= 0)
|
||||
{
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
this->pb.val(alpha[i]) = FieldT::zero();
|
||||
}
|
||||
|
||||
this->pb.val(success_flag) = FieldT::zero();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < arr.size(); ++i)
|
||||
{
|
||||
this->pb.val(alpha[i]) = (i == idx ? FieldT::one() : FieldT::zero());
|
||||
}
|
||||
|
||||
this->pb.val(success_flag) = FieldT::one();
|
||||
}
|
||||
|
||||
compute_result->generate_r1cs_witness();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void test_loose_multiplexing_gadget(const size_t n)
|
||||
{
|
||||
printf("testing loose_multiplexing_gadget on 2**%zu pb_variable<FieldT> array inputs\n", n);
|
||||
protoboard<FieldT> pb;
|
||||
|
||||
pb_variable_array<FieldT> arr;
|
||||
arr.allocate(pb, 1ul<<n, "arr");
|
||||
pb_variable<FieldT> index, result, success_flag;
|
||||
index.allocate(pb, "index");
|
||||
result.allocate(pb, "result");
|
||||
success_flag.allocate(pb, "success_flag");
|
||||
|
||||
loose_multiplexing_gadget<FieldT> g(pb, arr, index, result, success_flag, "g");
|
||||
g.generate_r1cs_constraints();
|
||||
|
||||
for (size_t i = 0; i < 1ul<<n; ++i)
|
||||
{
|
||||
pb.val(arr[i]) = FieldT((19*i) % (1ul<<n));
|
||||
}
|
||||
|
||||
for (int idx = -1; idx <= (int)(1ul<<n); ++idx)
|
||||
{
|
||||
pb.val(index) = FieldT(idx);
|
||||
g.generate_r1cs_witness();
|
||||
|
||||
if (0 <= idx && idx <= (int)(1ul<<n) - 1)
|
||||
{
|
||||
printf("demuxing element %d (in bounds)\n", idx);
|
||||
assert(pb.val(result) == FieldT((19*idx) % (1ul<<n)));
|
||||
assert(pb.val(success_flag) == FieldT::one());
|
||||
assert(pb.is_satisfied());
|
||||
pb.val(result) -= FieldT::one();
|
||||
assert(!pb.is_satisfied());
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("demuxing element %d (out of bounds)\n", idx);
|
||||
assert(pb.val(success_flag) == FieldT::zero());
|
||||
assert(pb.is_satisfied());
|
||||
pb.val(success_flag) = FieldT::one();
|
||||
assert(!pb.is_satisfied());
|
||||
}
|
||||
}
|
||||
printf("loose_multiplexing_gadget tests successful\n");
|
||||
}
|
||||
|
||||
template<typename FieldT, typename VarT>
|
||||
void create_linear_combination_constraints(protoboard<FieldT> &pb,
|
||||
const std::vector<FieldT> &base,
|
||||
const std::vector<std::pair<VarT, FieldT> > &v,
|
||||
const VarT &target,
|
||||
const std::string &annotation_prefix)
|
||||
{
|
||||
for (size_t i = 0; i < base.size(); ++i)
|
||||
{
|
||||
linear_combination<FieldT> a, b, c;
|
||||
|
||||
a.add_term(ONE);
|
||||
b.add_term(ONE, base[i]);
|
||||
|
||||
for (auto &p : v)
|
||||
{
|
||||
b.add_term(p.first.all_vars[i], p.second);
|
||||
}
|
||||
|
||||
c.add_term(target.all_vars[i]);
|
||||
|
||||
pb.add_r1cs_constraint(r1cs_constraint<FieldT>(a, b, c), FMT(annotation_prefix, " linear_combination_%zu", i));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT, typename VarT>
|
||||
void create_linear_combination_witness(protoboard<FieldT> &pb,
|
||||
const std::vector<FieldT> &base,
|
||||
const std::vector<std::pair<VarT, FieldT> > &v,
|
||||
const VarT &target)
|
||||
{
|
||||
for (size_t i = 0; i < base.size(); ++i)
|
||||
{
|
||||
pb.val(target.all_vars[i]) = base[i];
|
||||
|
||||
for (auto &p : v)
|
||||
{
|
||||
pb.val(target.all_vars[i]) += p.second * pb.val(p.first.all_vars[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // BASIC_GADGETS_TCC_
|
||||
45
src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.hpp
Normal file
45
src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Declaration of interfaces for a gadget that can be created from an R1CS constraint system.
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef GADGET_FROM_R1CS_HPP_
|
||||
#define GADGET_FROM_R1CS_HPP_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "gadgetlib1/gadget.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
class gadget_from_r1cs : public gadget<FieldT> {
|
||||
|
||||
private:
|
||||
const std::vector<pb_variable_array<FieldT> > vars;
|
||||
const r1cs_constraint_system<FieldT> cs;
|
||||
std::map<size_t, size_t> cs_to_vars;
|
||||
|
||||
public:
|
||||
|
||||
gadget_from_r1cs(protoboard<FieldT> &pb,
|
||||
const std::vector<pb_variable_array<FieldT> > &vars,
|
||||
const r1cs_constraint_system<FieldT> &cs,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness(const r1cs_primary_input<FieldT> &primary_input,
|
||||
const r1cs_auxiliary_input<FieldT> &auxiliary_input);
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
|
||||
#include "gadgetlib1/gadgets/gadget_from_r1cs.tcc"
|
||||
|
||||
#endif // GADGET_FROM_R1CS_HPP_
|
||||
123
src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.tcc
Normal file
123
src/snark/libsnark/gadgetlib1/gadgets/gadget_from_r1cs.tcc
Normal file
@@ -0,0 +1,123 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Implementation of interfaces for a gadget that can be created from an R1CS constraint system.
|
||||
|
||||
See gadget_from_r1cs.hpp .
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef GADGET_FROM_R1CS_TCC_
|
||||
#define GADGET_FROM_R1CS_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
gadget_from_r1cs<FieldT>::gadget_from_r1cs(protoboard<FieldT> &pb,
|
||||
const std::vector<pb_variable_array<FieldT> > &vars,
|
||||
const r1cs_constraint_system<FieldT> &cs,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
vars(vars),
|
||||
cs(cs)
|
||||
{
|
||||
cs_to_vars[0] = 0; /* constant term maps to constant term */
|
||||
|
||||
size_t cs_var_idx = 1;
|
||||
for (auto va : vars)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("gadget_from_r1cs: translating a block of variables with length %zu\n", va.size());
|
||||
#endif
|
||||
for (auto v : va)
|
||||
{
|
||||
cs_to_vars[cs_var_idx] = v.index;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (v.index != 0)
|
||||
{
|
||||
// handle annotations, except for re-annotating constant term
|
||||
const std::map<size_t, std::string>::const_iterator it = cs.variable_annotations.find(cs_var_idx);
|
||||
|
||||
std::string annotation = FMT(annotation_prefix, " variable_%zu", cs_var_idx);
|
||||
if (it != cs.variable_annotations.end())
|
||||
{
|
||||
annotation = annotation_prefix + " " + it->second;
|
||||
}
|
||||
|
||||
pb.augment_variable_annotation(v, annotation);
|
||||
}
|
||||
#endif
|
||||
++cs_var_idx;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("gadget_from_r1cs: sum of all block lengths: %zu\n", cs_var_idx-1);
|
||||
printf("gadget_from_r1cs: cs.num_variables(): %zu\n", cs.num_variables());
|
||||
#endif
|
||||
|
||||
assert(cs_var_idx - 1 == cs.num_variables());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void gadget_from_r1cs<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < cs.num_constraints(); ++i)
|
||||
{
|
||||
const r1cs_constraint<FieldT> &constr = cs.constraints[i];
|
||||
r1cs_constraint<FieldT> translated_constr;
|
||||
|
||||
for (const linear_term<FieldT> &t: constr.a.terms)
|
||||
{
|
||||
translated_constr.a.terms.emplace_back(linear_term<FieldT>(pb_variable<FieldT>(cs_to_vars[t.index]), t.coeff));
|
||||
}
|
||||
|
||||
for (const linear_term<FieldT> &t: constr.b.terms)
|
||||
{
|
||||
translated_constr.b.terms.emplace_back(linear_term<FieldT>(pb_variable<FieldT>(cs_to_vars[t.index]), t.coeff));
|
||||
}
|
||||
|
||||
for (const linear_term<FieldT> &t: constr.c.terms)
|
||||
{
|
||||
translated_constr.c.terms.emplace_back(linear_term<FieldT>(pb_variable<FieldT>(cs_to_vars[t.index]), t.coeff));
|
||||
}
|
||||
|
||||
std::string annotation = FMT(this->annotation_prefix, " constraint_%zu", i);
|
||||
|
||||
#ifdef DEBUG
|
||||
auto it = cs.constraint_annotations.find(i);
|
||||
if (it != cs.constraint_annotations.end())
|
||||
{
|
||||
annotation = this->annotation_prefix + " " + it->second;
|
||||
}
|
||||
#endif
|
||||
this->pb.add_r1cs_constraint(translated_constr, annotation);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void gadget_from_r1cs<FieldT>::generate_r1cs_witness(const r1cs_primary_input<FieldT> &primary_input,
|
||||
const r1cs_auxiliary_input<FieldT> &auxiliary_input)
|
||||
{
|
||||
assert(cs.num_inputs() == primary_input.size());
|
||||
assert(cs.num_variables() == primary_input.size() + auxiliary_input.size());
|
||||
|
||||
for (size_t i = 0; i < primary_input.size(); ++i)
|
||||
{
|
||||
this->pb.val(pb_variable<FieldT>(cs_to_vars[i+1])) = primary_input[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < auxiliary_input.size(); ++i)
|
||||
{
|
||||
this->pb.val(pb_variable<FieldT>(cs_to_vars[primary_input.size()+i+1])) = auxiliary_input[i];
|
||||
}
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
|
||||
#endif // GADGET_FROM_R1CS_TCC_
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
#ifndef DIGEST_SELECTOR_GADGET_HPP_
|
||||
#define DIGEST_SELECTOR_GADGET_HPP_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "gadgetlib1/gadgets/basic_gadgets.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/hash_io.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
class digest_selector_gadget : public gadget<FieldT> {
|
||||
public:
|
||||
size_t digest_size;
|
||||
digest_variable<FieldT> input;
|
||||
pb_linear_combination<FieldT> is_right;
|
||||
digest_variable<FieldT> left;
|
||||
digest_variable<FieldT> right;
|
||||
|
||||
digest_selector_gadget(protoboard<FieldT> &pb,
|
||||
const size_t digest_size,
|
||||
const digest_variable<FieldT> &input,
|
||||
const pb_linear_combination<FieldT> &is_right,
|
||||
const digest_variable<FieldT> &left,
|
||||
const digest_variable<FieldT> &right,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
|
||||
#include "gadgetlib1/gadgets/hashes/digest_selector_gadget.tcc"
|
||||
|
||||
#endif // DIGEST_SELECTOR_GADGET_HPP_
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
#ifndef DIGEST_SELECTOR_GADGET_TCC_
|
||||
#define DIGEST_SELECTOR_GADGET_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
digest_selector_gadget<FieldT>::digest_selector_gadget(protoboard<FieldT> &pb,
|
||||
const size_t digest_size,
|
||||
const digest_variable<FieldT> &input,
|
||||
const pb_linear_combination<FieldT> &is_right,
|
||||
const digest_variable<FieldT> &left,
|
||||
const digest_variable<FieldT> &right,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix), digest_size(digest_size), input(input), is_right(is_right), left(left), right(right)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void digest_selector_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < digest_size; ++i)
|
||||
{
|
||||
/*
|
||||
input = is_right * right + (1-is_right) * left
|
||||
input - left = is_right(right - left)
|
||||
*/
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(is_right, right.bits[i] - left.bits[i], input.bits[i] - left.bits[i]),
|
||||
FMT(this->annotation_prefix, " propagate_%zu", i));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void digest_selector_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
is_right.evaluate(this->pb);
|
||||
|
||||
assert(this->pb.lc_val(is_right) == FieldT::one() || this->pb.lc_val(is_right) == FieldT::zero());
|
||||
if (this->pb.lc_val(is_right) == FieldT::one())
|
||||
{
|
||||
for (size_t i = 0; i < digest_size; ++i)
|
||||
{
|
||||
this->pb.val(right.bits[i]) = this->pb.val(input.bits[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < digest_size; ++i)
|
||||
{
|
||||
this->pb.val(left.bits[i]) = this->pb.val(input.bits[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
|
||||
#endif // DIGEST_SELECTOR_GADGET_TCC_
|
||||
63
src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.hpp
Normal file
63
src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
#ifndef HASH_IO_HPP_
|
||||
#define HASH_IO_HPP_
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include "gadgetlib1/gadgets/basic_gadgets.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
class digest_variable : public gadget<FieldT> {
|
||||
public:
|
||||
size_t digest_size;
|
||||
pb_variable_array<FieldT> bits;
|
||||
|
||||
digest_variable<FieldT>(protoboard<FieldT> &pb,
|
||||
const size_t digest_size,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
digest_variable<FieldT>(protoboard<FieldT> &pb,
|
||||
const size_t digest_size,
|
||||
const pb_variable_array<FieldT> &partial_bits,
|
||||
const pb_variable<FieldT> &padding,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness(const bit_vector& contents);
|
||||
bit_vector get_digest() const;
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
class block_variable : public gadget<FieldT> {
|
||||
public:
|
||||
size_t block_size;
|
||||
pb_variable_array<FieldT> bits;
|
||||
|
||||
block_variable(protoboard<FieldT> &pb,
|
||||
const size_t block_size,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
block_variable(protoboard<FieldT> &pb,
|
||||
const std::vector<pb_variable_array<FieldT> > &parts,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
block_variable(protoboard<FieldT> &pb,
|
||||
const digest_variable<FieldT> &left,
|
||||
const digest_variable<FieldT> &right,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness(const bit_vector& contents);
|
||||
bit_vector get_block() const;
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
#include "gadgetlib1/gadgets/hashes/hash_io.tcc"
|
||||
|
||||
#endif // HASH_IO_HPP_
|
||||
105
src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.tcc
Normal file
105
src/snark/libsnark/gadgetlib1/gadgets/hashes/hash_io.tcc
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
#ifndef HASH_IO_TCC_
|
||||
#define HASH_IO_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
digest_variable<FieldT>::digest_variable(protoboard<FieldT> &pb,
|
||||
const size_t digest_size,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix), digest_size(digest_size)
|
||||
{
|
||||
bits.allocate(pb, digest_size, FMT(this->annotation_prefix, " bits"));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
digest_variable<FieldT>::digest_variable(protoboard<FieldT> &pb,
|
||||
const size_t digest_size,
|
||||
const pb_variable_array<FieldT> &partial_bits,
|
||||
const pb_variable<FieldT> &padding,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix), digest_size(digest_size)
|
||||
{
|
||||
assert(bits.size() <= digest_size);
|
||||
bits = partial_bits;
|
||||
while (bits.size() != digest_size)
|
||||
{
|
||||
bits.emplace_back(padding);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void digest_variable<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < digest_size; ++i)
|
||||
{
|
||||
generate_boolean_r1cs_constraint<FieldT>(this->pb, bits[i], FMT(this->annotation_prefix, " bits_%zu", i));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void digest_variable<FieldT>::generate_r1cs_witness(const bit_vector& contents)
|
||||
{
|
||||
bits.fill_with_bits(this->pb, contents);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector digest_variable<FieldT>::get_digest() const
|
||||
{
|
||||
return bits.get_bits(this->pb);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
block_variable<FieldT>::block_variable(protoboard<FieldT> &pb,
|
||||
const size_t block_size,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix), block_size(block_size)
|
||||
{
|
||||
bits.allocate(pb, block_size, FMT(this->annotation_prefix, " bits"));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
block_variable<FieldT>::block_variable(protoboard<FieldT> &pb,
|
||||
const std::vector<pb_variable_array<FieldT> > &parts,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix)
|
||||
{
|
||||
for (auto &part : parts)
|
||||
{
|
||||
bits.insert(bits.end(), part.begin(), part.end());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
block_variable<FieldT>::block_variable(protoboard<FieldT> &pb,
|
||||
const digest_variable<FieldT> &left,
|
||||
const digest_variable<FieldT> &right,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix)
|
||||
{
|
||||
assert(left.bits.size() == right.bits.size());
|
||||
block_size = 2 * left.bits.size();
|
||||
bits.insert(bits.end(), left.bits.begin(), left.bits.end());
|
||||
bits.insert(bits.end(), right.bits.begin(), right.bits.end());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void block_variable<FieldT>::generate_r1cs_witness(const bit_vector& contents)
|
||||
{
|
||||
bits.fill_with_bits(this->pb, contents);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector block_variable<FieldT>::get_block() const
|
||||
{
|
||||
return bits.get_bits(this->pb);
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // HASH_IO_TCC_
|
||||
@@ -0,0 +1,160 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Declaration of interfaces for auxiliary gadgets for the SHA256 gadget.
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SHA256_AUX_HPP_
|
||||
#define SHA256_AUX_HPP_
|
||||
|
||||
#include "gadgetlib1/gadgets/basic_gadgets.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
class lastbits_gadget : public gadget<FieldT> {
|
||||
public:
|
||||
pb_variable<FieldT> X;
|
||||
size_t X_bits;
|
||||
pb_variable<FieldT> result;
|
||||
pb_linear_combination_array<FieldT> result_bits;
|
||||
|
||||
pb_linear_combination_array<FieldT> full_bits;
|
||||
std::shared_ptr<packing_gadget<FieldT> > unpack_bits;
|
||||
std::shared_ptr<packing_gadget<FieldT> > pack_result;
|
||||
|
||||
lastbits_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable<FieldT> &X,
|
||||
const size_t X_bits,
|
||||
const pb_variable<FieldT> &result,
|
||||
const pb_linear_combination_array<FieldT> &result_bits,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
class XOR3_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
pb_variable<FieldT> tmp;
|
||||
public:
|
||||
pb_linear_combination<FieldT> A;
|
||||
pb_linear_combination<FieldT> B;
|
||||
pb_linear_combination<FieldT> C;
|
||||
bool assume_C_is_zero;
|
||||
pb_linear_combination<FieldT> out;
|
||||
|
||||
XOR3_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination<FieldT> &A,
|
||||
const pb_linear_combination<FieldT> &B,
|
||||
const pb_linear_combination<FieldT> &C,
|
||||
const bool assume_C_is_zero,
|
||||
const pb_linear_combination<FieldT> &out,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */
|
||||
template<typename FieldT>
|
||||
class small_sigma_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
pb_variable_array<FieldT> W;
|
||||
pb_variable<FieldT> result;
|
||||
public:
|
||||
pb_variable_array<FieldT> result_bits;
|
||||
std::vector<std::shared_ptr<XOR3_gadget<FieldT> > > compute_bits;
|
||||
std::shared_ptr<packing_gadget<FieldT> > pack_result;
|
||||
|
||||
small_sigma_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable_array<FieldT> &W,
|
||||
const pb_variable<FieldT> &result,
|
||||
const size_t rot1,
|
||||
const size_t rot2,
|
||||
const size_t shift,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */
|
||||
template<typename FieldT>
|
||||
class big_sigma_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
pb_linear_combination_array<FieldT> W;
|
||||
pb_variable<FieldT> result;
|
||||
public:
|
||||
pb_variable_array<FieldT> result_bits;
|
||||
std::vector<std::shared_ptr<XOR3_gadget<FieldT> > > compute_bits;
|
||||
std::shared_ptr<packing_gadget<FieldT> > pack_result;
|
||||
|
||||
big_sigma_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &W,
|
||||
const pb_variable<FieldT> &result,
|
||||
const size_t rot1,
|
||||
const size_t rot2,
|
||||
const size_t rot3,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */
|
||||
template<typename FieldT>
|
||||
class choice_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
pb_variable_array<FieldT> result_bits;
|
||||
public:
|
||||
pb_linear_combination_array<FieldT> X;
|
||||
pb_linear_combination_array<FieldT> Y;
|
||||
pb_linear_combination_array<FieldT> Z;
|
||||
pb_variable<FieldT> result;
|
||||
std::shared_ptr<packing_gadget<FieldT> > pack_result;
|
||||
|
||||
choice_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &X,
|
||||
const pb_linear_combination_array<FieldT> &Y,
|
||||
const pb_linear_combination_array<FieldT> &Z,
|
||||
const pb_variable<FieldT> &result, const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */
|
||||
template<typename FieldT>
|
||||
class majority_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
pb_variable_array<FieldT> result_bits;
|
||||
std::shared_ptr<packing_gadget<FieldT> > pack_result;
|
||||
public:
|
||||
pb_linear_combination_array<FieldT> X;
|
||||
pb_linear_combination_array<FieldT> Y;
|
||||
pb_linear_combination_array<FieldT> Z;
|
||||
pb_variable<FieldT> result;
|
||||
|
||||
majority_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &X,
|
||||
const pb_linear_combination_array<FieldT> &Y,
|
||||
const pb_linear_combination_array<FieldT> &Z,
|
||||
const pb_variable<FieldT> &result,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
|
||||
#include "gadgetlib1/gadgets/hashes/sha256/sha256_aux.tcc"
|
||||
|
||||
#endif // SHA256_AUX_HPP_
|
||||
@@ -0,0 +1,297 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Implementation of interfaces for auxiliary gadgets for the SHA256 gadget.
|
||||
|
||||
See sha256_aux.hpp .
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SHA256_AUX_TCC_
|
||||
#define SHA256_AUX_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
lastbits_gadget<FieldT>::lastbits_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable<FieldT> &X,
|
||||
const size_t X_bits,
|
||||
const pb_variable<FieldT> &result,
|
||||
const pb_linear_combination_array<FieldT> &result_bits,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
X(X),
|
||||
X_bits(X_bits),
|
||||
result(result),
|
||||
result_bits(result_bits)
|
||||
{
|
||||
full_bits = result_bits;
|
||||
for (size_t i = result_bits.size(); i < X_bits; ++i)
|
||||
{
|
||||
pb_variable<FieldT> full_bits_overflow;
|
||||
full_bits_overflow.allocate(pb, FMT(this->annotation_prefix, " full_bits_%zu", i));
|
||||
full_bits.emplace_back(full_bits_overflow);
|
||||
}
|
||||
|
||||
unpack_bits.reset(new packing_gadget<FieldT>(pb, full_bits, X, FMT(this->annotation_prefix, " unpack_bits")));
|
||||
pack_result.reset(new packing_gadget<FieldT>(pb, result_bits, result, FMT(this->annotation_prefix, " pack_result")));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void lastbits_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
unpack_bits->generate_r1cs_constraints(true);
|
||||
pack_result->generate_r1cs_constraints(false);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void lastbits_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
unpack_bits->generate_r1cs_witness_from_packed();
|
||||
pack_result->generate_r1cs_witness_from_bits();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
XOR3_gadget<FieldT>::XOR3_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination<FieldT> &A,
|
||||
const pb_linear_combination<FieldT> &B,
|
||||
const pb_linear_combination<FieldT> &C,
|
||||
const bool assume_C_is_zero,
|
||||
const pb_linear_combination<FieldT> &out,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
A(A),
|
||||
B(B),
|
||||
C(C),
|
||||
assume_C_is_zero(assume_C_is_zero),
|
||||
out(out)
|
||||
{
|
||||
if (!assume_C_is_zero)
|
||||
{
|
||||
tmp.allocate(pb, FMT(this->annotation_prefix, " tmp"));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void XOR3_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
/*
|
||||
tmp = A + B - 2AB i.e. tmp = A xor B
|
||||
out = tmp + C - 2tmp C i.e. out = tmp xor C
|
||||
*/
|
||||
if (assume_C_is_zero)
|
||||
{
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(2*A, B, A + B - out), FMT(this->annotation_prefix, " implicit_tmp_equals_out"));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(2*A, B, A + B - tmp), FMT(this->annotation_prefix, " tmp"));
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(2 * tmp, C, tmp + C - out), FMT(this->annotation_prefix, " out"));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void XOR3_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
if (assume_C_is_zero)
|
||||
{
|
||||
this->pb.lc_val(out) = this->pb.lc_val(A) + this->pb.lc_val(B) - FieldT(2) * this->pb.lc_val(A) * this->pb.lc_val(B);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->pb.val(tmp) = this->pb.lc_val(A) + this->pb.lc_val(B) - FieldT(2) * this->pb.lc_val(A) * this->pb.lc_val(B);
|
||||
this->pb.lc_val(out) = this->pb.val(tmp) + this->pb.lc_val(C) - FieldT(2) * this->pb.val(tmp) * this->pb.lc_val(C);
|
||||
}
|
||||
}
|
||||
|
||||
#define SHA256_GADGET_ROTR(A, i, k) A[((i)+(k)) % 32]
|
||||
|
||||
/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */
|
||||
template<typename FieldT>
|
||||
small_sigma_gadget<FieldT>::small_sigma_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable_array<FieldT> &W,
|
||||
const pb_variable<FieldT> &result,
|
||||
const size_t rot1,
|
||||
const size_t rot2,
|
||||
const size_t shift,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
W(W),
|
||||
result(result)
|
||||
{
|
||||
result_bits.allocate(pb, 32, FMT(this->annotation_prefix, " result_bits"));
|
||||
compute_bits.resize(32);
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
compute_bits[i].reset(new XOR3_gadget<FieldT>(pb, SHA256_GADGET_ROTR(W, i, rot1), SHA256_GADGET_ROTR(W, i, rot2),
|
||||
(i + shift < 32 ? W[i+shift] : ONE),
|
||||
(i + shift >= 32), result_bits[i],
|
||||
FMT(this->annotation_prefix, " compute_bits_%zu", i)));
|
||||
}
|
||||
pack_result.reset(new packing_gadget<FieldT>(pb, result_bits, result, FMT(this->annotation_prefix, " pack_result")));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void small_sigma_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
compute_bits[i]->generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
pack_result->generate_r1cs_constraints(false);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void small_sigma_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
compute_bits[i]->generate_r1cs_witness();
|
||||
}
|
||||
|
||||
pack_result->generate_r1cs_witness_from_bits();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
big_sigma_gadget<FieldT>::big_sigma_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &W,
|
||||
const pb_variable<FieldT> &result,
|
||||
const size_t rot1,
|
||||
const size_t rot2,
|
||||
const size_t rot3,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
W(W),
|
||||
result(result)
|
||||
{
|
||||
result_bits.allocate(pb, 32, FMT(this->annotation_prefix, " result_bits"));
|
||||
compute_bits.resize(32);
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
compute_bits[i].reset(new XOR3_gadget<FieldT>(pb, SHA256_GADGET_ROTR(W, i, rot1), SHA256_GADGET_ROTR(W, i, rot2), SHA256_GADGET_ROTR(W, i, rot3), false, result_bits[i],
|
||||
FMT(this->annotation_prefix, " compute_bits_%zu", i)));
|
||||
}
|
||||
|
||||
pack_result.reset(new packing_gadget<FieldT>(pb, result_bits, result, FMT(this->annotation_prefix, " pack_result")));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void big_sigma_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
compute_bits[i]->generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
pack_result->generate_r1cs_constraints(false);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void big_sigma_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
compute_bits[i]->generate_r1cs_witness();
|
||||
}
|
||||
|
||||
pack_result->generate_r1cs_witness_from_bits();
|
||||
}
|
||||
|
||||
/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */
|
||||
template<typename FieldT>
|
||||
choice_gadget<FieldT>::choice_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &X,
|
||||
const pb_linear_combination_array<FieldT> &Y,
|
||||
const pb_linear_combination_array<FieldT> &Z,
|
||||
const pb_variable<FieldT> &result, const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
X(X),
|
||||
Y(Y),
|
||||
Z(Z),
|
||||
result(result)
|
||||
{
|
||||
result_bits.allocate(pb, 32, FMT(this->annotation_prefix, " result_bits"));
|
||||
pack_result.reset(new packing_gadget<FieldT>(pb, result_bits, result, FMT(this->annotation_prefix, " result")));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void choice_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
/*
|
||||
result = x * y + (1-x) * z
|
||||
result - z = x * (y - z)
|
||||
*/
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(X[i], Y[i] - Z[i], result_bits[i] - Z[i]), FMT(this->annotation_prefix, " result_bits_%zu", i));
|
||||
}
|
||||
pack_result->generate_r1cs_constraints(false);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void choice_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
this->pb.val(result_bits[i]) = this->pb.lc_val(X[i]) * this->pb.lc_val(Y[i]) + (FieldT::one() - this->pb.lc_val(X[i])) * this->pb.lc_val(Z[i]);
|
||||
}
|
||||
pack_result->generate_r1cs_witness_from_bits();
|
||||
}
|
||||
|
||||
/* Page 10 of http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf */
|
||||
template<typename FieldT>
|
||||
majority_gadget<FieldT>::majority_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &X,
|
||||
const pb_linear_combination_array<FieldT> &Y,
|
||||
const pb_linear_combination_array<FieldT> &Z,
|
||||
const pb_variable<FieldT> &result,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
X(X),
|
||||
Y(Y),
|
||||
Z(Z),
|
||||
result(result)
|
||||
{
|
||||
result_bits.allocate(pb, 32, FMT(this->annotation_prefix, " result_bits"));
|
||||
pack_result.reset(new packing_gadget<FieldT>(pb, result_bits, result, FMT(this->annotation_prefix, " result")));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void majority_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
/*
|
||||
2*result + aux = x + y + z
|
||||
x, y, z, aux -- bits
|
||||
aux = x + y + z - 2*result
|
||||
*/
|
||||
generate_boolean_r1cs_constraint<FieldT>(this->pb, result_bits[i], FMT(this->annotation_prefix, " result_%zu", i));
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(X[i] + Y[i] + Z[i] - 2 * result_bits[i],
|
||||
1 - (X[i] + Y[i] + Z[i] - 2 * result_bits[i]),
|
||||
0),
|
||||
FMT(this->annotation_prefix, " result_bits_%zu", i));
|
||||
}
|
||||
pack_result->generate_r1cs_constraints(false);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void majority_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
{
|
||||
const long v = (this->pb.lc_val(X[i]) + this->pb.lc_val(Y[i]) + this->pb.lc_val(Z[i])).as_ulong();
|
||||
this->pb.val(result_bits[i]) = FieldT(v / 2);
|
||||
}
|
||||
|
||||
pack_result->generate_r1cs_witness_from_bits();
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
|
||||
#endif // SHA256_AUX_TCC_
|
||||
@@ -0,0 +1,108 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Declaration of interfaces for gadgets for the SHA256 message schedule and round function.
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SHA256_COMPONENTS_HPP_
|
||||
#define SHA256_COMPONENTS_HPP_
|
||||
|
||||
#include "gadgetlib1/gadgets/basic_gadgets.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/hash_io.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/sha256/sha256_aux.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
const size_t SHA256_digest_size = 256;
|
||||
const size_t SHA256_block_size = 512;
|
||||
|
||||
template<typename FieldT>
|
||||
pb_linear_combination_array<FieldT> SHA256_default_IV(protoboard<FieldT> &pb);
|
||||
|
||||
template<typename FieldT>
|
||||
class sha256_message_schedule_gadget : public gadget<FieldT> {
|
||||
public:
|
||||
std::vector<pb_variable_array<FieldT> > W_bits;
|
||||
std::vector<std::shared_ptr<packing_gadget<FieldT> > > pack_W;
|
||||
|
||||
std::vector<pb_variable<FieldT> > sigma0;
|
||||
std::vector<pb_variable<FieldT> > sigma1;
|
||||
std::vector<std::shared_ptr<small_sigma_gadget<FieldT> > > compute_sigma0;
|
||||
std::vector<std::shared_ptr<small_sigma_gadget<FieldT> > > compute_sigma1;
|
||||
std::vector<pb_variable<FieldT> > unreduced_W;
|
||||
std::vector<std::shared_ptr<lastbits_gadget<FieldT> > > mod_reduce_W;
|
||||
public:
|
||||
pb_variable_array<FieldT> M;
|
||||
pb_variable_array<FieldT> packed_W;
|
||||
sha256_message_schedule_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable_array<FieldT> &M,
|
||||
const pb_variable_array<FieldT> &packed_W,
|
||||
const std::string &annotation_prefix);
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
class sha256_round_function_gadget : public gadget<FieldT> {
|
||||
public:
|
||||
pb_variable<FieldT> sigma0;
|
||||
pb_variable<FieldT> sigma1;
|
||||
std::shared_ptr<big_sigma_gadget<FieldT> > compute_sigma0;
|
||||
std::shared_ptr<big_sigma_gadget<FieldT> > compute_sigma1;
|
||||
pb_variable<FieldT> choice;
|
||||
pb_variable<FieldT> majority;
|
||||
std::shared_ptr<choice_gadget<FieldT> > compute_choice;
|
||||
std::shared_ptr<majority_gadget<FieldT> > compute_majority;
|
||||
pb_variable<FieldT> packed_d;
|
||||
std::shared_ptr<packing_gadget<FieldT> > pack_d;
|
||||
pb_variable<FieldT> packed_h;
|
||||
std::shared_ptr<packing_gadget<FieldT> > pack_h;
|
||||
pb_variable<FieldT> unreduced_new_a;
|
||||
pb_variable<FieldT> unreduced_new_e;
|
||||
std::shared_ptr<lastbits_gadget<FieldT> > mod_reduce_new_a;
|
||||
std::shared_ptr<lastbits_gadget<FieldT> > mod_reduce_new_e;
|
||||
pb_variable<FieldT> packed_new_a;
|
||||
pb_variable<FieldT> packed_new_e;
|
||||
public:
|
||||
pb_linear_combination_array<FieldT> a;
|
||||
pb_linear_combination_array<FieldT> b;
|
||||
pb_linear_combination_array<FieldT> c;
|
||||
pb_linear_combination_array<FieldT> d;
|
||||
pb_linear_combination_array<FieldT> e;
|
||||
pb_linear_combination_array<FieldT> f;
|
||||
pb_linear_combination_array<FieldT> g;
|
||||
pb_linear_combination_array<FieldT> h;
|
||||
pb_variable<FieldT> W;
|
||||
long K;
|
||||
pb_linear_combination_array<FieldT> new_a;
|
||||
pb_linear_combination_array<FieldT> new_e;
|
||||
|
||||
sha256_round_function_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &a,
|
||||
const pb_linear_combination_array<FieldT> &b,
|
||||
const pb_linear_combination_array<FieldT> &c,
|
||||
const pb_linear_combination_array<FieldT> &d,
|
||||
const pb_linear_combination_array<FieldT> &e,
|
||||
const pb_linear_combination_array<FieldT> &f,
|
||||
const pb_linear_combination_array<FieldT> &g,
|
||||
const pb_linear_combination_array<FieldT> &h,
|
||||
const pb_variable<FieldT> &W,
|
||||
const long &K,
|
||||
const pb_linear_combination_array<FieldT> &new_a,
|
||||
const pb_linear_combination_array<FieldT> &new_e,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
|
||||
#include "gadgetlib1/gadgets/hashes/sha256/sha256_components.tcc"
|
||||
|
||||
#endif // SHA256_COMPONENTS_HPP_
|
||||
@@ -0,0 +1,250 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Implementation of interfaces for gadgets for the SHA256 message schedule and round function.
|
||||
|
||||
See sha256_components.hpp .
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SHA256_COMPONENTS_TCC_
|
||||
#define SHA256_COMPONENTS_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
const unsigned long SHA256_K[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
const unsigned long SHA256_H[8] = {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
pb_linear_combination_array<FieldT> SHA256_default_IV(protoboard<FieldT> &pb)
|
||||
{
|
||||
pb_linear_combination_array<FieldT> result;
|
||||
result.reserve(SHA256_digest_size);
|
||||
|
||||
for (size_t i = 0; i < SHA256_digest_size; ++i)
|
||||
{
|
||||
int iv_val = (SHA256_H[i / 32] >> (31-(i % 32))) & 1;
|
||||
|
||||
pb_linear_combination<FieldT> iv_element;
|
||||
iv_element.assign(pb, iv_val * ONE);
|
||||
iv_element.evaluate(pb);
|
||||
|
||||
result.emplace_back(iv_element);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
sha256_message_schedule_gadget<FieldT>::sha256_message_schedule_gadget(protoboard<FieldT> &pb,
|
||||
const pb_variable_array<FieldT> &M,
|
||||
const pb_variable_array<FieldT> &packed_W,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
M(M),
|
||||
packed_W(packed_W)
|
||||
{
|
||||
W_bits.resize(64);
|
||||
|
||||
pack_W.resize(16);
|
||||
for (size_t i = 0; i < 16; ++i)
|
||||
{
|
||||
W_bits[i] = pb_variable_array<FieldT>(M.rbegin() + (15-i) * 32, M.rbegin() + (16-i) * 32);
|
||||
pack_W[i].reset(new packing_gadget<FieldT>(pb, W_bits[i], packed_W[i], FMT(this->annotation_prefix, " pack_W_%zu", i)));
|
||||
}
|
||||
|
||||
/* NB: some of those will be un-allocated */
|
||||
sigma0.resize(64);
|
||||
sigma1.resize(64);
|
||||
compute_sigma0.resize(64);
|
||||
compute_sigma1.resize(64);
|
||||
unreduced_W.resize(64);
|
||||
mod_reduce_W.resize(64);
|
||||
|
||||
for (size_t i = 16; i < 64; ++i)
|
||||
{
|
||||
/* allocate result variables for sigma0/sigma1 invocations */
|
||||
sigma0[i].allocate(pb, FMT(this->annotation_prefix, " sigma0_%zu", i));
|
||||
sigma1[i].allocate(pb, FMT(this->annotation_prefix, " sigma1_%zu", i));
|
||||
|
||||
/* compute sigma0/sigma1 */
|
||||
compute_sigma0[i].reset(new small_sigma_gadget<FieldT>(pb, W_bits[i-15], sigma0[i], 7, 18, 3, FMT(this->annotation_prefix, " compute_sigma0_%zu", i)));
|
||||
compute_sigma1[i].reset(new small_sigma_gadget<FieldT>(pb, W_bits[i-2], sigma1[i], 17, 19, 10, FMT(this->annotation_prefix, " compute_sigma1_%zu", i)));
|
||||
|
||||
/* unreduced_W = sigma0(W_{i-15}) + sigma1(W_{i-2}) + W_{i-7} + W_{i-16} before modulo 2^32 */
|
||||
unreduced_W[i].allocate(pb, FMT(this->annotation_prefix, "unreduced_W_%zu", i));
|
||||
|
||||
/* allocate the bit representation of packed_W[i] */
|
||||
W_bits[i].allocate(pb, 32, FMT(this->annotation_prefix, " W_bits_%zu", i));
|
||||
|
||||
/* and finally reduce this into packed and bit representations */
|
||||
mod_reduce_W[i].reset(new lastbits_gadget<FieldT>(pb, unreduced_W[i], 32+2, packed_W[i], W_bits[i], FMT(this->annotation_prefix, " mod_reduce_W_%zu", i)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void sha256_message_schedule_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < 16; ++i)
|
||||
{
|
||||
pack_W[i]->generate_r1cs_constraints(false); // do not enforce bitness here; caller be aware.
|
||||
}
|
||||
|
||||
for (size_t i = 16; i < 64; ++i)
|
||||
{
|
||||
compute_sigma0[i]->generate_r1cs_constraints();
|
||||
compute_sigma1[i]->generate_r1cs_constraints();
|
||||
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1,
|
||||
sigma0[i] + sigma1[i] + packed_W[i-16] + packed_W[i-7],
|
||||
unreduced_W[i]),
|
||||
FMT(this->annotation_prefix, " unreduced_W_%zu", i));
|
||||
|
||||
mod_reduce_W[i]->generate_r1cs_constraints();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void sha256_message_schedule_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
for (size_t i = 0; i < 16; ++i)
|
||||
{
|
||||
pack_W[i]->generate_r1cs_witness_from_bits();
|
||||
}
|
||||
|
||||
for (size_t i = 16; i < 64; ++i)
|
||||
{
|
||||
compute_sigma0[i]->generate_r1cs_witness();
|
||||
compute_sigma1[i]->generate_r1cs_witness();
|
||||
|
||||
this->pb.val(unreduced_W[i]) = this->pb.val(sigma0[i]) + this->pb.val(sigma1[i]) + this->pb.val(packed_W[i-16]) + this->pb.val(packed_W[i-7]);
|
||||
mod_reduce_W[i]->generate_r1cs_witness();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
sha256_round_function_gadget<FieldT>::sha256_round_function_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &a,
|
||||
const pb_linear_combination_array<FieldT> &b,
|
||||
const pb_linear_combination_array<FieldT> &c,
|
||||
const pb_linear_combination_array<FieldT> &d,
|
||||
const pb_linear_combination_array<FieldT> &e,
|
||||
const pb_linear_combination_array<FieldT> &f,
|
||||
const pb_linear_combination_array<FieldT> &g,
|
||||
const pb_linear_combination_array<FieldT> &h,
|
||||
const pb_variable<FieldT> &W,
|
||||
const long &K,
|
||||
const pb_linear_combination_array<FieldT> &new_a,
|
||||
const pb_linear_combination_array<FieldT> &new_e,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
a(a),
|
||||
b(b),
|
||||
c(c),
|
||||
d(d),
|
||||
e(e),
|
||||
f(f),
|
||||
g(g),
|
||||
h(h),
|
||||
W(W),
|
||||
K(K),
|
||||
new_a(new_a),
|
||||
new_e(new_e)
|
||||
{
|
||||
/* compute sigma0 and sigma1 */
|
||||
sigma0.allocate(pb, FMT(this->annotation_prefix, " sigma0"));
|
||||
sigma1.allocate(pb, FMT(this->annotation_prefix, " sigma1"));
|
||||
compute_sigma0.reset(new big_sigma_gadget<FieldT>(pb, a, sigma0, 2, 13, 22, FMT(this->annotation_prefix, " compute_sigma0")));
|
||||
compute_sigma1.reset(new big_sigma_gadget<FieldT>(pb, e, sigma1, 6, 11, 25, FMT(this->annotation_prefix, " compute_sigma1")));
|
||||
|
||||
/* compute choice */
|
||||
choice.allocate(pb, FMT(this->annotation_prefix, " choice"));
|
||||
compute_choice.reset(new choice_gadget<FieldT>(pb, e, f, g, choice, FMT(this->annotation_prefix, " compute_choice")));
|
||||
|
||||
/* compute majority */
|
||||
majority.allocate(pb, FMT(this->annotation_prefix, " majority"));
|
||||
compute_majority.reset(new majority_gadget<FieldT>(pb, a, b, c, majority, FMT(this->annotation_prefix, " compute_majority")));
|
||||
|
||||
/* pack d */
|
||||
packed_d.allocate(pb, FMT(this->annotation_prefix, " packed_d"));
|
||||
pack_d.reset(new packing_gadget<FieldT>(pb, d, packed_d, FMT(this->annotation_prefix, " pack_d")));
|
||||
|
||||
/* pack h */
|
||||
packed_h.allocate(pb, FMT(this->annotation_prefix, " packed_h"));
|
||||
pack_h.reset(new packing_gadget<FieldT>(pb, h, packed_h, FMT(this->annotation_prefix, " pack_h")));
|
||||
|
||||
/* compute the actual results for the round */
|
||||
unreduced_new_a.allocate(pb, FMT(this->annotation_prefix, " unreduced_new_a"));
|
||||
unreduced_new_e.allocate(pb, FMT(this->annotation_prefix, " unreduced_new_e"));
|
||||
|
||||
packed_new_a.allocate(pb, FMT(this->annotation_prefix, " packed_new_a"));
|
||||
packed_new_e.allocate(pb, FMT(this->annotation_prefix, " packed_new_e"));
|
||||
|
||||
mod_reduce_new_a.reset(new lastbits_gadget<FieldT>(pb, unreduced_new_a, 32+3, packed_new_a, new_a, FMT(this->annotation_prefix, " mod_reduce_new_a")));
|
||||
mod_reduce_new_e.reset(new lastbits_gadget<FieldT>(pb, unreduced_new_e, 32+3, packed_new_e, new_e, FMT(this->annotation_prefix, " mod_reduce_new_e")));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void sha256_round_function_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
compute_sigma0->generate_r1cs_constraints();
|
||||
compute_sigma1->generate_r1cs_constraints();
|
||||
|
||||
compute_choice->generate_r1cs_constraints();
|
||||
compute_majority->generate_r1cs_constraints();
|
||||
|
||||
pack_d->generate_r1cs_constraints(false);
|
||||
pack_h->generate_r1cs_constraints(false);
|
||||
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1,
|
||||
packed_h + sigma1 + choice + K + W + sigma0 + majority,
|
||||
unreduced_new_a),
|
||||
FMT(this->annotation_prefix, " unreduced_new_a"));
|
||||
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1,
|
||||
packed_d + packed_h + sigma1 + choice + K + W,
|
||||
unreduced_new_e),
|
||||
FMT(this->annotation_prefix, " unreduced_new_e"));
|
||||
|
||||
mod_reduce_new_a->generate_r1cs_constraints();
|
||||
mod_reduce_new_e->generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void sha256_round_function_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
compute_sigma0->generate_r1cs_witness();
|
||||
compute_sigma1->generate_r1cs_witness();
|
||||
|
||||
compute_choice->generate_r1cs_witness();
|
||||
compute_majority->generate_r1cs_witness();
|
||||
|
||||
pack_d->generate_r1cs_witness_from_bits();
|
||||
pack_h->generate_r1cs_witness_from_bits();
|
||||
|
||||
this->pb.val(unreduced_new_a) = this->pb.val(packed_h) + this->pb.val(sigma1) + this->pb.val(choice) + FieldT(K) + this->pb.val(W) + this->pb.val(sigma0) + this->pb.val(majority);
|
||||
this->pb.val(unreduced_new_e) = this->pb.val(packed_d) + this->pb.val(packed_h) + this->pb.val(sigma1) + this->pb.val(choice) + FieldT(K) + this->pb.val(W);
|
||||
|
||||
mod_reduce_new_a->generate_r1cs_witness();
|
||||
mod_reduce_new_e->generate_r1cs_witness();
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
|
||||
#endif // SHA256_COMPONENTS_TCC_
|
||||
@@ -0,0 +1,98 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Declaration of interfaces for top-level SHA256 gadgets.
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SHA256_GADGET_HPP_
|
||||
#define SHA256_GADGET_HPP_
|
||||
|
||||
#include "common/data_structures/merkle_tree.hpp"
|
||||
#include "gadgetlib1/gadgets/basic_gadgets.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/hash_io.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/sha256/sha256_components.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
/**
|
||||
* Gadget for the SHA256 compression function.
|
||||
*/
|
||||
template<typename FieldT>
|
||||
class sha256_compression_function_gadget : public gadget<FieldT> {
|
||||
public:
|
||||
std::vector<pb_linear_combination_array<FieldT> > round_a;
|
||||
std::vector<pb_linear_combination_array<FieldT> > round_b;
|
||||
std::vector<pb_linear_combination_array<FieldT> > round_c;
|
||||
std::vector<pb_linear_combination_array<FieldT> > round_d;
|
||||
std::vector<pb_linear_combination_array<FieldT> > round_e;
|
||||
std::vector<pb_linear_combination_array<FieldT> > round_f;
|
||||
std::vector<pb_linear_combination_array<FieldT> > round_g;
|
||||
std::vector<pb_linear_combination_array<FieldT> > round_h;
|
||||
|
||||
pb_variable_array<FieldT> packed_W;
|
||||
std::shared_ptr<sha256_message_schedule_gadget<FieldT> > message_schedule;
|
||||
std::vector<sha256_round_function_gadget<FieldT> > round_functions;
|
||||
|
||||
pb_variable_array<FieldT> unreduced_output;
|
||||
pb_variable_array<FieldT> reduced_output;
|
||||
std::vector<lastbits_gadget<FieldT> > reduce_output;
|
||||
public:
|
||||
pb_linear_combination_array<FieldT> prev_output;
|
||||
pb_variable_array<FieldT> new_block;
|
||||
digest_variable<FieldT> output;
|
||||
|
||||
sha256_compression_function_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &prev_output,
|
||||
const pb_variable_array<FieldT> &new_block,
|
||||
const digest_variable<FieldT> &output,
|
||||
const std::string &annotation_prefix);
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
};
|
||||
|
||||
/**
|
||||
* Gadget for the SHA256 compression function, viewed as a 2-to-1 hash
|
||||
* function, and using the same initialization vector as in SHA256
|
||||
* specification. Thus, any collision for
|
||||
* sha256_two_to_one_hash_gadget trivially extends to a collision for
|
||||
* full SHA256 (by appending the same padding).
|
||||
*/
|
||||
template<typename FieldT>
|
||||
class sha256_two_to_one_hash_gadget : public gadget<FieldT> {
|
||||
public:
|
||||
typedef bit_vector hash_value_type;
|
||||
typedef merkle_authentication_path merkle_authentication_path_type;
|
||||
|
||||
std::shared_ptr<sha256_compression_function_gadget<FieldT> > f;
|
||||
|
||||
sha256_two_to_one_hash_gadget(protoboard<FieldT> &pb,
|
||||
const digest_variable<FieldT> &left,
|
||||
const digest_variable<FieldT> &right,
|
||||
const digest_variable<FieldT> &output,
|
||||
const std::string &annotation_prefix);
|
||||
sha256_two_to_one_hash_gadget(protoboard<FieldT> &pb,
|
||||
const size_t block_length,
|
||||
const block_variable<FieldT> &input_block,
|
||||
const digest_variable<FieldT> &output,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints(const bool ensure_output_bitness=true); // TODO: ignored for now
|
||||
void generate_r1cs_witness();
|
||||
|
||||
static size_t get_block_len();
|
||||
static size_t get_digest_len();
|
||||
static bit_vector get_hash(const bit_vector &input);
|
||||
|
||||
static size_t expected_constraints(const bool ensure_output_bitness=true); // TODO: ignored for now
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
|
||||
#include "gadgetlib1/gadgets/hashes/sha256/sha256_gadget.tcc"
|
||||
|
||||
#endif // SHA256_GADGET_HPP_
|
||||
@@ -0,0 +1,230 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Implementation of interfaces for top-level SHA256 gadgets.
|
||||
|
||||
See sha256_gadget.hpp .
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SHA256_GADGET_TCC_
|
||||
#define SHA256_GADGET_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
sha256_compression_function_gadget<FieldT>::sha256_compression_function_gadget(protoboard<FieldT> &pb,
|
||||
const pb_linear_combination_array<FieldT> &prev_output,
|
||||
const pb_variable_array<FieldT> &new_block,
|
||||
const digest_variable<FieldT> &output,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
prev_output(prev_output),
|
||||
new_block(new_block),
|
||||
output(output)
|
||||
{
|
||||
/* message schedule and inputs for it */
|
||||
packed_W.allocate(pb, 64, FMT(this->annotation_prefix, " packed_W"));
|
||||
message_schedule.reset(new sha256_message_schedule_gadget<FieldT>(pb, new_block, packed_W, FMT(this->annotation_prefix, " message_schedule")));
|
||||
|
||||
/* initalize */
|
||||
round_a.push_back(pb_linear_combination_array<FieldT>(prev_output.rbegin() + 7*32, prev_output.rbegin() + 8*32));
|
||||
round_b.push_back(pb_linear_combination_array<FieldT>(prev_output.rbegin() + 6*32, prev_output.rbegin() + 7*32));
|
||||
round_c.push_back(pb_linear_combination_array<FieldT>(prev_output.rbegin() + 5*32, prev_output.rbegin() + 6*32));
|
||||
round_d.push_back(pb_linear_combination_array<FieldT>(prev_output.rbegin() + 4*32, prev_output.rbegin() + 5*32));
|
||||
round_e.push_back(pb_linear_combination_array<FieldT>(prev_output.rbegin() + 3*32, prev_output.rbegin() + 4*32));
|
||||
round_f.push_back(pb_linear_combination_array<FieldT>(prev_output.rbegin() + 2*32, prev_output.rbegin() + 3*32));
|
||||
round_g.push_back(pb_linear_combination_array<FieldT>(prev_output.rbegin() + 1*32, prev_output.rbegin() + 2*32));
|
||||
round_h.push_back(pb_linear_combination_array<FieldT>(prev_output.rbegin() + 0*32, prev_output.rbegin() + 1*32));
|
||||
|
||||
/* do the rounds */
|
||||
for (size_t i = 0; i < 64; ++i)
|
||||
{
|
||||
round_h.push_back(round_g[i]);
|
||||
round_g.push_back(round_f[i]);
|
||||
round_f.push_back(round_e[i]);
|
||||
round_d.push_back(round_c[i]);
|
||||
round_c.push_back(round_b[i]);
|
||||
round_b.push_back(round_a[i]);
|
||||
|
||||
pb_variable_array<FieldT> new_round_a_variables;
|
||||
new_round_a_variables.allocate(pb, 32, FMT(this->annotation_prefix, " new_round_a_variables_%zu", i+1));
|
||||
round_a.emplace_back(new_round_a_variables);
|
||||
|
||||
pb_variable_array<FieldT> new_round_e_variables;
|
||||
new_round_e_variables.allocate(pb, 32, FMT(this->annotation_prefix, " new_round_e_variables_%zu", i+1));
|
||||
round_e.emplace_back(new_round_e_variables);
|
||||
|
||||
round_functions.push_back(sha256_round_function_gadget<FieldT>(pb,
|
||||
round_a[i], round_b[i], round_c[i], round_d[i],
|
||||
round_e[i], round_f[i], round_g[i], round_h[i],
|
||||
packed_W[i], SHA256_K[i], round_a[i+1], round_e[i+1],
|
||||
FMT(this->annotation_prefix, " round_functions_%zu", i)));
|
||||
}
|
||||
|
||||
/* finalize */
|
||||
unreduced_output.allocate(pb, 8, FMT(this->annotation_prefix, " unreduced_output"));
|
||||
reduced_output.allocate(pb, 8, FMT(this->annotation_prefix, " reduced_output"));
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
{
|
||||
reduce_output.push_back(lastbits_gadget<FieldT>(pb,
|
||||
unreduced_output[i],
|
||||
32+1,
|
||||
reduced_output[i],
|
||||
pb_variable_array<FieldT>(output.bits.rbegin() + (7-i) * 32, output.bits.rbegin() + (8-i) * 32),
|
||||
FMT(this->annotation_prefix, " reduce_output_%zu", i)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void sha256_compression_function_gadget<FieldT>::generate_r1cs_constraints()
|
||||
{
|
||||
message_schedule->generate_r1cs_constraints();
|
||||
for (size_t i = 0; i < 64; ++i)
|
||||
{
|
||||
round_functions[i].generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1,
|
||||
round_functions[3-i].packed_d + round_functions[63-i].packed_new_a,
|
||||
unreduced_output[i]),
|
||||
FMT(this->annotation_prefix, " unreduced_output_%zu", i));
|
||||
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1,
|
||||
round_functions[3-i].packed_h + round_functions[63-i].packed_new_e,
|
||||
unreduced_output[4+i]),
|
||||
FMT(this->annotation_prefix, " unreduced_output_%zu", 4+i));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
{
|
||||
reduce_output[i].generate_r1cs_constraints();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void sha256_compression_function_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
message_schedule->generate_r1cs_witness();
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Input:\n");
|
||||
for (size_t j = 0; j < 16; ++j)
|
||||
{
|
||||
printf("%lx ", this->pb.val(packed_W[j]).as_ulong());
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < 64; ++i)
|
||||
{
|
||||
round_functions[i].generate_r1cs_witness();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
this->pb.val(unreduced_output[i]) = this->pb.val(round_functions[3-i].packed_d) + this->pb.val(round_functions[63-i].packed_new_a);
|
||||
this->pb.val(unreduced_output[4+i]) = this->pb.val(round_functions[3-i].packed_h) + this->pb.val(round_functions[63-i].packed_new_e);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
{
|
||||
reduce_output[i].generate_r1cs_witness();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Output:\n");
|
||||
for (size_t j = 0; j < 8; ++j)
|
||||
{
|
||||
printf("%lx ", this->pb.val(reduced_output[j]).as_ulong());
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
sha256_two_to_one_hash_gadget<FieldT>::sha256_two_to_one_hash_gadget(protoboard<FieldT> &pb,
|
||||
const digest_variable<FieldT> &left,
|
||||
const digest_variable<FieldT> &right,
|
||||
const digest_variable<FieldT> &output,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix)
|
||||
{
|
||||
/* concatenate block = left || right */
|
||||
pb_variable_array<FieldT> block;
|
||||
block.insert(block.end(), left.bits.begin(), left.bits.end());
|
||||
block.insert(block.end(), right.bits.begin(), right.bits.end());
|
||||
|
||||
/* compute the hash itself */
|
||||
f.reset(new sha256_compression_function_gadget<FieldT>(pb, SHA256_default_IV<FieldT>(pb), block, output, FMT(this->annotation_prefix, " f")));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
sha256_two_to_one_hash_gadget<FieldT>::sha256_two_to_one_hash_gadget(protoboard<FieldT> &pb,
|
||||
const size_t block_length,
|
||||
const block_variable<FieldT> &input_block,
|
||||
const digest_variable<FieldT> &output,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix)
|
||||
{
|
||||
assert(block_length == SHA256_block_size);
|
||||
assert(input_block.bits.size() == block_length);
|
||||
f.reset(new sha256_compression_function_gadget<FieldT>(pb, SHA256_default_IV<FieldT>(pb), input_block.bits, output, FMT(this->annotation_prefix, " f")));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void sha256_two_to_one_hash_gadget<FieldT>::generate_r1cs_constraints(const bool ensure_output_bitness)
|
||||
{
|
||||
UNUSED(ensure_output_bitness);
|
||||
f->generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void sha256_two_to_one_hash_gadget<FieldT>::generate_r1cs_witness()
|
||||
{
|
||||
f->generate_r1cs_witness();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
size_t sha256_two_to_one_hash_gadget<FieldT>::get_block_len()
|
||||
{
|
||||
return SHA256_block_size;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
size_t sha256_two_to_one_hash_gadget<FieldT>::get_digest_len()
|
||||
{
|
||||
return SHA256_digest_size;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector sha256_two_to_one_hash_gadget<FieldT>::get_hash(const bit_vector &input)
|
||||
{
|
||||
protoboard<FieldT> pb;
|
||||
|
||||
block_variable<FieldT> input_variable(pb, SHA256_block_size, "input");
|
||||
digest_variable<FieldT> output_variable(pb, SHA256_digest_size, "output");
|
||||
sha256_two_to_one_hash_gadget<FieldT> f(pb, SHA256_block_size, input_variable, output_variable, "f");
|
||||
|
||||
input_variable.generate_r1cs_witness(input);
|
||||
f.generate_r1cs_witness();
|
||||
|
||||
return output_variable.get_digest();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
size_t sha256_two_to_one_hash_gadget<FieldT>::expected_constraints(const bool ensure_output_bitness)
|
||||
{
|
||||
UNUSED(ensure_output_bitness);
|
||||
return 27280; /* hardcoded for now */
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
|
||||
#endif // SHA256_GADGET_TCC_
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python
|
||||
##
|
||||
# @author This file is part of libsnark, developed by SCIPR Lab
|
||||
# and contributors (see AUTHORS).
|
||||
# @copyright MIT license (see LICENSE file)
|
||||
|
||||
import random
|
||||
import pypy_sha256 # PyPy's implementation of SHA256 compression function; see copyright and authorship notice within.
|
||||
|
||||
BLOCK_LEN = 512
|
||||
BLOCK_BYTES = BLOCK_LEN // 8
|
||||
HASH_LEN = 256
|
||||
HASH_BYTES = HASH_LEN // 8
|
||||
|
||||
def gen_random_bytes(n):
|
||||
return [random.randint(0, 255) for i in xrange(n)]
|
||||
|
||||
def words_to_bytes(arr):
|
||||
return sum(([x >> 24, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff] for x in arr), [])
|
||||
|
||||
def bytes_to_words(arr):
|
||||
l = len(arr)
|
||||
assert l % 4 == 0
|
||||
return [(arr[i*4 + 3] << 24) + (arr[i*4+2] << 16) + (arr[i*4+1] << 8) + arr[i*4] for i in xrange(l//4)]
|
||||
|
||||
def cpp_val(s, log_radix=32):
|
||||
if log_radix == 8:
|
||||
hexfmt = '0x%02x'
|
||||
elif log_radix == 32:
|
||||
hexfmt = '0x%08x'
|
||||
s = bytes_to_words(s)
|
||||
else:
|
||||
raise
|
||||
return 'int_list_to_bits({%s}, %d)' % (', '.join(hexfmt % x for x in s), log_radix)
|
||||
|
||||
def H_bytes(x):
|
||||
assert len(x) == BLOCK_BYTES
|
||||
state = pypy_sha256.sha_init()
|
||||
state['data'] = words_to_bytes(bytes_to_words(x))
|
||||
pypy_sha256.sha_transform(state)
|
||||
return words_to_bytes(bytes_to_words(words_to_bytes(state['digest'])))
|
||||
|
||||
def generate_sha256_gadget_tests():
|
||||
left = gen_random_bytes(HASH_BYTES)
|
||||
right = gen_random_bytes(HASH_BYTES)
|
||||
hash = H_bytes(left + right)
|
||||
|
||||
print "const bit_vector left_bv = %s;" % cpp_val(left)
|
||||
print "const bit_vector right_bv = %s;" % cpp_val(right)
|
||||
print "const bit_vector hash_bv = %s;" % cpp_val(hash)
|
||||
|
||||
if __name__ == '__main__':
|
||||
random.seed(0) # for reproducibility
|
||||
generate_sha256_gadget_tests()
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# SHA256 compression function implementation below is a verbatim copy of PyPy's implementation from
|
||||
# https://bitbucket.org/pypy/pypy/raw/f1f064b3faf1e012f7a9a9ab08f18074637ebe8a/lib_pypy/_sha256.py .
|
||||
#
|
||||
# It is licensed under the MIT license and copyright PyPy Copyright holders 2003-2015
|
||||
# See https://bitbucket.org/pypy/pypy/src/tip/LICENSE for the full copyright notice.
|
||||
#
|
||||
|
||||
SHA_BLOCKSIZE = 64
|
||||
SHA_DIGESTSIZE = 32
|
||||
|
||||
|
||||
def new_shaobject():
|
||||
return {
|
||||
'digest': [0]*8,
|
||||
'count_lo': 0,
|
||||
'count_hi': 0,
|
||||
'data': [0]* SHA_BLOCKSIZE,
|
||||
'local': 0,
|
||||
'digestsize': 0
|
||||
}
|
||||
|
||||
ROR = lambda x, y: (((x & 0xffffffff) >> (y & 31)) | (x << (32 - (y & 31)))) & 0xffffffff
|
||||
Ch = lambda x, y, z: (z ^ (x & (y ^ z)))
|
||||
Maj = lambda x, y, z: (((x | y) & z) | (x & y))
|
||||
S = lambda x, n: ROR(x, n)
|
||||
R = lambda x, n: (x & 0xffffffff) >> n
|
||||
Sigma0 = lambda x: (S(x, 2) ^ S(x, 13) ^ S(x, 22))
|
||||
Sigma1 = lambda x: (S(x, 6) ^ S(x, 11) ^ S(x, 25))
|
||||
Gamma0 = lambda x: (S(x, 7) ^ S(x, 18) ^ R(x, 3))
|
||||
Gamma1 = lambda x: (S(x, 17) ^ S(x, 19) ^ R(x, 10))
|
||||
|
||||
def sha_transform(sha_info):
|
||||
W = []
|
||||
|
||||
d = sha_info['data']
|
||||
for i in range(0,16):
|
||||
W.append( (d[4*i]<<24) + (d[4*i+1]<<16) + (d[4*i+2]<<8) + d[4*i+3])
|
||||
|
||||
for i in range(16,64):
|
||||
W.append( (Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]) & 0xffffffff )
|
||||
|
||||
ss = sha_info['digest'][:]
|
||||
|
||||
def RND(a,b,c,d,e,f,g,h,i,ki):
|
||||
t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i];
|
||||
t1 = Sigma0(a) + Maj(a, b, c);
|
||||
d += t0;
|
||||
h = t0 + t1;
|
||||
return d & 0xffffffff, h & 0xffffffff
|
||||
|
||||
ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],0,0x428a2f98);
|
||||
ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],1,0x71374491);
|
||||
ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],2,0xb5c0fbcf);
|
||||
ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],3,0xe9b5dba5);
|
||||
ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],4,0x3956c25b);
|
||||
ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],5,0x59f111f1);
|
||||
ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],6,0x923f82a4);
|
||||
ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],7,0xab1c5ed5);
|
||||
ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],8,0xd807aa98);
|
||||
ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],9,0x12835b01);
|
||||
ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],10,0x243185be);
|
||||
ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],11,0x550c7dc3);
|
||||
ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],12,0x72be5d74);
|
||||
ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],13,0x80deb1fe);
|
||||
ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],14,0x9bdc06a7);
|
||||
ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],15,0xc19bf174);
|
||||
ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],16,0xe49b69c1);
|
||||
ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],17,0xefbe4786);
|
||||
ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],18,0x0fc19dc6);
|
||||
ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],19,0x240ca1cc);
|
||||
ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],20,0x2de92c6f);
|
||||
ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],21,0x4a7484aa);
|
||||
ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],22,0x5cb0a9dc);
|
||||
ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],23,0x76f988da);
|
||||
ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],24,0x983e5152);
|
||||
ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],25,0xa831c66d);
|
||||
ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],26,0xb00327c8);
|
||||
ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],27,0xbf597fc7);
|
||||
ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],28,0xc6e00bf3);
|
||||
ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],29,0xd5a79147);
|
||||
ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],30,0x06ca6351);
|
||||
ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],31,0x14292967);
|
||||
ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],32,0x27b70a85);
|
||||
ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],33,0x2e1b2138);
|
||||
ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],34,0x4d2c6dfc);
|
||||
ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],35,0x53380d13);
|
||||
ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],36,0x650a7354);
|
||||
ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],37,0x766a0abb);
|
||||
ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],38,0x81c2c92e);
|
||||
ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],39,0x92722c85);
|
||||
ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],40,0xa2bfe8a1);
|
||||
ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],41,0xa81a664b);
|
||||
ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],42,0xc24b8b70);
|
||||
ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],43,0xc76c51a3);
|
||||
ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],44,0xd192e819);
|
||||
ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],45,0xd6990624);
|
||||
ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],46,0xf40e3585);
|
||||
ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],47,0x106aa070);
|
||||
ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],48,0x19a4c116);
|
||||
ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],49,0x1e376c08);
|
||||
ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],50,0x2748774c);
|
||||
ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],51,0x34b0bcb5);
|
||||
ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],52,0x391c0cb3);
|
||||
ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],53,0x4ed8aa4a);
|
||||
ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],54,0x5b9cca4f);
|
||||
ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],55,0x682e6ff3);
|
||||
ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],56,0x748f82ee);
|
||||
ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],57,0x78a5636f);
|
||||
ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],58,0x84c87814);
|
||||
ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],59,0x8cc70208);
|
||||
ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],60,0x90befffa);
|
||||
ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],61,0xa4506ceb);
|
||||
ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],62,0xbef9a3f7);
|
||||
ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],63,0xc67178f2);
|
||||
|
||||
dig = []
|
||||
for i, x in enumerate(sha_info['digest']):
|
||||
dig.append( (x + ss[i]) & 0xffffffff )
|
||||
sha_info['digest'] = dig
|
||||
|
||||
def sha_init():
|
||||
sha_info = new_shaobject()
|
||||
sha_info['digest'] = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19]
|
||||
sha_info['count_lo'] = 0
|
||||
sha_info['count_hi'] = 0
|
||||
sha_info['local'] = 0
|
||||
sha_info['digestsize'] = 32
|
||||
return sha_info
|
||||
|
||||
def sha224_init():
|
||||
sha_info = new_shaobject()
|
||||
sha_info['digest'] = [0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4]
|
||||
sha_info['count_lo'] = 0
|
||||
sha_info['count_hi'] = 0
|
||||
sha_info['local'] = 0
|
||||
sha_info['digestsize'] = 28
|
||||
return sha_info
|
||||
|
||||
def sha_update(sha_info, buffer):
|
||||
if isinstance(buffer, str):
|
||||
raise TypeError("Unicode strings must be encoded before hashing")
|
||||
count = len(buffer)
|
||||
buffer_idx = 0
|
||||
clo = (sha_info['count_lo'] + (count << 3)) & 0xffffffff
|
||||
if clo < sha_info['count_lo']:
|
||||
sha_info['count_hi'] += 1
|
||||
sha_info['count_lo'] = clo
|
||||
|
||||
sha_info['count_hi'] += (count >> 29)
|
||||
|
||||
if sha_info['local']:
|
||||
i = SHA_BLOCKSIZE - sha_info['local']
|
||||
if i > count:
|
||||
i = count
|
||||
|
||||
# copy buffer
|
||||
sha_info['data'][sha_info['local']:sha_info['local']+i] = buffer[buffer_idx:buffer_idx+i]
|
||||
|
||||
count -= i
|
||||
buffer_idx += i
|
||||
|
||||
sha_info['local'] += i
|
||||
if sha_info['local'] == SHA_BLOCKSIZE:
|
||||
sha_transform(sha_info)
|
||||
sha_info['local'] = 0
|
||||
else:
|
||||
return
|
||||
|
||||
while count >= SHA_BLOCKSIZE:
|
||||
# copy buffer
|
||||
sha_info['data'] = list(buffer[buffer_idx:buffer_idx + SHA_BLOCKSIZE])
|
||||
count -= SHA_BLOCKSIZE
|
||||
buffer_idx += SHA_BLOCKSIZE
|
||||
sha_transform(sha_info)
|
||||
|
||||
|
||||
# copy buffer
|
||||
pos = sha_info['local']
|
||||
sha_info['data'][pos:pos+count] = buffer[buffer_idx:buffer_idx + count]
|
||||
sha_info['local'] = count
|
||||
|
||||
def sha_final(sha_info):
|
||||
lo_bit_count = sha_info['count_lo']
|
||||
hi_bit_count = sha_info['count_hi']
|
||||
count = (lo_bit_count >> 3) & 0x3f
|
||||
sha_info['data'][count] = 0x80;
|
||||
count += 1
|
||||
if count > SHA_BLOCKSIZE - 8:
|
||||
# zero the bytes in data after the count
|
||||
sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count))
|
||||
sha_transform(sha_info)
|
||||
# zero bytes in data
|
||||
sha_info['data'] = [0] * SHA_BLOCKSIZE
|
||||
else:
|
||||
sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count))
|
||||
|
||||
sha_info['data'][56] = (hi_bit_count >> 24) & 0xff
|
||||
sha_info['data'][57] = (hi_bit_count >> 16) & 0xff
|
||||
sha_info['data'][58] = (hi_bit_count >> 8) & 0xff
|
||||
sha_info['data'][59] = (hi_bit_count >> 0) & 0xff
|
||||
sha_info['data'][60] = (lo_bit_count >> 24) & 0xff
|
||||
sha_info['data'][61] = (lo_bit_count >> 16) & 0xff
|
||||
sha_info['data'][62] = (lo_bit_count >> 8) & 0xff
|
||||
sha_info['data'][63] = (lo_bit_count >> 0) & 0xff
|
||||
|
||||
sha_transform(sha_info)
|
||||
|
||||
dig = []
|
||||
for i in sha_info['digest']:
|
||||
dig.extend([ ((i>>24) & 0xff), ((i>>16) & 0xff), ((i>>8) & 0xff), (i & 0xff) ])
|
||||
return ''.join([chr(i) for i in dig])
|
||||
|
||||
class sha256(object):
|
||||
digest_size = digestsize = SHA_DIGESTSIZE
|
||||
block_size = SHA_BLOCKSIZE
|
||||
|
||||
def __init__(self, s=None):
|
||||
self._sha = sha_init()
|
||||
if s:
|
||||
sha_update(self._sha, s)
|
||||
|
||||
def update(self, s):
|
||||
sha_update(self._sha, s)
|
||||
|
||||
def digest(self):
|
||||
return sha_final(self._sha.copy())[:self._sha['digestsize']]
|
||||
|
||||
def hexdigest(self):
|
||||
return ''.join(['%.2x' % ord(i) for i in self.digest()])
|
||||
|
||||
def copy(self):
|
||||
new = sha256.__new__(sha256)
|
||||
new._sha = self._sha.copy()
|
||||
return new
|
||||
|
||||
class sha224(sha256):
|
||||
digest_size = digestsize = 28
|
||||
|
||||
def __init__(self, s=None):
|
||||
self._sha = sha224_init()
|
||||
if s:
|
||||
sha_update(self._sha, s)
|
||||
|
||||
def copy(self):
|
||||
new = sha224.__new__(sha224)
|
||||
new._sha = self._sha.copy()
|
||||
return new
|
||||
|
||||
def test():
|
||||
a_str = "just a test string"
|
||||
|
||||
assert 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' == sha256().hexdigest()
|
||||
assert 'd7b553c6f09ac85d142415f857c5310f3bbbe7cdd787cce4b985acedd585266f' == sha256(a_str).hexdigest()
|
||||
assert '8113ebf33c97daa9998762aacafe750c7cefc2b2f173c90c59663a57fe626f21' == sha256(a_str*7).hexdigest()
|
||||
|
||||
s = sha256(a_str)
|
||||
s.update(a_str)
|
||||
assert '03d9963e05a094593190b6fc794cb1a3e1ac7d7883f0b5855268afeccc70d461' == s.hexdigest()
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
||||
@@ -0,0 +1,48 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#include "common/default_types/ec_pp.hpp"
|
||||
#include "common/utils.hpp"
|
||||
#include "common/profiling.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace libsnark;
|
||||
|
||||
template<typename FieldT>
|
||||
void test_two_to_one()
|
||||
{
|
||||
protoboard<FieldT> pb;
|
||||
|
||||
digest_variable<FieldT> left(pb, SHA256_digest_size, "left");
|
||||
digest_variable<FieldT> right(pb, SHA256_digest_size, "right");
|
||||
digest_variable<FieldT> output(pb, SHA256_digest_size, "output");
|
||||
|
||||
sha256_two_to_one_hash_gadget<FieldT> f(pb, left, right, output, "f");
|
||||
f.generate_r1cs_constraints();
|
||||
printf("Number of constraints for sha256_two_to_one_hash_gadget: %zu\n", pb.num_constraints());
|
||||
|
||||
const bit_vector left_bv = int_list_to_bits({0x426bc2d8, 0x4dc86782, 0x81e8957a, 0x409ec148, 0xe6cffbe8, 0xafe6ba4f, 0x9c6f1978, 0xdd7af7e9}, 32);
|
||||
const bit_vector right_bv = int_list_to_bits({0x038cce42, 0xabd366b8, 0x3ede7e00, 0x9130de53, 0x72cdf73d, 0xee825114, 0x8cb48d1b, 0x9af68ad0}, 32);
|
||||
const bit_vector hash_bv = int_list_to_bits({0xeffd0b7f, 0x1ccba116, 0x2ee816f7, 0x31c62b48, 0x59305141, 0x990e5c0a, 0xce40d33d, 0x0b1167d1}, 32);
|
||||
|
||||
left.generate_r1cs_witness(left_bv);
|
||||
right.generate_r1cs_witness(right_bv);
|
||||
|
||||
f.generate_r1cs_witness();
|
||||
output.generate_r1cs_witness(hash_bv);
|
||||
|
||||
EXPECT_TRUE(pb.is_satisfied());
|
||||
}
|
||||
|
||||
TEST(gadgetlib1, sha256)
|
||||
{
|
||||
start_profiling();
|
||||
default_ec_pp::init_public_params();
|
||||
test_two_to_one<Fr<default_ec_pp> >();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP_
|
||||
#define MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP_
|
||||
|
||||
#include "common/data_structures/merkle_tree.hpp"
|
||||
#include "gadgetlib1/gadget.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/hash_io.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
class merkle_authentication_path_variable : public gadget<FieldT> {
|
||||
public:
|
||||
|
||||
const size_t tree_depth;
|
||||
std::vector<digest_variable<FieldT> > left_digests;
|
||||
std::vector<digest_variable<FieldT> > right_digests;
|
||||
|
||||
merkle_authentication_path_variable(protoboard<FieldT> &pb,
|
||||
const size_t tree_depth,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness(const size_t address, const merkle_authentication_path &path);
|
||||
merkle_authentication_path get_authentication_path(const size_t address) const;
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
|
||||
#include "gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.tcc"
|
||||
|
||||
#endif // MERKLE_AUTHENTICATION_PATH_VARIABLE_HPP
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MERKLE_AUTHENTICATION_PATH_VARIABLE_TCC_
|
||||
#define MERKLE_AUTHENTICATION_PATH_VARIABLE_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
merkle_authentication_path_variable<FieldT, HashT>::merkle_authentication_path_variable(protoboard<FieldT> &pb,
|
||||
const size_t tree_depth,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
tree_depth(tree_depth)
|
||||
{
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
left_digests.emplace_back(digest_variable<FieldT>(pb, HashT::get_digest_len(), FMT(annotation_prefix, " left_digests_%zu", i)));
|
||||
right_digests.emplace_back(digest_variable<FieldT>(pb, HashT::get_digest_len(), FMT(annotation_prefix, " right_digests_%zu", i)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void merkle_authentication_path_variable<FieldT, HashT>::generate_r1cs_constraints()
|
||||
{
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
left_digests[i].generate_r1cs_constraints();
|
||||
right_digests[i].generate_r1cs_constraints();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void merkle_authentication_path_variable<FieldT, HashT>::generate_r1cs_witness(const size_t address, const merkle_authentication_path &path)
|
||||
{
|
||||
assert(path.size() == tree_depth);
|
||||
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
if (address & (1ul << (tree_depth-1-i)))
|
||||
{
|
||||
left_digests[i].generate_r1cs_witness(path[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
right_digests[i].generate_r1cs_witness(path[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
merkle_authentication_path merkle_authentication_path_variable<FieldT, HashT>::get_authentication_path(const size_t address) const
|
||||
{
|
||||
merkle_authentication_path result;
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
if (address & (1ul << (tree_depth-1-i)))
|
||||
{
|
||||
result.emplace_back(left_digests[i].get_digest());
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace_back(right_digests[i].get_digest());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
|
||||
#endif // MERKLE_AUTHENTICATION_PATH_VARIABLE_TCC
|
||||
@@ -0,0 +1,73 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Declaration of interfaces for the Merkle tree check read gadget.
|
||||
|
||||
The gadget checks the following: given a root R, address A, value V, and
|
||||
authentication path P, check that P is a valid authentication path for the
|
||||
value V as the A-th leaf in a Merkle tree with root R.
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MERKLE_TREE_CHECK_READ_GADGET_HPP_
|
||||
#define MERKLE_TREE_CHECK_READ_GADGET_HPP_
|
||||
|
||||
#include "common/data_structures/merkle_tree.hpp"
|
||||
#include "gadgetlib1/gadget.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/hash_io.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/digest_selector_gadget.hpp"
|
||||
#include "gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
class merkle_tree_check_read_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
|
||||
std::vector<HashT> hashers;
|
||||
std::vector<block_variable<FieldT> > hasher_inputs;
|
||||
std::vector<digest_selector_gadget<FieldT> > propagators;
|
||||
std::vector<digest_variable<FieldT> > internal_output;
|
||||
|
||||
std::shared_ptr<digest_variable<FieldT> > computed_root;
|
||||
std::shared_ptr<bit_vector_copy_gadget<FieldT> > check_root;
|
||||
|
||||
public:
|
||||
|
||||
const size_t digest_size;
|
||||
const size_t tree_depth;
|
||||
pb_linear_combination_array<FieldT> address_bits;
|
||||
digest_variable<FieldT> leaf;
|
||||
digest_variable<FieldT> root;
|
||||
merkle_authentication_path_variable<FieldT, HashT> path;
|
||||
pb_linear_combination<FieldT> read_successful;
|
||||
|
||||
merkle_tree_check_read_gadget(protoboard<FieldT> &pb,
|
||||
const size_t tree_depth,
|
||||
const pb_linear_combination_array<FieldT> &address_bits,
|
||||
const digest_variable<FieldT> &leaf_digest,
|
||||
const digest_variable<FieldT> &root_digest,
|
||||
const merkle_authentication_path_variable<FieldT, HashT> &path,
|
||||
const pb_linear_combination<FieldT> &read_successful,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
|
||||
static size_t root_size_in_bits();
|
||||
/* for debugging purposes */
|
||||
static size_t expected_constraints(const size_t tree_depth);
|
||||
};
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void test_merkle_tree_check_read_gadget();
|
||||
|
||||
} // libsnark
|
||||
|
||||
#include "gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.tcc"
|
||||
|
||||
#endif // MERKLE_TREE_CHECK_READ_GADGET_HPP_
|
||||
@@ -0,0 +1,196 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Implementation of interfaces for the Merkle tree check read.
|
||||
|
||||
See merkle_tree_check_read_gadget.hpp .
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MERKLE_TREE_CHECK_READ_GADGET_TCC_
|
||||
#define MERKLE_TREE_CHECK_READ_GADGET_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
merkle_tree_check_read_gadget<FieldT, HashT>::merkle_tree_check_read_gadget(protoboard<FieldT> &pb,
|
||||
const size_t tree_depth,
|
||||
const pb_linear_combination_array<FieldT> &address_bits,
|
||||
const digest_variable<FieldT> &leaf,
|
||||
const digest_variable<FieldT> &root,
|
||||
const merkle_authentication_path_variable<FieldT, HashT> &path,
|
||||
const pb_linear_combination<FieldT> &read_successful,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
digest_size(HashT::get_digest_len()),
|
||||
tree_depth(tree_depth),
|
||||
address_bits(address_bits),
|
||||
leaf(leaf),
|
||||
root(root),
|
||||
path(path),
|
||||
read_successful(read_successful)
|
||||
{
|
||||
/*
|
||||
The tricky part here is ordering. For Merkle tree
|
||||
authentication paths, path[0] corresponds to one layer below
|
||||
the root (and path[tree_depth-1] corresponds to the layer
|
||||
containing the leaf), while address_bits has the reverse order:
|
||||
address_bits[0] is LSB, and corresponds to layer containing the
|
||||
leaf, and address_bits[tree_depth-1] is MSB, and corresponds to
|
||||
the subtree directly under the root.
|
||||
*/
|
||||
assert(tree_depth > 0);
|
||||
assert(tree_depth == address_bits.size());
|
||||
|
||||
for (size_t i = 0; i < tree_depth-1; ++i)
|
||||
{
|
||||
internal_output.emplace_back(digest_variable<FieldT>(pb, digest_size, FMT(this->annotation_prefix, " internal_output_%zu", i)));
|
||||
}
|
||||
|
||||
computed_root.reset(new digest_variable<FieldT>(pb, digest_size, FMT(this->annotation_prefix, " computed_root")));
|
||||
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
block_variable<FieldT> inp(pb, path.left_digests[i], path.right_digests[i], FMT(this->annotation_prefix, " inp_%zu", i));
|
||||
hasher_inputs.emplace_back(inp);
|
||||
hashers.emplace_back(HashT(pb, 2*digest_size, inp, (i == 0 ? *computed_root : internal_output[i-1]),
|
||||
FMT(this->annotation_prefix, " load_hashers_%zu", i)));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
/*
|
||||
The propagators take a computed hash value (or leaf in the
|
||||
base case) and propagate it one layer up, either in the left
|
||||
or the right slot of authentication_path_variable.
|
||||
*/
|
||||
propagators.emplace_back(digest_selector_gadget<FieldT>(pb, digest_size, i < tree_depth - 1 ? internal_output[i] : leaf,
|
||||
address_bits[tree_depth-1-i], path.left_digests[i], path.right_digests[i],
|
||||
FMT(this->annotation_prefix, " digest_selector_%zu", i)));
|
||||
}
|
||||
|
||||
check_root.reset(new bit_vector_copy_gadget<FieldT>(pb, computed_root->bits, root.bits, read_successful, FieldT::capacity(), FMT(annotation_prefix, " check_root")));
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void merkle_tree_check_read_gadget<FieldT, HashT>::generate_r1cs_constraints()
|
||||
{
|
||||
/* ensure correct hash computations */
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
// Note that we check root outside and have enforced booleanity of path.left_digests/path.right_digests outside in path.generate_r1cs_constraints
|
||||
hashers[i].generate_r1cs_constraints(false);
|
||||
}
|
||||
|
||||
/* ensure consistency of path.left_digests/path.right_digests with internal_output */
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
propagators[i].generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
check_root->generate_r1cs_constraints(false, false);
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void merkle_tree_check_read_gadget<FieldT, HashT>::generate_r1cs_witness()
|
||||
{
|
||||
/* do the hash computations bottom-up */
|
||||
for (int i = tree_depth-1; i >= 0; --i)
|
||||
{
|
||||
/* propagate previous input */
|
||||
propagators[i].generate_r1cs_witness();
|
||||
|
||||
/* compute hash */
|
||||
hashers[i].generate_r1cs_witness();
|
||||
}
|
||||
|
||||
check_root->generate_r1cs_witness();
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
size_t merkle_tree_check_read_gadget<FieldT, HashT>::root_size_in_bits()
|
||||
{
|
||||
return HashT::get_digest_len();
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
size_t merkle_tree_check_read_gadget<FieldT, HashT>::expected_constraints(const size_t tree_depth)
|
||||
{
|
||||
/* NB: this includes path constraints */
|
||||
const size_t hasher_constraints = tree_depth * HashT::expected_constraints(false);
|
||||
const size_t propagator_constraints = tree_depth * HashT::get_digest_len();
|
||||
const size_t authentication_path_constraints = 2 * tree_depth * HashT::get_digest_len();
|
||||
const size_t check_root_constraints = 3 * div_ceil(HashT::get_digest_len(), FieldT::capacity());
|
||||
|
||||
return hasher_constraints + propagator_constraints + authentication_path_constraints + check_root_constraints;
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void test_merkle_tree_check_read_gadget()
|
||||
{
|
||||
/* prepare test */
|
||||
const size_t digest_len = HashT::get_digest_len();
|
||||
const size_t tree_depth = 16;
|
||||
std::vector<merkle_authentication_node> path(tree_depth);
|
||||
|
||||
bit_vector prev_hash(digest_len);
|
||||
std::generate(prev_hash.begin(), prev_hash.end(), [&]() { return std::rand() % 2; });
|
||||
bit_vector leaf = prev_hash;
|
||||
|
||||
bit_vector address_bits;
|
||||
|
||||
size_t address = 0;
|
||||
for (long level = tree_depth-1; level >= 0; --level)
|
||||
{
|
||||
const bool computed_is_right = (std::rand() % 2);
|
||||
address |= (computed_is_right ? 1ul << (tree_depth-1-level) : 0);
|
||||
address_bits.push_back(computed_is_right);
|
||||
bit_vector other(digest_len);
|
||||
std::generate(other.begin(), other.end(), [&]() { return std::rand() % 2; });
|
||||
|
||||
bit_vector block = prev_hash;
|
||||
block.insert(computed_is_right ? block.begin() : block.end(), other.begin(), other.end());
|
||||
bit_vector h = HashT::get_hash(block);
|
||||
|
||||
path[level] = other;
|
||||
|
||||
prev_hash = h;
|
||||
}
|
||||
bit_vector root = prev_hash;
|
||||
|
||||
/* execute test */
|
||||
protoboard<FieldT> pb;
|
||||
pb_variable_array<FieldT> address_bits_va;
|
||||
address_bits_va.allocate(pb, tree_depth, "address_bits");
|
||||
digest_variable<FieldT> leaf_digest(pb, digest_len, "input_block");
|
||||
digest_variable<FieldT> root_digest(pb, digest_len, "output_digest");
|
||||
merkle_authentication_path_variable<FieldT, HashT> path_var(pb, tree_depth, "path_var");
|
||||
merkle_tree_check_read_gadget<FieldT, HashT> ml(pb, tree_depth, address_bits_va, leaf_digest, root_digest, path_var, ONE, "ml");
|
||||
|
||||
path_var.generate_r1cs_constraints();
|
||||
ml.generate_r1cs_constraints();
|
||||
|
||||
address_bits_va.fill_with_bits(pb, address_bits);
|
||||
assert(address_bits_va.get_field_element_from_bits(pb).as_ulong() == address);
|
||||
leaf_digest.generate_r1cs_witness(leaf);
|
||||
path_var.generate_r1cs_witness(address, path);
|
||||
ml.generate_r1cs_witness();
|
||||
|
||||
/* make sure that read checker didn't accidentally overwrite anything */
|
||||
address_bits_va.fill_with_bits(pb, address_bits);
|
||||
leaf_digest.generate_r1cs_witness(leaf);
|
||||
root_digest.generate_r1cs_witness(root);
|
||||
assert(pb.is_satisfied());
|
||||
|
||||
const size_t num_constraints = pb.num_constraints();
|
||||
const size_t expected_constraints = merkle_tree_check_read_gadget<FieldT, HashT>::expected_constraints(tree_depth);
|
||||
assert(num_constraints == expected_constraints);
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
|
||||
#endif // MERKLE_TREE_CHECK_READ_GADGET_TCC_
|
||||
@@ -0,0 +1,90 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Declaration of interfaces for the Merkle tree check read gadget.
|
||||
|
||||
The gadget checks the following: given two roots R1 and R2, address A, two
|
||||
values V1 and V2, and authentication path P, check that
|
||||
- P is a valid authentication path for the value V1 as the A-th leaf in a Merkle tree with root R1, and
|
||||
- P is a valid authentication path for the value V2 as the A-th leaf in a Merkle tree with root R2.
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MERKLE_TREE_CHECK_UPDATE_GADGET_HPP_
|
||||
#define MERKLE_TREE_CHECK_UPDATE_GADGET_HPP_
|
||||
|
||||
#include "common/data_structures/merkle_tree.hpp"
|
||||
#include "gadgetlib1/gadget.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/hash_io.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/digest_selector_gadget.hpp"
|
||||
#include "gadgetlib1/gadgets/merkle_tree/merkle_authentication_path_variable.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
class merkle_tree_check_update_gadget : public gadget<FieldT> {
|
||||
private:
|
||||
|
||||
std::vector<HashT> prev_hashers;
|
||||
std::vector<block_variable<FieldT> > prev_hasher_inputs;
|
||||
std::vector<digest_selector_gadget<FieldT> > prev_propagators;
|
||||
std::vector<digest_variable<FieldT> > prev_internal_output;
|
||||
|
||||
std::vector<HashT> next_hashers;
|
||||
std::vector<block_variable<FieldT> > next_hasher_inputs;
|
||||
std::vector<digest_selector_gadget<FieldT> > next_propagators;
|
||||
std::vector<digest_variable<FieldT> > next_internal_output;
|
||||
|
||||
std::shared_ptr<digest_variable<FieldT> > computed_next_root;
|
||||
std::shared_ptr<bit_vector_copy_gadget<FieldT> > check_next_root;
|
||||
|
||||
public:
|
||||
|
||||
const size_t digest_size;
|
||||
const size_t tree_depth;
|
||||
|
||||
pb_variable_array<FieldT> address_bits;
|
||||
digest_variable<FieldT> prev_leaf_digest;
|
||||
digest_variable<FieldT> prev_root_digest;
|
||||
merkle_authentication_path_variable<FieldT, HashT> prev_path;
|
||||
digest_variable<FieldT> next_leaf_digest;
|
||||
digest_variable<FieldT> next_root_digest;
|
||||
merkle_authentication_path_variable<FieldT, HashT> next_path;
|
||||
pb_linear_combination<FieldT> update_successful;
|
||||
|
||||
/* Note that while it is necessary to generate R1CS constraints
|
||||
for prev_path, it is not necessary to do so for next_path. See
|
||||
comment in the implementation of generate_r1cs_constraints() */
|
||||
|
||||
merkle_tree_check_update_gadget(protoboard<FieldT> &pb,
|
||||
const size_t tree_depth,
|
||||
const pb_variable_array<FieldT> &address_bits,
|
||||
const digest_variable<FieldT> &prev_leaf_digest,
|
||||
const digest_variable<FieldT> &prev_root_digest,
|
||||
const merkle_authentication_path_variable<FieldT, HashT> &prev_path,
|
||||
const digest_variable<FieldT> &next_leaf_digest,
|
||||
const digest_variable<FieldT> &next_root_digest,
|
||||
const merkle_authentication_path_variable<FieldT, HashT> &next_path,
|
||||
const pb_linear_combination<FieldT> &update_successful,
|
||||
const std::string &annotation_prefix);
|
||||
|
||||
void generate_r1cs_constraints();
|
||||
void generate_r1cs_witness();
|
||||
|
||||
static size_t root_size_in_bits();
|
||||
/* for debugging purposes */
|
||||
static size_t expected_constraints(const size_t tree_depth);
|
||||
};
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void test_merkle_tree_check_update_gadget();
|
||||
|
||||
} // libsnark
|
||||
|
||||
#include "gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.tcc"
|
||||
|
||||
#endif // MERKLE_TREE_CHECK_UPDATE_GADGET_HPP_
|
||||
@@ -0,0 +1,265 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
|
||||
Implementation of interfaces for the Merkle tree check update gadget.
|
||||
|
||||
See merkle_tree_check_update_gadget.hpp .
|
||||
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_
|
||||
#define MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
merkle_tree_check_update_gadget<FieldT, HashT>::merkle_tree_check_update_gadget(protoboard<FieldT> &pb,
|
||||
const size_t tree_depth,
|
||||
const pb_variable_array<FieldT> &address_bits,
|
||||
const digest_variable<FieldT> &prev_leaf_digest,
|
||||
const digest_variable<FieldT> &prev_root_digest,
|
||||
const merkle_authentication_path_variable<FieldT, HashT> &prev_path,
|
||||
const digest_variable<FieldT> &next_leaf_digest,
|
||||
const digest_variable<FieldT> &next_root_digest,
|
||||
const merkle_authentication_path_variable<FieldT, HashT> &next_path,
|
||||
const pb_linear_combination<FieldT> &update_successful,
|
||||
const std::string &annotation_prefix) :
|
||||
gadget<FieldT>(pb, annotation_prefix),
|
||||
digest_size(HashT::get_digest_len()),
|
||||
tree_depth(tree_depth),
|
||||
address_bits(address_bits),
|
||||
prev_leaf_digest(prev_leaf_digest),
|
||||
prev_root_digest(prev_root_digest),
|
||||
prev_path(prev_path),
|
||||
next_leaf_digest(next_leaf_digest),
|
||||
next_root_digest(next_root_digest),
|
||||
next_path(next_path),
|
||||
update_successful(update_successful)
|
||||
{
|
||||
assert(tree_depth > 0);
|
||||
assert(tree_depth == address_bits.size());
|
||||
|
||||
for (size_t i = 0; i < tree_depth-1; ++i)
|
||||
{
|
||||
prev_internal_output.emplace_back(digest_variable<FieldT>(pb, digest_size, FMT(this->annotation_prefix, " prev_internal_output_%zu", i)));
|
||||
next_internal_output.emplace_back(digest_variable<FieldT>(pb, digest_size, FMT(this->annotation_prefix, " next_internal_output_%zu", i)));
|
||||
}
|
||||
|
||||
computed_next_root.reset(new digest_variable<FieldT>(pb, digest_size, FMT(this->annotation_prefix, " computed_root")));
|
||||
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
block_variable<FieldT> prev_inp(pb, prev_path.left_digests[i], prev_path.right_digests[i], FMT(this->annotation_prefix, " prev_inp_%zu", i));
|
||||
prev_hasher_inputs.emplace_back(prev_inp);
|
||||
prev_hashers.emplace_back(HashT(pb, 2*digest_size, prev_inp, (i == 0 ? prev_root_digest : prev_internal_output[i-1]),
|
||||
FMT(this->annotation_prefix, " prev_hashers_%zu", i)));
|
||||
|
||||
block_variable<FieldT> next_inp(pb, next_path.left_digests[i], next_path.right_digests[i], FMT(this->annotation_prefix, " next_inp_%zu", i));
|
||||
next_hasher_inputs.emplace_back(next_inp);
|
||||
next_hashers.emplace_back(HashT(pb, 2*digest_size, next_inp, (i == 0 ? *computed_next_root : next_internal_output[i-1]),
|
||||
FMT(this->annotation_prefix, " next_hashers_%zu", i)));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
prev_propagators.emplace_back(digest_selector_gadget<FieldT>(pb, digest_size, i < tree_depth -1 ? prev_internal_output[i] : prev_leaf_digest,
|
||||
address_bits[tree_depth-1-i], prev_path.left_digests[i], prev_path.right_digests[i],
|
||||
FMT(this->annotation_prefix, " prev_propagators_%zu", i)));
|
||||
next_propagators.emplace_back(digest_selector_gadget<FieldT>(pb, digest_size, i < tree_depth -1 ? next_internal_output[i] : next_leaf_digest,
|
||||
address_bits[tree_depth-1-i], next_path.left_digests[i], next_path.right_digests[i],
|
||||
FMT(this->annotation_prefix, " next_propagators_%zu", i)));
|
||||
}
|
||||
|
||||
check_next_root.reset(new bit_vector_copy_gadget<FieldT>(pb, computed_next_root->bits, next_root_digest.bits, update_successful, FieldT::capacity(), FMT(annotation_prefix, " check_next_root")));
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void merkle_tree_check_update_gadget<FieldT, HashT>::generate_r1cs_constraints()
|
||||
{
|
||||
/* ensure correct hash computations */
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
prev_hashers[i].generate_r1cs_constraints(false); // we check root outside and prev_left/prev_right above
|
||||
next_hashers[i].generate_r1cs_constraints(true); // however we must check right side hashes
|
||||
}
|
||||
|
||||
/* ensure consistency of internal_left/internal_right with internal_output */
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
prev_propagators[i].generate_r1cs_constraints();
|
||||
next_propagators[i].generate_r1cs_constraints();
|
||||
}
|
||||
|
||||
/* ensure that prev auxiliary input and next auxiliary input match */
|
||||
for (size_t i = 0; i < tree_depth; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < digest_size; ++j)
|
||||
{
|
||||
/*
|
||||
addr * (prev_left - next_left) + (1 - addr) * (prev_right - next_right) = 0
|
||||
addr * (prev_left - next_left - prev_right + next_right) = next_right - prev_right
|
||||
*/
|
||||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(address_bits[tree_depth-1-i],
|
||||
prev_path.left_digests[i].bits[j] - next_path.left_digests[i].bits[j] - prev_path.right_digests[i].bits[j] + next_path.right_digests[i].bits[j],
|
||||
next_path.right_digests[i].bits[j] - prev_path.right_digests[i].bits[j]),
|
||||
FMT(this->annotation_prefix, " aux_check_%zu_%zu", i, j));
|
||||
}
|
||||
}
|
||||
|
||||
/* Note that while it is necessary to generate R1CS constraints
|
||||
for prev_path, it is not necessary to do so for next_path.
|
||||
|
||||
This holds, because { next_path.left_inputs[i],
|
||||
next_path.right_inputs[i] } is a pair { hash_output,
|
||||
auxiliary_input }. The bitness for hash_output is enforced
|
||||
above by next_hashers[i].generate_r1cs_constraints.
|
||||
|
||||
Because auxiliary input is the same for prev_path and next_path
|
||||
(enforced above), we have that auxiliary_input part is also
|
||||
constrained to be boolean, because prev_path is *all*
|
||||
constrained to be all boolean. */
|
||||
|
||||
check_next_root->generate_r1cs_constraints(false, false);
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void merkle_tree_check_update_gadget<FieldT, HashT>::generate_r1cs_witness()
|
||||
{
|
||||
/* do the hash computations bottom-up */
|
||||
for (int i = tree_depth-1; i >= 0; --i)
|
||||
{
|
||||
/* ensure consistency of prev_path and next_path */
|
||||
if (this->pb.val(address_bits[tree_depth-1-i]) == FieldT::one())
|
||||
{
|
||||
next_path.left_digests[i].generate_r1cs_witness(prev_path.left_digests[i].get_digest());
|
||||
}
|
||||
else
|
||||
{
|
||||
next_path.right_digests[i].generate_r1cs_witness(prev_path.right_digests[i].get_digest());
|
||||
}
|
||||
|
||||
/* propagate previous input */
|
||||
prev_propagators[i].generate_r1cs_witness();
|
||||
next_propagators[i].generate_r1cs_witness();
|
||||
|
||||
/* compute hash */
|
||||
prev_hashers[i].generate_r1cs_witness();
|
||||
next_hashers[i].generate_r1cs_witness();
|
||||
}
|
||||
|
||||
check_next_root->generate_r1cs_witness();
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
size_t merkle_tree_check_update_gadget<FieldT, HashT>::root_size_in_bits()
|
||||
{
|
||||
return HashT::get_digest_len();
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
size_t merkle_tree_check_update_gadget<FieldT, HashT>::expected_constraints(const size_t tree_depth)
|
||||
{
|
||||
/* NB: this includes path constraints */
|
||||
const size_t prev_hasher_constraints = tree_depth * HashT::expected_constraints(false);
|
||||
const size_t next_hasher_constraints = tree_depth * HashT::expected_constraints(true);
|
||||
const size_t prev_authentication_path_constraints = 2 * tree_depth * HashT::get_digest_len();
|
||||
const size_t prev_propagator_constraints = tree_depth * HashT::get_digest_len();
|
||||
const size_t next_propagator_constraints = tree_depth * HashT::get_digest_len();
|
||||
const size_t check_next_root_constraints = 3 * div_ceil(HashT::get_digest_len(), FieldT::capacity());
|
||||
const size_t aux_equality_constraints = tree_depth * HashT::get_digest_len();
|
||||
|
||||
return (prev_hasher_constraints + next_hasher_constraints + prev_authentication_path_constraints +
|
||||
prev_propagator_constraints + next_propagator_constraints + check_next_root_constraints +
|
||||
aux_equality_constraints);
|
||||
}
|
||||
|
||||
template<typename FieldT, typename HashT>
|
||||
void test_merkle_tree_check_update_gadget()
|
||||
{
|
||||
/* prepare test */
|
||||
const size_t digest_len = HashT::get_digest_len();
|
||||
|
||||
const size_t tree_depth = 16;
|
||||
std::vector<merkle_authentication_node> prev_path(tree_depth);
|
||||
|
||||
bit_vector prev_load_hash(digest_len);
|
||||
std::generate(prev_load_hash.begin(), prev_load_hash.end(), [&]() { return std::rand() % 2; });
|
||||
bit_vector prev_store_hash(digest_len);
|
||||
std::generate(prev_store_hash.begin(), prev_store_hash.end(), [&]() { return std::rand() % 2; });
|
||||
|
||||
bit_vector loaded_leaf = prev_load_hash;
|
||||
bit_vector stored_leaf = prev_store_hash;
|
||||
|
||||
bit_vector address_bits;
|
||||
|
||||
size_t address = 0;
|
||||
for (long level = tree_depth-1; level >= 0; --level)
|
||||
{
|
||||
const bool computed_is_right = (std::rand() % 2);
|
||||
address |= (computed_is_right ? 1ul << (tree_depth-1-level) : 0);
|
||||
address_bits.push_back(computed_is_right);
|
||||
bit_vector other(digest_len);
|
||||
std::generate(other.begin(), other.end(), [&]() { return std::rand() % 2; });
|
||||
|
||||
bit_vector load_block = prev_load_hash;
|
||||
load_block.insert(computed_is_right ? load_block.begin() : load_block.end(), other.begin(), other.end());
|
||||
bit_vector store_block = prev_store_hash;
|
||||
store_block.insert(computed_is_right ? store_block.begin() : store_block.end(), other.begin(), other.end());
|
||||
|
||||
bit_vector load_h = HashT::get_hash(load_block);
|
||||
bit_vector store_h = HashT::get_hash(store_block);
|
||||
|
||||
prev_path[level] = other;
|
||||
|
||||
prev_load_hash = load_h;
|
||||
prev_store_hash = store_h;
|
||||
}
|
||||
|
||||
bit_vector load_root = prev_load_hash;
|
||||
bit_vector store_root = prev_store_hash;
|
||||
|
||||
/* execute the test */
|
||||
protoboard<FieldT> pb;
|
||||
pb_variable_array<FieldT> address_bits_va;
|
||||
address_bits_va.allocate(pb, tree_depth, "address_bits");
|
||||
digest_variable<FieldT> prev_leaf_digest(pb, digest_len, "prev_leaf_digest");
|
||||
digest_variable<FieldT> prev_root_digest(pb, digest_len, "prev_root_digest");
|
||||
merkle_authentication_path_variable<FieldT, HashT> prev_path_var(pb, tree_depth, "prev_path_var");
|
||||
digest_variable<FieldT> next_leaf_digest(pb, digest_len, "next_leaf_digest");
|
||||
digest_variable<FieldT> next_root_digest(pb, digest_len, "next_root_digest");
|
||||
merkle_authentication_path_variable<FieldT, HashT> next_path_var(pb, tree_depth, "next_path_var");
|
||||
merkle_tree_check_update_gadget<FieldT, HashT> mls(pb, tree_depth, address_bits_va,
|
||||
prev_leaf_digest, prev_root_digest, prev_path_var,
|
||||
next_leaf_digest, next_root_digest, next_path_var, ONE, "mls");
|
||||
|
||||
prev_path_var.generate_r1cs_constraints();
|
||||
mls.generate_r1cs_constraints();
|
||||
|
||||
address_bits_va.fill_with_bits(pb, address_bits);
|
||||
assert(address_bits_va.get_field_element_from_bits(pb).as_ulong() == address);
|
||||
prev_leaf_digest.generate_r1cs_witness(loaded_leaf);
|
||||
prev_path_var.generate_r1cs_witness(address, prev_path);
|
||||
next_leaf_digest.generate_r1cs_witness(stored_leaf);
|
||||
address_bits_va.fill_with_bits(pb, address_bits);
|
||||
mls.generate_r1cs_witness();
|
||||
|
||||
/* make sure that update check will check for the right things */
|
||||
prev_leaf_digest.generate_r1cs_witness(loaded_leaf);
|
||||
next_leaf_digest.generate_r1cs_witness(stored_leaf);
|
||||
prev_root_digest.generate_r1cs_witness(load_root);
|
||||
next_root_digest.generate_r1cs_witness(store_root);
|
||||
address_bits_va.fill_with_bits(pb, address_bits);
|
||||
assert(pb.is_satisfied());
|
||||
|
||||
const size_t num_constraints = pb.num_constraints();
|
||||
const size_t expected_constraints = merkle_tree_check_update_gadget<FieldT, HashT>::expected_constraints(tree_depth);
|
||||
assert(num_constraints == expected_constraints);
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
|
||||
#endif // MERKLE_TREE_CHECK_UPDATE_GADGET_TCC_
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp"
|
||||
#ifdef CURVE_BN128
|
||||
#include "algebra/curves/bn128/bn128_pp.hpp"
|
||||
#endif
|
||||
#include "gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp"
|
||||
#include "gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.hpp"
|
||||
#include "gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace libsnark;
|
||||
|
||||
template<typename ppT>
|
||||
void test_all_merkle_tree_gadgets()
|
||||
{
|
||||
typedef Fr<ppT> FieldT;
|
||||
test_merkle_tree_check_read_gadget<FieldT, sha256_two_to_one_hash_gadget<FieldT> >();
|
||||
|
||||
test_merkle_tree_check_update_gadget<FieldT, sha256_two_to_one_hash_gadget<FieldT> >();
|
||||
}
|
||||
|
||||
TEST(gadgetlib1, merkle_tree)
|
||||
{
|
||||
start_profiling();
|
||||
|
||||
alt_bn128_pp::init_public_params();
|
||||
test_all_merkle_tree_gadgets<alt_bn128_pp>();
|
||||
|
||||
#ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled
|
||||
bn128_pp::init_public_params();
|
||||
test_all_merkle_tree_gadgets<bn128_pp>();
|
||||
#endif
|
||||
}
|
||||
144
src/snark/libsnark/gadgetlib1/pb_variable.hpp
Normal file
144
src/snark/libsnark/gadgetlib1/pb_variable.hpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PB_VARIABLE_HPP_
|
||||
#define PB_VARIABLE_HPP_
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/utils.hpp"
|
||||
#include "relations/variable.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
typedef size_t lc_index_t;
|
||||
|
||||
template<typename FieldT>
|
||||
class protoboard;
|
||||
|
||||
template<typename FieldT>
|
||||
class pb_variable : public variable<FieldT> {
|
||||
public:
|
||||
pb_variable(const var_index_t index = 0) : variable<FieldT>(index) {};
|
||||
|
||||
void allocate(protoboard<FieldT> &pb, const std::string &annotation="");
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
class pb_variable_array : private std::vector<pb_variable<FieldT> >
|
||||
{
|
||||
typedef std::vector<pb_variable<FieldT> > contents;
|
||||
public:
|
||||
using typename contents::iterator;
|
||||
using typename contents::const_iterator;
|
||||
using typename contents::reverse_iterator;
|
||||
using typename contents::const_reverse_iterator;
|
||||
|
||||
using contents::begin;
|
||||
using contents::end;
|
||||
using contents::rbegin;
|
||||
using contents::rend;
|
||||
using contents::emplace_back;
|
||||
using contents::insert;
|
||||
using contents::reserve;
|
||||
using contents::size;
|
||||
using contents::empty;
|
||||
using contents::operator[];
|
||||
using contents::resize;
|
||||
|
||||
pb_variable_array() : contents() {};
|
||||
pb_variable_array(size_t count, const pb_variable<FieldT> &value) : contents(count, value) {};
|
||||
pb_variable_array(typename contents::const_iterator first, typename contents::const_iterator last) : contents(first, last) {};
|
||||
pb_variable_array(typename contents::const_reverse_iterator first, typename contents::const_reverse_iterator last) : contents(first, last) {};
|
||||
void allocate(protoboard<FieldT> &pb, const size_t n, const std::string &annotation_prefix="");
|
||||
|
||||
void fill_with_field_elements(protoboard<FieldT> &pb, const std::vector<FieldT>& vals) const;
|
||||
void fill_with_bits(protoboard<FieldT> &pb, const bit_vector& bits) const;
|
||||
void fill_with_bits_of_ulong(protoboard<FieldT> &pb, const unsigned long i) const;
|
||||
void fill_with_bits_of_field_element(protoboard<FieldT> &pb, const FieldT &r) const;
|
||||
|
||||
std::vector<FieldT> get_vals(const protoboard<FieldT> &pb) const;
|
||||
bit_vector get_bits(const protoboard<FieldT> &pb) const;
|
||||
|
||||
FieldT get_field_element_from_bits(const protoboard<FieldT> &pb) const;
|
||||
};
|
||||
|
||||
/* index 0 corresponds to the constant term (used in legacy code) */
|
||||
#define ONE pb_variable<FieldT>(0)
|
||||
|
||||
template<typename FieldT>
|
||||
class pb_linear_combination : public linear_combination<FieldT> {
|
||||
public:
|
||||
bool is_variable;
|
||||
lc_index_t index;
|
||||
|
||||
pb_linear_combination();
|
||||
pb_linear_combination(const pb_variable<FieldT> &var);
|
||||
|
||||
void assign(protoboard<FieldT> &pb, const linear_combination<FieldT> &lc);
|
||||
void evaluate(protoboard<FieldT> &pb) const;
|
||||
|
||||
bool is_constant() const;
|
||||
FieldT constant_term() const;
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
class pb_linear_combination_array : private std::vector<pb_linear_combination<FieldT> >
|
||||
{
|
||||
typedef std::vector<pb_linear_combination<FieldT> > contents;
|
||||
public:
|
||||
using typename contents::iterator;
|
||||
using typename contents::const_iterator;
|
||||
using typename contents::reverse_iterator;
|
||||
using typename contents::const_reverse_iterator;
|
||||
|
||||
using contents::begin;
|
||||
using contents::end;
|
||||
using contents::rbegin;
|
||||
using contents::rend;
|
||||
using contents::emplace_back;
|
||||
using contents::insert;
|
||||
using contents::reserve;
|
||||
using contents::size;
|
||||
using contents::empty;
|
||||
using contents::operator[];
|
||||
using contents::resize;
|
||||
|
||||
pb_linear_combination_array() : contents() {};
|
||||
pb_linear_combination_array(const pb_variable_array<FieldT> &arr) { for (auto &v : arr) this->emplace_back(pb_linear_combination<FieldT>(v)); };
|
||||
pb_linear_combination_array(size_t count) : contents(count) {};
|
||||
pb_linear_combination_array(size_t count, const pb_linear_combination<FieldT> &value) : contents(count, value) {};
|
||||
pb_linear_combination_array(typename contents::const_iterator first, typename contents::const_iterator last) : contents(first, last) {};
|
||||
pb_linear_combination_array(typename contents::const_reverse_iterator first, typename contents::const_reverse_iterator last) : contents(first, last) {};
|
||||
|
||||
void evaluate(protoboard<FieldT> &pb) const;
|
||||
|
||||
void fill_with_field_elements(protoboard<FieldT> &pb, const std::vector<FieldT>& vals) const;
|
||||
void fill_with_bits(protoboard<FieldT> &pb, const bit_vector& bits) const;
|
||||
void fill_with_bits_of_ulong(protoboard<FieldT> &pb, const unsigned long i) const;
|
||||
void fill_with_bits_of_field_element(protoboard<FieldT> &pb, const FieldT &r) const;
|
||||
|
||||
std::vector<FieldT> get_vals(const protoboard<FieldT> &pb) const;
|
||||
bit_vector get_bits(const protoboard<FieldT> &pb) const;
|
||||
|
||||
FieldT get_field_element_from_bits(const protoboard<FieldT> &pb) const;
|
||||
};
|
||||
|
||||
template<typename FieldT>
|
||||
linear_combination<FieldT> pb_sum(const pb_linear_combination_array<FieldT> &v);
|
||||
|
||||
template<typename FieldT>
|
||||
linear_combination<FieldT> pb_packing_sum(const pb_linear_combination_array<FieldT> &v);
|
||||
|
||||
template<typename FieldT>
|
||||
linear_combination<FieldT> pb_coeff_sum(const pb_linear_combination_array<FieldT> &v, const std::vector<FieldT> &coeffs);
|
||||
|
||||
} // libsnark
|
||||
#include "gadgetlib1/pb_variable.tcc"
|
||||
|
||||
#endif // PB_VARIABLE_HPP_
|
||||
330
src/snark/libsnark/gadgetlib1/pb_variable.tcc
Normal file
330
src/snark/libsnark/gadgetlib1/pb_variable.tcc
Normal file
@@ -0,0 +1,330 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PB_VARIABLE_TCC_
|
||||
#define PB_VARIABLE_TCC_
|
||||
#include <cassert>
|
||||
#include "gadgetlib1/protoboard.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_variable<FieldT>::allocate(protoboard<FieldT> &pb, const std::string &annotation)
|
||||
{
|
||||
this->index = pb.allocate_var_index(annotation);
|
||||
}
|
||||
|
||||
/* allocates pb_variable<FieldT> array in MSB->LSB order */
|
||||
template<typename FieldT>
|
||||
void pb_variable_array<FieldT>::allocate(protoboard<FieldT> &pb, const size_t n, const std::string &annotation_prefix)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assert(annotation_prefix != "");
|
||||
#endif
|
||||
(*this).resize(n);
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
(*this)[i].allocate(pb, FMT(annotation_prefix, "_%zu", i));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_variable_array<FieldT>::fill_with_field_elements(protoboard<FieldT> &pb, const std::vector<FieldT>& vals) const
|
||||
{
|
||||
assert(this->size() == vals.size());
|
||||
for (size_t i = 0; i < vals.size(); ++i)
|
||||
{
|
||||
pb.val((*this)[i]) = vals[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_variable_array<FieldT>::fill_with_bits(protoboard<FieldT> &pb, const bit_vector& bits) const
|
||||
{
|
||||
assert(this->size() == bits.size());
|
||||
for (size_t i = 0; i < bits.size(); ++i)
|
||||
{
|
||||
pb.val((*this)[i]) = (bits[i] ? FieldT::one() : FieldT::zero());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_variable_array<FieldT>::fill_with_bits_of_field_element(protoboard<FieldT> &pb, const FieldT &r) const
|
||||
{
|
||||
const bigint<FieldT::num_limbs> rint = r.as_bigint();
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
pb.val((*this)[i]) = rint.test_bit(i) ? FieldT::one() : FieldT::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_variable_array<FieldT>::fill_with_bits_of_ulong(protoboard<FieldT> &pb, const unsigned long i) const
|
||||
{
|
||||
this->fill_with_bits_of_field_element(pb, FieldT(i, true));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> pb_variable_array<FieldT>::get_vals(const protoboard<FieldT> &pb) const
|
||||
{
|
||||
std::vector<FieldT> result(this->size());
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
result[i] = pb.val((*this)[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector pb_variable_array<FieldT>::get_bits(const protoboard<FieldT> &pb) const
|
||||
{
|
||||
bit_vector result;
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
const FieldT v = pb.val((*this)[i]);
|
||||
assert(v == FieldT::zero() || v == FieldT::one());
|
||||
result.push_back(v == FieldT::one());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT pb_variable_array<FieldT>::get_field_element_from_bits(const protoboard<FieldT> &pb) const
|
||||
{
|
||||
FieldT result = FieldT::zero();
|
||||
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
/* push in the new bit */
|
||||
const FieldT v = pb.val((*this)[this->size()-1-i]);
|
||||
assert(v == FieldT::zero() || v == FieldT::one());
|
||||
result += result + v;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
pb_linear_combination<FieldT>::pb_linear_combination()
|
||||
{
|
||||
this->is_variable = false;
|
||||
this->index = 0;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
pb_linear_combination<FieldT>::pb_linear_combination(const pb_variable<FieldT> &var)
|
||||
{
|
||||
this->is_variable = true;
|
||||
this->index = var.index;
|
||||
this->terms.emplace_back(linear_term<FieldT>(var));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_linear_combination<FieldT>::assign(protoboard<FieldT> &pb, const linear_combination<FieldT> &lc)
|
||||
{
|
||||
assert(this->is_variable == false);
|
||||
this->index = pb.allocate_lc_index();
|
||||
this->terms = lc.terms;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_linear_combination<FieldT>::evaluate(protoboard<FieldT> &pb) const
|
||||
{
|
||||
if (this->is_variable)
|
||||
{
|
||||
return; // do nothing
|
||||
}
|
||||
|
||||
FieldT sum = 0;
|
||||
for (auto term : this->terms)
|
||||
{
|
||||
sum += term.coeff * pb.val(pb_variable<FieldT>(term.index));
|
||||
}
|
||||
|
||||
pb.lc_val(*this) = sum;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bool pb_linear_combination<FieldT>::is_constant() const
|
||||
{
|
||||
if (is_variable)
|
||||
{
|
||||
return (index == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto term : this->terms)
|
||||
{
|
||||
if (term.index != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT pb_linear_combination<FieldT>::constant_term() const
|
||||
{
|
||||
if (is_variable)
|
||||
{
|
||||
return (index == 0 ? FieldT::one() : FieldT::zero());
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldT result = FieldT::zero();
|
||||
for (auto term : this->terms)
|
||||
{
|
||||
if (term.index == 0)
|
||||
{
|
||||
result += term.coeff;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_linear_combination_array<FieldT>::evaluate(protoboard<FieldT> &pb) const
|
||||
{
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
(*this)[i].evaluate(pb);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_linear_combination_array<FieldT>::fill_with_field_elements(protoboard<FieldT> &pb, const std::vector<FieldT>& vals) const
|
||||
{
|
||||
assert(this->size() == vals.size());
|
||||
for (size_t i = 0; i < vals.size(); ++i)
|
||||
{
|
||||
pb.lc_val((*this)[i]) = vals[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_linear_combination_array<FieldT>::fill_with_bits(protoboard<FieldT> &pb, const bit_vector& bits) const
|
||||
{
|
||||
assert(this->size() == bits.size());
|
||||
for (size_t i = 0; i < bits.size(); ++i)
|
||||
{
|
||||
pb.lc_val((*this)[i]) = (bits[i] ? FieldT::one() : FieldT::zero());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_linear_combination_array<FieldT>::fill_with_bits_of_field_element(protoboard<FieldT> &pb, const FieldT &r) const
|
||||
{
|
||||
const bigint<FieldT::num_limbs> rint = r.as_bigint();
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
pb.lc_val((*this)[i]) = rint.test_bit(i) ? FieldT::one() : FieldT::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void pb_linear_combination_array<FieldT>::fill_with_bits_of_ulong(protoboard<FieldT> &pb, const unsigned long i) const
|
||||
{
|
||||
this->fill_with_bits_of_field_element(pb, FieldT(i));
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
std::vector<FieldT> pb_linear_combination_array<FieldT>::get_vals(const protoboard<FieldT> &pb) const
|
||||
{
|
||||
std::vector<FieldT> result(this->size());
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
result[i] = pb.lc_val((*this)[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bit_vector pb_linear_combination_array<FieldT>::get_bits(const protoboard<FieldT> &pb) const
|
||||
{
|
||||
bit_vector result;
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
const FieldT v = pb.lc_val((*this)[i]);
|
||||
assert(v == FieldT::zero() || v == FieldT::one());
|
||||
result.push_back(v == FieldT::one());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT pb_linear_combination_array<FieldT>::get_field_element_from_bits(const protoboard<FieldT> &pb) const
|
||||
{
|
||||
FieldT result = FieldT::zero();
|
||||
|
||||
for (size_t i = 0; i < this->size(); ++i)
|
||||
{
|
||||
/* push in the new bit */
|
||||
const FieldT v = pb.lc_val((*this)[this->size()-1-i]);
|
||||
assert(v == FieldT::zero() || v == FieldT::one());
|
||||
result += result + v;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
linear_combination<FieldT> pb_sum(const pb_linear_combination_array<FieldT> &v)
|
||||
{
|
||||
linear_combination<FieldT> result;
|
||||
for (auto &term : v)
|
||||
{
|
||||
result = result + term;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
linear_combination<FieldT> pb_packing_sum(const pb_linear_combination_array<FieldT> &v)
|
||||
{
|
||||
FieldT twoi = FieldT::one(); // will hold 2^i entering each iteration
|
||||
std::vector<linear_term<FieldT> > all_terms;
|
||||
for (auto &lc : v)
|
||||
{
|
||||
for (auto &term : lc.terms)
|
||||
{
|
||||
all_terms.emplace_back(twoi * term);
|
||||
}
|
||||
twoi += twoi;
|
||||
}
|
||||
|
||||
return linear_combination<FieldT>(all_terms);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
linear_combination<FieldT> pb_coeff_sum(const pb_linear_combination_array<FieldT> &v, const std::vector<FieldT> &coeffs)
|
||||
{
|
||||
assert(v.size() == coeffs.size());
|
||||
std::vector<linear_term<FieldT> > all_terms;
|
||||
|
||||
auto coeff_it = coeffs.begin();
|
||||
for (auto &lc : v)
|
||||
{
|
||||
for (auto &term : lc.terms)
|
||||
{
|
||||
all_terms.emplace_back((*coeff_it) * term);
|
||||
}
|
||||
++coeff_it;
|
||||
}
|
||||
|
||||
return linear_combination<FieldT>(all_terms);
|
||||
}
|
||||
|
||||
|
||||
} // libsnark
|
||||
#endif // PB_VARIABLE_TCC
|
||||
75
src/snark/libsnark/gadgetlib1/protoboard.hpp
Normal file
75
src/snark/libsnark/gadgetlib1/protoboard.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PROTOBOARD_HPP_
|
||||
#define PROTOBOARD_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "gadgetlib1/pb_variable.hpp"
|
||||
#include "relations/constraint_satisfaction_problems/r1cs/r1cs.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
class r1cs_constraint;
|
||||
|
||||
template<typename FieldT>
|
||||
class r1cs_constraint_system;
|
||||
|
||||
template<typename FieldT>
|
||||
class protoboard {
|
||||
private:
|
||||
FieldT constant_term; /* only here, because pb.val() needs to be able to return reference to the constant 1 term */
|
||||
r1cs_variable_assignment<FieldT> values; /* values[0] will hold the value of the first allocated variable of the protoboard, *NOT* constant 1 */
|
||||
var_index_t next_free_var;
|
||||
lc_index_t next_free_lc;
|
||||
std::vector<FieldT> lc_values;
|
||||
public:
|
||||
r1cs_constraint_system<FieldT> constraint_system;
|
||||
|
||||
protoboard();
|
||||
|
||||
void clear_values();
|
||||
|
||||
FieldT& val(const pb_variable<FieldT> &var);
|
||||
FieldT val(const pb_variable<FieldT> &var) const;
|
||||
|
||||
FieldT& lc_val(const pb_linear_combination<FieldT> &lc);
|
||||
FieldT lc_val(const pb_linear_combination<FieldT> &lc) const;
|
||||
|
||||
void add_r1cs_constraint(const r1cs_constraint<FieldT> &constr, const std::string &annotation="");
|
||||
void augment_variable_annotation(const pb_variable<FieldT> &v, const std::string &postfix);
|
||||
bool is_satisfied() const;
|
||||
void dump_variables() const;
|
||||
|
||||
size_t num_constraints() const;
|
||||
size_t num_inputs() const;
|
||||
size_t num_variables() const;
|
||||
|
||||
void set_input_sizes(const size_t primary_input_size);
|
||||
|
||||
r1cs_variable_assignment<FieldT> full_variable_assignment() const;
|
||||
r1cs_primary_input<FieldT> primary_input() const;
|
||||
r1cs_auxiliary_input<FieldT> auxiliary_input() const;
|
||||
r1cs_constraint_system<FieldT> get_constraint_system() const;
|
||||
|
||||
friend class pb_variable<FieldT>;
|
||||
friend class pb_linear_combination<FieldT>;
|
||||
|
||||
private:
|
||||
var_index_t allocate_var_index(const std::string &annotation="");
|
||||
lc_index_t allocate_lc_index();
|
||||
};
|
||||
|
||||
} // libsnark
|
||||
#include "gadgetlib1/protoboard.tcc"
|
||||
#endif // PROTOBOARD_HPP_
|
||||
189
src/snark/libsnark/gadgetlib1/protoboard.tcc
Normal file
189
src/snark/libsnark/gadgetlib1/protoboard.tcc
Normal file
@@ -0,0 +1,189 @@
|
||||
/** @file
|
||||
*****************************************************************************
|
||||
* @author This file is part of libsnark, developed by SCIPR Lab
|
||||
* and contributors (see AUTHORS).
|
||||
* @copyright MIT license (see LICENSE file)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef PROTOBOARD_TCC_
|
||||
#define PROTOBOARD_TCC_
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include "common/profiling.hpp"
|
||||
|
||||
namespace libsnark {
|
||||
|
||||
template<typename FieldT>
|
||||
protoboard<FieldT>::protoboard()
|
||||
{
|
||||
constant_term = FieldT::one();
|
||||
|
||||
#ifdef DEBUG
|
||||
constraint_system.variable_annotations[0] = "ONE";
|
||||
#endif
|
||||
|
||||
next_free_var = 1; /* to account for constant 1 term */
|
||||
next_free_lc = 0;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void protoboard<FieldT>::clear_values()
|
||||
{
|
||||
std::fill(values.begin(), values.end(), FieldT::zero());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
var_index_t protoboard<FieldT>::allocate_var_index(const std::string &annotation)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assert(annotation != "");
|
||||
constraint_system.variable_annotations[next_free_var] = annotation;
|
||||
#else
|
||||
UNUSED(annotation);
|
||||
#endif
|
||||
++constraint_system.auxiliary_input_size;
|
||||
values.emplace_back(FieldT::zero());
|
||||
return next_free_var++;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
lc_index_t protoboard<FieldT>::allocate_lc_index()
|
||||
{
|
||||
lc_values.emplace_back(FieldT::zero());
|
||||
return next_free_lc++;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT& protoboard<FieldT>::val(const pb_variable<FieldT> &var)
|
||||
{
|
||||
assert(var.index <= values.size());
|
||||
return (var.index == 0 ? constant_term : values[var.index-1]);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT protoboard<FieldT>::val(const pb_variable<FieldT> &var) const
|
||||
{
|
||||
assert(var.index <= values.size());
|
||||
return (var.index == 0 ? constant_term : values[var.index-1]);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT& protoboard<FieldT>::lc_val(const pb_linear_combination<FieldT> &lc)
|
||||
{
|
||||
if (lc.is_variable)
|
||||
{
|
||||
return this->val(pb_variable<FieldT>(lc.index));
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(lc.index < lc_values.size());
|
||||
return lc_values[lc.index];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
FieldT protoboard<FieldT>::lc_val(const pb_linear_combination<FieldT> &lc) const
|
||||
{
|
||||
if (lc.is_variable)
|
||||
{
|
||||
return this->val(pb_variable<FieldT>(lc.index));
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(lc.index < lc_values.size());
|
||||
return lc_values[lc.index];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void protoboard<FieldT>::add_r1cs_constraint(const r1cs_constraint<FieldT> &constr, const std::string &annotation)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
assert(annotation != "");
|
||||
constraint_system.constraint_annotations[constraint_system.constraints.size()] = annotation;
|
||||
#else
|
||||
UNUSED(annotation);
|
||||
#endif
|
||||
constraint_system.constraints.emplace_back(constr);
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void protoboard<FieldT>::augment_variable_annotation(const pb_variable<FieldT> &v, const std::string &postfix)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
auto it = constraint_system.variable_annotations.find(v.index);
|
||||
constraint_system.variable_annotations[v.index] = (it == constraint_system.variable_annotations.end() ? "" : it->second + " ") + postfix;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
bool protoboard<FieldT>::is_satisfied() const
|
||||
{
|
||||
return constraint_system.is_satisfied(primary_input(), auxiliary_input());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void protoboard<FieldT>::dump_variables() const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
for (size_t i = 0; i < constraint_system.num_variables; ++i)
|
||||
{
|
||||
printf("%-40s --> ", constraint_system.variable_annotations[i].c_str());
|
||||
values[i].as_bigint().print_hex();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
size_t protoboard<FieldT>::num_constraints() const
|
||||
{
|
||||
return constraint_system.num_constraints();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
size_t protoboard<FieldT>::num_inputs() const
|
||||
{
|
||||
return constraint_system.num_inputs();
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
size_t protoboard<FieldT>::num_variables() const
|
||||
{
|
||||
return next_free_var - 1;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
void protoboard<FieldT>::set_input_sizes(const size_t primary_input_size)
|
||||
{
|
||||
assert(primary_input_size <= num_variables());
|
||||
constraint_system.primary_input_size = primary_input_size;
|
||||
constraint_system.auxiliary_input_size = num_variables() - primary_input_size;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
r1cs_variable_assignment<FieldT> protoboard<FieldT>::full_variable_assignment() const
|
||||
{
|
||||
return values;
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
r1cs_primary_input<FieldT> protoboard<FieldT>::primary_input() const
|
||||
{
|
||||
return r1cs_primary_input<FieldT>(values.begin(), values.begin() + num_inputs());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
r1cs_auxiliary_input<FieldT> protoboard<FieldT>::auxiliary_input() const
|
||||
{
|
||||
return r1cs_primary_input<FieldT>(values.begin() + num_inputs(), values.end());
|
||||
}
|
||||
|
||||
template<typename FieldT>
|
||||
r1cs_constraint_system<FieldT> protoboard<FieldT>::get_constraint_system() const
|
||||
{
|
||||
return constraint_system;
|
||||
}
|
||||
|
||||
} // libsnark
|
||||
#endif // PROTOBOARD_TCC_
|
||||
Reference in New Issue
Block a user