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:
syd
2017-11-24 13:54:17 -05:00
parent 7888624f74
commit a55c186a74
119 changed files with 114 additions and 119 deletions

View 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;
}
}

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View File

@@ -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_

View File

@@ -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_

View 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_

View 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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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()

View File

@@ -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()

View File

@@ -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> >();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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
}

View 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_

View 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

View 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_

View 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_