Squashed 'src/snark/' content from commit 9ada3f8

git-subtree-dir: src/snark
git-subtree-split: 9ada3f84ab484c57b2247c2f41091fd6a0916573
This commit is contained in:
Jack Grigg
2017-08-02 11:17:25 +01:00
commit 51e448641d
123 changed files with 22264 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
#ifndef ASSERT_except_H
#define ASSERT_except_H
#include <exception>
inline void assert_except(bool condition) {
if (!condition) {
throw std::runtime_error("Assertion failed.");
}
}
#endif

View File

@@ -0,0 +1,74 @@
/** @file
*****************************************************************************
Declaration of interfaces for an accumulation vector.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ACCUMULATION_VECTOR_HPP_
#define ACCUMULATION_VECTOR_HPP_
#include "common/data_structures/sparse_vector.hpp"
namespace libsnark {
template<typename T>
class accumulation_vector;
template<typename T>
std::ostream& operator<<(std::ostream &out, const accumulation_vector<T> &v);
template<typename T>
std::istream& operator>>(std::istream &in, accumulation_vector<T> &v);
/**
* An accumulation vector comprises an accumulation value and a sparse vector.
* The method "accumulate_chunk" allows one to accumlate portions of the sparse
* vector into the accumualation value.
*/
template<typename T>
class accumulation_vector {
public:
T first;
sparse_vector<T> rest;
accumulation_vector() = default;
accumulation_vector(const accumulation_vector<T> &other) = default;
accumulation_vector(accumulation_vector<T> &&other) = default;
accumulation_vector(T &&first, sparse_vector<T> &&rest) : first(std::move(first)), rest(std::move(rest)) {};
accumulation_vector(T &&first, std::vector<T> &&v) : first(std::move(first)), rest(std::move(v)) {}
accumulation_vector(std::vector<T> &&v) : first(T::zero()), rest(std::move(v)) {};
accumulation_vector<T>& operator=(const accumulation_vector<T> &other) = default;
accumulation_vector<T>& operator=(accumulation_vector<T> &&other) = default;
bool operator==(const accumulation_vector<T> &other) const;
bool is_fully_accumulated() const;
size_t domain_size() const;
size_t size() const;
size_t size_in_bits() const;
template<typename FieldT>
accumulation_vector<T> accumulate_chunk(const typename std::vector<FieldT>::const_iterator &it_begin,
const typename std::vector<FieldT>::const_iterator &it_end,
const size_t offset) const;
};
template<typename T>
std::ostream& operator<<(std::ostream &out, const accumulation_vector<T> &v);
template<typename T>
std::istream& operator>>(std::istream &in, accumulation_vector<T> &v);
} // libsnark
#include "common/data_structures/accumulation_vector.tcc"
#endif // ACCUMULATION_VECTOR_HPP_

View File

@@ -0,0 +1,84 @@
/** @file
*****************************************************************************
Implementation of interfaces for an accumulation vector.
See accumulation_vector.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ACCUMULATION_VECTOR_TCC_
#define ACCUMULATION_VECTOR_TCC_
namespace libsnark {
template<typename T>
bool accumulation_vector<T>::operator==(const accumulation_vector<T> &other) const
{
return (this->first == other.first && this->rest == other.rest);
}
template<typename T>
bool accumulation_vector<T>::is_fully_accumulated() const
{
return rest.empty();
}
template<typename T>
size_t accumulation_vector<T>::domain_size() const
{
return rest.domain_size();
}
template<typename T>
size_t accumulation_vector<T>::size() const
{
return rest.domain_size();
}
template<typename T>
size_t accumulation_vector<T>::size_in_bits() const
{
const size_t first_size_in_bits = T::size_in_bits();
const size_t rest_size_in_bits = rest.size_in_bits();
return first_size_in_bits + rest_size_in_bits;
}
template<typename T>
template<typename FieldT>
accumulation_vector<T> accumulation_vector<T>::accumulate_chunk(const typename std::vector<FieldT>::const_iterator &it_begin,
const typename std::vector<FieldT>::const_iterator &it_end,
const size_t offset) const
{
std::pair<T, sparse_vector<T> > acc_result = rest.template accumulate<FieldT>(it_begin, it_end, offset);
T new_first = first + acc_result.first;
return accumulation_vector<T>(std::move(new_first), std::move(acc_result.second));
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const accumulation_vector<T> &v)
{
out << v.first << OUTPUT_NEWLINE;
out << v.rest << OUTPUT_NEWLINE;
return out;
}
template<typename T>
std::istream& operator>>(std::istream& in, accumulation_vector<T> &v)
{
in >> v.first;
consume_OUTPUT_NEWLINE(in);
in >> v.rest;
consume_OUTPUT_NEWLINE(in);
return in;
}
} // libsnark
#endif // ACCUMULATION_VECTOR_TCC_

View File

@@ -0,0 +1,71 @@
/** @file
*****************************************************************************
Declaration of interfaces for a Merkle tree.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef MERKLE_TREE_HPP_
#define MERKLE_TREE_HPP_
#include <map>
#include <vector>
#include "common/utils.hpp"
namespace libsnark {
/**
* A Merkle tree is maintained as two maps:
* - a map from addresses to values, and
* - a map from addresses to hashes.
*
* The second map maintains the intermediate hashes of a Merkle tree
* built atop the values currently stored in the tree (the
* implementation admits a very efficient support for sparse
* trees). Besides offering methods to load and store values, the
* class offers methods to retrieve the root of the Merkle tree and to
* obtain the authentication paths for (the value at) a given address.
*/
typedef bit_vector merkle_authentication_node;
typedef std::vector<merkle_authentication_node> merkle_authentication_path;
template<typename HashT>
class merkle_tree {
private:
typedef typename HashT::hash_value_type hash_value_type;
typedef typename HashT::merkle_authentication_path_type merkle_authentication_path_type;
public:
std::vector<hash_value_type> hash_defaults;
std::map<size_t, bit_vector> values;
std::map<size_t, hash_value_type> hashes;
size_t depth;
size_t value_size;
size_t digest_size;
merkle_tree(const size_t depth, const size_t value_size);
merkle_tree(const size_t depth, const size_t value_size, const std::vector<bit_vector> &contents_as_vector);
merkle_tree(const size_t depth, const size_t value_size, const std::map<size_t, bit_vector> &contents);
bit_vector get_value(const size_t address) const;
void set_value(const size_t address, const bit_vector &value);
hash_value_type get_root() const;
merkle_authentication_path_type get_path(const size_t address) const;
void dump() const;
};
} // libsnark
#include "common/data_structures/merkle_tree.tcc"
#endif // MERKLE_TREE_HPP_

View File

@@ -0,0 +1,246 @@
/** @file
*****************************************************************************
Implementation of interfaces for Merkle tree.
See merkle_tree.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_TCC
#define MERKLE_TREE_TCC
#include <algorithm>
#include "common/profiling.hpp"
#include "common/utils.hpp"
namespace libsnark {
template<typename HashT>
typename HashT::hash_value_type two_to_one_CRH(const typename HashT::hash_value_type &l,
const typename HashT::hash_value_type &r)
{
typename HashT::hash_value_type new_input;
new_input.insert(new_input.end(), l.begin(), l.end());
new_input.insert(new_input.end(), r.begin(), r.end());
const size_t digest_size = HashT::get_digest_len();
assert(l.size() == digest_size);
assert(r.size() == digest_size);
return HashT::get_hash(new_input);
}
template<typename HashT>
merkle_tree<HashT>::merkle_tree(const size_t depth, const size_t value_size) :
depth(depth), value_size(value_size)
{
assert(depth < sizeof(size_t) * 8);
digest_size = HashT::get_digest_len();
assert(value_size <= digest_size);
hash_value_type last(digest_size);
hash_defaults.reserve(depth+1);
hash_defaults.emplace_back(last);
for (size_t i = 0; i < depth; ++i)
{
last = two_to_one_CRH<HashT>(last, last);
hash_defaults.emplace_back(last);
}
std::reverse(hash_defaults.begin(), hash_defaults.end());
}
template<typename HashT>
merkle_tree<HashT>::merkle_tree(const size_t depth,
const size_t value_size,
const std::vector<bit_vector> &contents_as_vector) :
merkle_tree<HashT>(depth, value_size)
{
assert(log2(contents_as_vector.size()) <= depth);
for (size_t address = 0; address < contents_as_vector.size(); ++address)
{
const size_t idx = address + (1ul<<depth) - 1;
values[idx] = contents_as_vector[address];
hashes[idx] = contents_as_vector[address];
hashes[idx].resize(digest_size);
}
size_t idx_begin = (1ul<<depth) - 1;
size_t idx_end = contents_as_vector.size() + ((1ul<<depth) - 1);
for (int layer = depth; layer > 0; --layer)
{
for (size_t idx = idx_begin; idx < idx_end; idx += 2)
{
hash_value_type l = hashes[idx]; // this is sound, because idx_begin is always a left child
hash_value_type r = (idx + 1 < idx_end ? hashes[idx+1] : hash_defaults[layer]);
hash_value_type h = two_to_one_CRH<HashT>(l, r);
hashes[(idx-1)/2] = h;
}
idx_begin = (idx_begin-1)/2;
idx_end = (idx_end-1)/2;
}
}
template<typename HashT>
merkle_tree<HashT>::merkle_tree(const size_t depth,
const size_t value_size,
const std::map<size_t, bit_vector> &contents) :
merkle_tree<HashT>(depth, value_size)
{
if (!contents.empty())
{
assert(contents.rbegin()->first < 1ul<<depth);
for (auto it = contents.begin(); it != contents.end(); ++it)
{
const size_t address = it->first;
const bit_vector value = it->second;
const size_t idx = address + (1ul<<depth) - 1;
values[address] = value;
hashes[idx] = value;
hashes[idx].resize(digest_size);
}
auto last_it = hashes.end();
for (int layer = depth; layer > 0; --layer)
{
auto next_last_it = hashes.begin();
for (auto it = hashes.begin(); it != last_it; ++it)
{
const size_t idx = it->first;
const hash_value_type hash = it->second;
if (idx % 2 == 0)
{
// this is the right child of its parent and by invariant we are missing the left child
hashes[(idx-1)/2] = two_to_one_CRH<HashT>(hash_defaults[layer], hash);
}
else
{
if (std::next(it) == last_it || std::next(it)->first != idx + 1)
{
// this is the left child of its parent and is missing its right child
hashes[(idx-1)/2] = two_to_one_CRH<HashT>(hash, hash_defaults[layer]);
}
else
{
// typical case: this is the left child of the parent and adjecent to it there is a right child
hashes[(idx-1)/2] = two_to_one_CRH<HashT>(hash, std::next(it)->second);
++it;
}
}
}
last_it = next_last_it;
}
}
}
template<typename HashT>
bit_vector merkle_tree<HashT>::get_value(const size_t address) const
{
assert(log2(address) <= depth);
auto it = values.find(address);
bit_vector padded_result = (it == values.end() ? bit_vector(digest_size) : it->second);
padded_result.resize(value_size);
return padded_result;
}
template<typename HashT>
void merkle_tree<HashT>::set_value(const size_t address,
const bit_vector &value)
{
assert(log2(address) <= depth);
size_t idx = address + (1ul<<depth) - 1;
assert(value.size() == value_size);
values[address] = value;
hashes[idx] = value;
hashes[idx].resize(digest_size);
for (int layer = depth-1; layer >=0; --layer)
{
idx = (idx-1)/2;
auto it = hashes.find(2*idx+1);
hash_value_type l = (it == hashes.end() ? hash_defaults[layer+1] : it->second);
it = hashes.find(2*idx+2);
hash_value_type r = (it == hashes.end() ? hash_defaults[layer+1] : it->second);
hash_value_type h = two_to_one_CRH<HashT>(l, r);
hashes[idx] = h;
}
}
template<typename HashT>
typename HashT::hash_value_type merkle_tree<HashT>::get_root() const
{
auto it = hashes.find(0);
return (it == hashes.end() ? hash_defaults[0] : it->second);
}
template<typename HashT>
typename HashT::merkle_authentication_path_type merkle_tree<HashT>::get_path(const size_t address) const
{
typename HashT::merkle_authentication_path_type result(depth);
assert(log2(address) <= depth);
size_t idx = address + (1ul<<depth) - 1;
for (size_t layer = depth; layer > 0; --layer)
{
size_t sibling_idx = ((idx + 1) ^ 1) - 1;
auto it = hashes.find(sibling_idx);
if (layer == depth)
{
auto it2 = values.find(sibling_idx - ((1ul<<depth) - 1));
result[layer-1] = (it2 == values.end() ? bit_vector(value_size, false) : it2->second);
result[layer-1].resize(digest_size);
}
else
{
result[layer-1] = (it == hashes.end() ? hash_defaults[layer] : it->second);
}
idx = (idx-1)/2;
}
return result;
}
template<typename HashT>
void merkle_tree<HashT>::dump() const
{
for (size_t i = 0; i < 1ul<<depth; ++i)
{
auto it = values.find(i);
printf("[%zu] -> ", i);
const bit_vector value = (it == values.end() ? bit_vector(value_size) : it->second);
for (bool b : value)
{
printf("%d", b ? 1 : 0);
}
printf("\n");
}
printf("\n");
}
} // libsnark
#endif // MERKLE_TREE_TCC

View File

@@ -0,0 +1,79 @@
/** @file
*****************************************************************************
Declaration of interfaces for a sparse vector.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef SPARSE_VECTOR_HPP_
#define SPARSE_VECTOR_HPP_
#include <vector>
namespace libsnark {
template<typename T>
struct sparse_vector;
template<typename T>
std::ostream& operator<<(std::ostream &out, const sparse_vector<T> &v);
template<typename T>
std::istream& operator>>(std::istream &in, sparse_vector<T> &v);
/**
* A sparse vector is a list of indices along with corresponding values.
* The indices are selected from the set {0,1,...,domain_size-1}.
*/
template<typename T>
struct sparse_vector {
std::vector<size_t> indices;
std::vector<T> values;
size_t domain_size_ = 0;
sparse_vector() = default;
sparse_vector(const sparse_vector<T> &other) = default;
sparse_vector(sparse_vector<T> &&other) = default;
sparse_vector(std::vector<T> &&v); /* constructor from std::vector */
sparse_vector<T>& operator=(const sparse_vector<T> &other) = default;
sparse_vector<T>& operator=(sparse_vector<T> &&other) = default;
T operator[](const size_t idx) const;
bool operator==(const sparse_vector<T> &other) const;
bool operator==(const std::vector<T> &other) const;
bool is_valid() const;
bool empty() const;
size_t domain_size() const; // return domain_size_
size_t size() const; // return the number of indices (representing the number of non-zero entries)
size_t size_in_bits() const; // return the number bits needed to store the sparse vector
/* return a pair consisting of the accumulated value and the sparse vector of non-accumuated values */
template<typename FieldT>
std::pair<T, sparse_vector<T> > accumulate(const typename std::vector<FieldT>::const_iterator &it_begin,
const typename std::vector<FieldT>::const_iterator &it_end,
const size_t offset) const;
friend std::ostream& operator<< <T>(std::ostream &out, const sparse_vector<T> &v);
friend std::istream& operator>> <T>(std::istream &in, sparse_vector<T> &v);
};
template<typename T>
std::ostream& operator<<(std::ostream& out, const sparse_vector<T> &v);
template<typename T>
std::istream& operator>>(std::istream& in, sparse_vector<T> &v);
} // libsnark
#include "common/data_structures/sparse_vector.tcc"
#endif // SPARSE_VECTOR_HPP_

View File

@@ -0,0 +1,316 @@
/** @file
*****************************************************************************
Implementation of interfaces for a sparse vector.
See sparse_vector.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef SPARSE_VECTOR_TCC_
#define SPARSE_VECTOR_TCC_
#include "algebra/scalar_multiplication/multiexp.hpp"
#include <numeric>
namespace libsnark {
template<typename T>
sparse_vector<T>::sparse_vector(std::vector<T> &&v) :
values(std::move(v)), domain_size_(values.size())
{
indices.resize(domain_size_);
std::iota(indices.begin(), indices.end(), 0);
}
template<typename T>
T sparse_vector<T>::operator[](const size_t idx) const
{
auto it = std::lower_bound(indices.begin(), indices.end(), idx);
return (it != indices.end() && *it == idx) ? values[it - indices.begin()] : T();
}
template<typename T>
bool sparse_vector<T>::operator==(const sparse_vector<T> &other) const
{
if (this->domain_size_ != other.domain_size_)
{
return false;
}
size_t this_pos = 0, other_pos = 0;
while (this_pos < this->indices.size() && other_pos < other.indices.size())
{
if (this->indices[this_pos] == other.indices[other_pos])
{
if (this->values[this_pos] != other.values[other_pos])
{
return false;
}
++this_pos;
++other_pos;
}
else if (this->indices[this_pos] < other.indices[other_pos])
{
if (!this->values[this_pos].is_zero())
{
return false;
}
++this_pos;
}
else
{
if (!other.values[other_pos].is_zero())
{
return false;
}
++other_pos;
}
}
/* at least one of the vectors has been exhausted, so other must be empty */
while (this_pos < this->indices.size())
{
if (!this->values[this_pos].is_zero())
{
return false;
}
++this_pos;
}
while (other_pos < other.indices.size())
{
if (!other.values[other_pos].is_zero())
{
return false;
}
++other_pos;
}
return true;
}
template<typename T>
bool sparse_vector<T>::operator==(const std::vector<T> &other) const
{
if (this->domain_size_ < other.size())
{
return false;
}
size_t j = 0;
for (size_t i = 0; i < other.size(); ++i)
{
if (this->indices[j] == i)
{
if (this->values[j] != other[j])
{
return false;
}
++j;
}
else
{
if (!other[j].is_zero())
{
return false;
}
}
}
return true;
}
template<typename T>
bool sparse_vector<T>::is_valid() const
{
if (values.size() == indices.size() && values.size() <= domain_size_)
{
return false;
}
for (size_t i = 0; i + 1 < indices.size(); ++i)
{
if (indices[i] >= indices[i+1])
{
return false;
}
}
if (!indices.empty() && indices[indices.size()-1] >= domain_size_)
{
return false;
}
return true;
}
template<typename T>
bool sparse_vector<T>::empty() const
{
return indices.empty();
}
template<typename T>
size_t sparse_vector<T>::domain_size() const
{
return domain_size_;
}
template<typename T>
size_t sparse_vector<T>::size() const
{
return indices.size();
}
template<typename T>
size_t sparse_vector<T>::size_in_bits() const
{
return indices.size() * (sizeof(size_t) * 8 + T::size_in_bits());
}
template<typename T>
template<typename FieldT>
std::pair<T, sparse_vector<T> > sparse_vector<T>::accumulate(const typename std::vector<FieldT>::const_iterator &it_begin,
const typename std::vector<FieldT>::const_iterator &it_end,
const size_t offset) const
{
// TODO: does not really belong here.
const size_t chunks = 1;
const bool use_multiexp = true;
T accumulated_value = T::zero();
sparse_vector<T> resulting_vector;
resulting_vector.domain_size_ = domain_size_;
const size_t range_len = it_end - it_begin;
bool in_block = false;
size_t first_pos = -1, last_pos = -1; // g++ -flto emits unitialized warning, even though in_block guards for such cases.
for (size_t i = 0; i < indices.size(); ++i)
{
const bool matching_pos = (offset <= indices[i] && indices[i] < offset + range_len);
// printf("i = %zu, pos[i] = %zu, offset = %zu, w_size = %zu\n", i, indices[i], offset, w_size);
bool copy_over;
if (in_block)
{
if (matching_pos && last_pos == i-1)
{
// block can be extended, do it
last_pos = i;
copy_over = false;
}
else
{
// block has ended here
in_block = false;
copy_over = true;
#ifdef DEBUG
print_indent(); printf("doing multiexp for w_%zu ... w_%zu\n", indices[first_pos], indices[last_pos]);
#endif
accumulated_value = accumulated_value + multi_exp<T, FieldT>(values.begin() + first_pos,
values.begin() + last_pos + 1,
it_begin + (indices[first_pos] - offset),
it_begin + (indices[last_pos] - offset) + 1,
chunks, use_multiexp);
}
}
else
{
if (matching_pos)
{
// block can be started
first_pos = i;
last_pos = i;
in_block = true;
copy_over = false;
}
else
{
copy_over = true;
}
}
if (copy_over)
{
resulting_vector.indices.emplace_back(indices[i]);
resulting_vector.values.emplace_back(values[i]);
}
}
if (in_block)
{
#ifdef DEBUG
print_indent(); printf("doing multiexp for w_%zu ... w_%zu\n", indices[first_pos], indices[last_pos]);
#endif
accumulated_value = accumulated_value + multi_exp<T, FieldT>(values.begin() + first_pos,
values.begin() + last_pos + 1,
it_begin + (indices[first_pos] - offset),
it_begin + (indices[last_pos] - offset) + 1,
chunks, use_multiexp);
}
return std::make_pair(accumulated_value, resulting_vector);
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const sparse_vector<T> &v)
{
out << v.domain_size_ << "\n";
out << v.indices.size() << "\n";
for (const size_t& i : v.indices)
{
out << i << "\n";
}
out << v.values.size() << "\n";
for (const T& t : v.values)
{
out << t << OUTPUT_NEWLINE;
}
return out;
}
template<typename T>
std::istream& operator>>(std::istream& in, sparse_vector<T> &v)
{
in >> v.domain_size_;
consume_newline(in);
size_t s;
in >> s;
consume_newline(in);
v.indices.resize(s);
for (size_t i = 0; i < s; ++i)
{
in >> v.indices[i];
consume_newline(in);
}
v.values.clear();
in >> s;
consume_newline(in);
v.values.reserve(s);
for (size_t i = 0; i < s; ++i)
{
T t;
in >> t;
consume_OUTPUT_NEWLINE(in);
v.values.emplace_back(t);
}
return in;
}
} // libsnark
#endif // SPARSE_VECTOR_TCC_

View File

@@ -0,0 +1,53 @@
/** @file
*****************************************************************************
This file defines default_ec_pp based on the CURVE=... make flag, which selects
which elliptic curve is used to implement group arithmetic and pairings.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef EC_PP_HPP_
#define EC_PP_HPP_
/************************ Pick the elliptic curve ****************************/
#ifdef CURVE_ALT_BN128
#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp"
namespace libsnark {
typedef alt_bn128_pp default_ec_pp;
} // libsnark
#endif
#ifdef CURVE_BN128
#include "algebra/curves/bn128/bn128_pp.hpp"
namespace libsnark {
typedef bn128_pp default_ec_pp;
} // libsnark
#endif
#ifdef CURVE_EDWARDS
#include "algebra/curves/edwards/edwards_pp.hpp"
namespace libsnark {
typedef edwards_pp default_ec_pp;
} // libsnark
#endif
#ifdef CURVE_MNT4
#include "algebra/curves/mnt/mnt4/mnt4_pp.hpp"
namespace libsnark {
typedef mnt4_pp default_ec_pp;
} // libsnark
#endif
#ifdef CURVE_MNT6
#include "algebra/curves/mnt/mnt6/mnt6_pp.hpp"
namespace libsnark {
typedef mnt6_pp default_ec_pp;
} // libsnark
#endif
#endif // EC_PP_HPP_

View File

@@ -0,0 +1,22 @@
/** @file
*****************************************************************************
This file defines default_r1cs_ppzksnark_pp based on the elliptic curve
choice selected in ec_pp.hpp.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef R1CS_PPZKSNARK_PP_HPP_
#define R1CS_PPZKSNARK_PP_HPP_
#include "common/default_types/ec_pp.hpp"
namespace libsnark {
typedef default_ec_pp default_r1cs_ppzksnark_pp;
} // libsnark
#endif // R1CS_PPZKSNARK_PP_HPP_

379
src/common/profiling.cpp Normal file
View File

@@ -0,0 +1,379 @@
/** @file
*****************************************************************************
Implementation of functions for profiling code blocks.
See profiling.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "common/profiling.hpp"
#include <cassert>
#include <stdexcept>
#include <chrono>
#include <cstdio>
#include <list>
#include <vector>
#include <ctime>
#include "common/default_types/ec_pp.hpp"
#include "common/utils.hpp"
#ifndef NO_PROCPS
#include <proc/readproc.h>
#endif
namespace libsnark {
long long get_nsec_time()
{
auto timepoint = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(timepoint.time_since_epoch()).count();
}
/* Return total CPU time consumsed by all threads of the process, in nanoseconds. */
long long get_nsec_cpu_time()
{
::timespec ts;
if ( ::clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) )
throw ::std::runtime_error("clock_gettime(CLOCK_PROCESS_CPUTIME_ID) failed");
// If we expected this to work, don't silently ignore failures, because that would hide the problem and incur an unnecessarily system-call overhead. So if we ever observe this exception, we should probably add a suitable #ifdef .
//TODO: clock_gettime(CLOCK_PROCESS_CPUTIME_ID) is not supported by native Windows. What about Cygwin? Should we #ifdef on CLOCK_PROCESS_CPUTIME_ID or on __linux__?
return ts.tv_sec * 1000000000ll + ts.tv_nsec;
}
long long start_time, last_time;
long long start_cpu_time, last_cpu_time;
void start_profiling()
{
printf("Reset time counters for profiling\n");
last_time = start_time = get_nsec_time();
last_cpu_time = start_cpu_time = get_nsec_cpu_time();
}
std::map<std::string, size_t> invocation_counts;
std::map<std::string, long long> enter_times;
std::map<std::string, long long> last_times;
std::map<std::string, long long> cumulative_times;
//TODO: Instead of analogous maps for time and cpu_time, use a single struct-valued map
std::map<std::string, long long> enter_cpu_times;
std::map<std::string, long long> last_cpu_times;
std::map<std::pair<std::string, std::string>, long long> op_counts;
std::map<std::pair<std::string, std::string>, long long> cumulative_op_counts; // ((msg, data_point), value)
// TODO: Convert op_counts and cumulative_op_counts from pair to structs
size_t indentation = 0;
std::vector<std::string> block_names;
std::list<std::pair<std::string, long long*> > op_data_points = {
#ifdef PROFILE_OP_COUNTS
std::make_pair("Fradd", &Fr<default_ec_pp>::add_cnt),
std::make_pair("Frsub", &Fr<default_ec_pp>::sub_cnt),
std::make_pair("Frmul", &Fr<default_ec_pp>::mul_cnt),
std::make_pair("Frinv", &Fr<default_ec_pp>::inv_cnt),
std::make_pair("Fqadd", &Fq<default_ec_pp>::add_cnt),
std::make_pair("Fqsub", &Fq<default_ec_pp>::sub_cnt),
std::make_pair("Fqmul", &Fq<default_ec_pp>::mul_cnt),
std::make_pair("Fqinv", &Fq<default_ec_pp>::inv_cnt),
std::make_pair("G1add", &G1<default_ec_pp>::add_cnt),
std::make_pair("G1dbl", &G1<default_ec_pp>::dbl_cnt),
std::make_pair("G2add", &G2<default_ec_pp>::add_cnt),
std::make_pair("G2dbl", &G2<default_ec_pp>::dbl_cnt)
#endif
};
bool inhibit_profiling_info = false;
bool inhibit_profiling_counters = false;
void clear_profiling_counters()
{
invocation_counts.clear();
last_times.clear();
last_cpu_times.clear();
cumulative_times.clear();
}
void print_cumulative_time_entry(const std::string &key, const long long factor)
{
const double total_ms = (cumulative_times.at(key) * 1e-6);
const size_t cnt = invocation_counts.at(key);
const double avg_ms = total_ms / cnt;
printf(" %-45s: %12.5fms = %lld * %0.5fms (%zu invocations, %0.5fms = %lld * %0.5fms per invocation)\n", key.c_str(), total_ms, factor, total_ms/factor, cnt, avg_ms, factor, avg_ms/factor);
}
void print_cumulative_times(const long long factor)
{
printf("Dumping times:\n");
for (auto& kv : cumulative_times)
{
print_cumulative_time_entry(kv.first, factor);
}
}
void print_cumulative_op_counts(const bool only_fq)
{
#ifdef PROFILE_OP_COUNTS
printf("Dumping operation counts:\n");
for (auto& msg : invocation_counts)
{
printf(" %-45s: ", msg.first.c_str());
bool first = true;
for (auto& data_point : op_data_points)
{
if (only_fq && data_point.first.compare(0, 2, "Fq") != 0)
{
continue;
}
if (!first)
{
printf(", ");
}
printf("%-5s = %7.0f (%3zu)",
data_point.first.c_str(),
1. * cumulative_op_counts[std::make_pair(msg.first, data_point.first)] / msg.second,
msg.second);
first = false;
}
printf("\n");
}
#else
UNUSED(only_fq);
#endif
}
void print_op_profiling(const std::string &msg)
{
#ifdef PROFILE_OP_COUNTS
printf("\n");
print_indent();
printf("(opcounts) = (");
bool first = true;
for (std::pair<std::string, long long*> p : op_data_points)
{
if (!first)
{
printf(", ");
}
printf("%s=%lld", p.first.c_str(), *(p.second)-op_counts[std::make_pair(msg, p.first)]);
first = false;
}
printf(")");
#else
UNUSED(msg);
#endif
}
static void print_times_from_last_and_start(long long now, long long last,
long long cpu_now, long long cpu_last)
{
long long time_from_start = now - start_time;
long long time_from_last = now - last;
long long cpu_time_from_start = cpu_now - start_cpu_time;
long long cpu_time_from_last = cpu_now - cpu_last;
if (time_from_last != 0) {
double parallelism_from_last = 1.0 * cpu_time_from_last / time_from_last;
printf("[%0.4fs x%0.2f]", time_from_last * 1e-9, parallelism_from_last);
} else {
printf("[ ]");
}
if (time_from_start != 0) {
double parallelism_from_start = 1.0 * cpu_time_from_start / time_from_start;
printf("\t(%0.4fs x%0.2f from start)", time_from_start * 1e-9, parallelism_from_start);
}
}
void print_time(const char* msg)
{
if (inhibit_profiling_info)
{
return;
}
long long now = get_nsec_time();
long long cpu_now = get_nsec_cpu_time();
printf("%-35s\t", msg);
print_times_from_last_and_start(now, last_time, cpu_now, last_cpu_time);
#ifdef PROFILE_OP_COUNTS
print_op_profiling(msg);
#endif
printf("\n");
fflush(stdout);
last_time = now;
last_cpu_time = cpu_now;
}
void print_header(const char *msg)
{
printf("\n================================================================================\n");
printf("%s\n", msg);
printf("================================================================================\n\n");
}
void print_indent()
{
for (size_t i = 0; i < indentation; ++i)
{
printf(" ");
}
}
void op_profiling_enter(const std::string &msg)
{
for (std::pair<std::string, long long*> p : op_data_points)
{
op_counts[std::make_pair(msg, p.first)] = *(p.second);
}
}
void enter_block(const std::string &msg, const bool indent)
{
if (inhibit_profiling_counters)
{
return;
}
block_names.emplace_back(msg);
long long t = get_nsec_time();
enter_times[msg] = t;
long long cpu_t = get_nsec_cpu_time();
enter_cpu_times[msg] = cpu_t;
if (inhibit_profiling_info)
{
return;
}
#ifdef MULTICORE
#pragma omp critical
#endif
{
op_profiling_enter(msg);
print_indent();
printf("(enter) %-35s\t", msg.c_str());
print_times_from_last_and_start(t, t, cpu_t, cpu_t);
printf("\n");
fflush(stdout);
if (indent)
{
++indentation;
}
}
}
void leave_block(const std::string &msg, const bool indent)
{
if (inhibit_profiling_counters)
{
return;
}
#ifndef MULTICORE
assert(*(--block_names.end()) == msg);
#endif
block_names.pop_back();
++invocation_counts[msg];
long long t = get_nsec_time();
last_times[msg] = (t - enter_times[msg]);
cumulative_times[msg] += (t - enter_times[msg]);
long long cpu_t = get_nsec_cpu_time();
last_cpu_times[msg] = (cpu_t - enter_cpu_times[msg]);
#ifdef PROFILE_OP_COUNTS
for (std::pair<std::string, long long*> p : op_data_points)
{
cumulative_op_counts[std::make_pair(msg, p.first)] += *(p.second)-op_counts[std::make_pair(msg, p.first)];
}
#endif
if (inhibit_profiling_info)
{
return;
}
#ifdef MULTICORE
#pragma omp critical
#endif
{
if (indent)
{
--indentation;
}
print_indent();
printf("(leave) %-35s\t", msg.c_str());
print_times_from_last_and_start(t, enter_times[msg], cpu_t, enter_cpu_times[msg]);
print_op_profiling(msg);
printf("\n");
fflush(stdout);
}
}
void print_mem(const std::string &s)
{
#ifndef NO_PROCPS
struct proc_t usage;
look_up_our_self(&usage);
if (s.empty())
{
printf("* Peak vsize (physical memory+swap) in mebibytes: %lu\n", usage.vsize >> 20);
}
else
{
printf("* Peak vsize (physical memory+swap) in mebibytes (%s): %lu\n", s.c_str(), usage.vsize >> 20);
}
#else
printf("* Memory profiling not supported in NO_PROCPS mode\n");
#endif
}
void print_compilation_info()
{
#ifdef __GNUC__
printf("g++ version: %s\n", __VERSION__);
//printf("Compiled on %s %s\n", __DATE__, __TIME__);
#endif
#ifdef STATIC
printf("STATIC: yes\n");
#else
printf("STATIC: no\n");
#endif
#ifdef MULTICORE
printf("MULTICORE: yes\n");
#else
printf("MULTICORE: no\n");
#endif
#ifdef DEBUG
printf("DEBUG: yes\n");
#else
printf("DEBUG: no\n");
#endif
#ifdef PROFILE_OP_COUNTS
printf("PROFILE_OP_COUNTS: yes\n");
#else
printf("PROFILE_OP_COUNTS: no\n");
#endif
#ifdef _GLIBCXX_DEBUG
printf("_GLIBCXX_DEBUG: yes\n");
#else
printf("_GLIBCXX_DEBUG: no\n");
#endif
}
} // libsnark

51
src/common/profiling.hpp Normal file
View File

@@ -0,0 +1,51 @@
/** @file
*****************************************************************************
Declaration of functions for profiling code blocks.
Reports time, operation counts, memory usage, and others.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef PROFILING_HPP_
#define PROFILING_HPP_
#include <cstddef>
#include <map>
#include <string>
#include <vector>
namespace libsnark {
void start_profiling();
long long get_nsec_time();
void print_time(const char* msg);
void print_header(const char* msg);
void print_indent();
extern bool inhibit_profiling_info;
extern bool inhibit_profiling_counters;
extern std::map<std::string, size_t> invocation_counts;
extern std::map<std::string, long long> last_times;
extern std::map<std::string, long long> cumulative_times;
void clear_profiling_counters();
void print_cumulative_time_entry(const std::string &key, const long long factor=1);
void print_cumulative_times(const long long factor=1);
void print_cumulative_op_counts(const bool only_fq=false);
void enter_block(const std::string &msg, const bool indent=true);
void leave_block(const std::string &msg, const bool indent=true);
void print_mem(const std::string &s = "");
void print_compilation_info();
} // libsnark
#endif // PROFILING_HPP_

View File

@@ -0,0 +1,104 @@
/** @file
*****************************************************************************
Declaration of serialization routines and constants.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef SERIALIZATION_HPP_
#define SERIALIZATION_HPP_
#include <istream>
#include <map>
#include <ostream>
#include <set>
#include <vector>
namespace libsnark {
/*
* @todo
* The serialization is fragile. Shoud be rewritten using a standard, portable-format
* library like boost::serialize.
*
* However, for now the following conventions are used within the code.
*
* All algebraic objects support either binary or decimal output using
* the standard C++ stream operators (operator<<, operator>>).
*
* The binary mode is activated by defining a BINARY_OUTPUT
* preprocessor macro (e.g. g++ -DBINARY_OUTPUT ...).
*
* Binary output assumes that the stream is to be binary read at its
* current position so any white space should be consumed beforehand.
*
* Consecutive algebraic objects are separated by OUTPUT_NEWLINE and
* within themselves (e.g. X and Y coordinates for field elements) with
* OUTPUT_SEPARATOR (as defined below).
*
* Therefore to dump two integers, two Fp elements and another integer
* one would:
*
* out << 3 << "\n";
* out << 4 << "\n";
* out << FieldT(56) << OUTPUT_NEWLINE;
* out << FieldT(78) << OUTPUT_NEWLINE;
* out << 9 << "\n";
*
* Then reading back it its reader's responsibility (!) to consume "\n"
* after 4, but Fp::operator<< will correctly consume OUTPUT_NEWLINE.
*
* The reader should also consume "\n" after 9, so that another field
* element can be properly chained. This is especially important for
* binary output.
*
* The binary serialization of algebraic objects is currently *not*
* portable between machines of different word sizes.
*/
#ifdef BINARY_OUTPUT
#define OUTPUT_NEWLINE ""
#define OUTPUT_SEPARATOR ""
#else
#define OUTPUT_NEWLINE "\n"
#define OUTPUT_SEPARATOR " "
#endif
inline void consume_newline(std::istream &in);
inline void consume_OUTPUT_NEWLINE(std::istream &in);
inline void consume_OUTPUT_SEPARATOR(std::istream &in);
inline void output_bool(std::ostream &out, const bool b);
inline void output_bool_vector(std::ostream &out, const std::vector<bool> &v);
template<typename T>
T reserialize(const T &obj);
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T> &v);
template<typename T>
std::istream& operator>>(std::ostream& out, std::vector<T> &v);
template<typename T1, typename T2>
std::ostream& operator<<(std::ostream& out, const std::map<T1, T2> &m);
template<typename T1, typename T2>
std::istream& operator>>(std::istream& in, std::map<T1, T2> &m);
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::set<T> &s);
template<typename T>
std::istream& operator>>(std::istream& in, std::set<T> &s);
} // libsnark
#include "common/serialization.tcc"
#endif // SERIALIZATION_HPP_

View File

@@ -0,0 +1,180 @@
/** @file
*****************************************************************************
Implementation of serialization routines.
See serialization.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef SERIALIZATION_TCC_
#define SERIALIZATION_TCC_
#include <cassert>
#include <sstream>
#include "common/utils.hpp"
namespace libsnark {
inline void consume_newline(std::istream &in)
{
char c;
in.read(&c, 1);
}
inline void consume_OUTPUT_NEWLINE(std::istream &in)
{
#ifdef BINARY_OUTPUT
// nothing to consume
UNUSED(in);
#else
char c;
in.read(&c, 1);
#endif
}
inline void consume_OUTPUT_SEPARATOR(std::istream &in)
{
#ifdef BINARY_OUTPUT
// nothing to consume
UNUSED(in);
#else
char c;
in.read(&c, 1);
#endif
}
inline void output_bool(std::ostream &out, const bool b)
{
out << (b ? 1 : 0) << "\n";
}
inline void output_bool_vector(std::ostream &out, const std::vector<bool> &v)
{
out << v.size() << "\n";
for (const bool b : v)
{
output_bool(out, b);
}
}
template<typename T>
T reserialize(const T &obj)
{
std::stringstream ss;
ss << obj;
T tmp;
ss >> tmp;
assert(obj == tmp);
return tmp;
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T> &v)
{
static_assert(!std::is_same<T, bool>::value, "this does not work for std::vector<bool>");
out << v.size() << "\n";
for (const T& t : v)
{
out << t << OUTPUT_NEWLINE;
}
return out;
}
template<typename T>
std::istream& operator>>(std::istream& in, std::vector<T> &v)
{
static_assert(!std::is_same<T, bool>::value, "this does not work for std::vector<bool>");
size_t size;
in >> size;
consume_newline(in);
v.resize(0);
for (size_t i = 0; i < size; ++i)
{
T elt;
in >> elt;
consume_OUTPUT_NEWLINE(in);
v.push_back(elt);
}
return in;
}
template<typename T1, typename T2>
std::ostream& operator<<(std::ostream& out, const std::map<T1, T2> &m)
{
out << m.size() << "\n";
for (auto &it : m)
{
out << it.first << "\n";
out << it.second << "\n";
}
return out;
}
template<typename T1, typename T2>
std::istream& operator>>(std::istream& in, std::map<T1, T2> &m)
{
m.clear();
size_t size;
in >> size;
consume_newline(in);
for (size_t i = 0; i < size; ++i)
{
T1 k;
T2 v;
in >> k;
consume_newline(in);
in >> v;
consume_newline(in);
m[k] = v;
}
return in;
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::set<T> &s)
{
out << s.size() << "\n";
for (auto &el : s)
{
out << el << "\n";
}
return out;
}
template<typename T>
std::istream& operator>>(std::istream& in, std::set<T> &s)
{
s.clear();
size_t size;
in >> size;
consume_newline(in);
for (size_t i = 0; i < size; ++i)
{
T el;
in >> el;
consume_newline(in);
s.insert(el);
}
return in;
}
}
#endif // SERIALIZATION_TCC_

View File

@@ -0,0 +1,26 @@
/** @file
*****************************************************************************
Declaration of functions for supporting the use of templates.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef TEMPLATE_UTILS_HPP_
#define TEMPLATE_UTILS_HPP_
namespace libsnark {
/* A commonly used SFINAE helper type */
template<typename T>
struct void_type
{
typedef void type;
};
} // libsnark
#endif // TEMPLATE_UTILS_HPP_

102
src/common/utils.cpp Normal file
View File

@@ -0,0 +1,102 @@
/** @file
*****************************************************************************
Implementation of misc math and serialization utility functions
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstdarg>
#include "common/utils.hpp"
namespace libsnark {
size_t log2(size_t n)
/* returns ceil(log2(n)), so 1ul<<log2(n) is the smallest power of 2,
that is not less than n. */
{
size_t r = ((n & (n-1)) == 0 ? 0 : 1); // add 1 if n is not power of 2
while (n > 1)
{
n >>= 1;
r++;
}
return r;
}
size_t bitreverse(size_t n, const size_t l)
{
size_t r = 0;
for (size_t k = 0; k < l; ++k)
{
r = (r << 1) | (n & 1);
n >>= 1;
}
return r;
}
bit_vector int_list_to_bits(const std::initializer_list<unsigned long> &l, const size_t wordsize)
{
bit_vector res(wordsize*l.size());
for (size_t i = 0; i < l.size(); ++i)
{
for (size_t j = 0; j < wordsize; ++j)
{
res[i*wordsize + j] = (*(l.begin()+i) & (1ul<<(wordsize-1-j)));
}
}
return res;
}
long long div_ceil(long long x, long long y)
{
return (x + (y-1)) / y;
}
bool is_little_endian()
{
uint64_t a = 0x12345678;
unsigned char *c = (unsigned char*)(&a);
return (*c = 0x78);
}
std::string FORMAT(const std::string &prefix, const char* format, ...)
{
const static size_t MAX_FMT = 256;
char buf[MAX_FMT];
va_list args;
va_start(args, format);
vsnprintf(buf, MAX_FMT, format, args);
va_end(args);
return prefix + std::string(buf);
}
void serialize_bit_vector(std::ostream &out, const bit_vector &v)
{
out << v.size() << "\n";
for (size_t i = 0; i < v.size(); ++i)
{
out << v[i] << "\n";
}
}
void deserialize_bit_vector(std::istream &in, bit_vector &v)
{
size_t size;
in >> size;
v.resize(size);
for (size_t i = 0; i < size; ++i)
{
bool b;
in >> b;
v[i] = b;
}
}
} // libsnark

57
src/common/utils.hpp Normal file
View File

@@ -0,0 +1,57 @@
/** @file
*****************************************************************************
Declaration of misc math and serialization utility functions
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef UTILS_HPP_
#define UTILS_HPP_
#include <cassert>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
namespace libsnark {
typedef std::vector<bool> bit_vector;
/// returns ceil(log2(n)), so 1ul<<log2(n) is the smallest power of 2, that is not less than n
size_t log2(size_t n);
inline size_t exp2(size_t k) { return 1ul << k; }
size_t bitreverse(size_t n, const size_t l);
bit_vector int_list_to_bits(const std::initializer_list<unsigned long> &l, const size_t wordsize);
long long div_ceil(long long x, long long y);
bool is_little_endian();
std::string FORMAT(const std::string &prefix, const char* format, ...);
/* A variadic template to suppress unused argument warnings */
template<typename ... Types>
void UNUSED(Types&&...) {}
#ifdef DEBUG
#define FMT FORMAT
#else
#define FMT(...) (UNUSED(__VA_ARGS__), "")
#endif
void serialize_bit_vector(std::ostream &out, const bit_vector &v);
void deserialize_bit_vector(std::istream &in, bit_vector &v);
template<typename T>
size_t size_in_bits(const std::vector<T> &v);
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
} // libsnark
#include "common/utils.tcc" /* note that utils has a templatized part (utils.tcc) and non-templatized part (utils.cpp) */
#endif // UTILS_HPP_

23
src/common/utils.tcc Normal file
View File

@@ -0,0 +1,23 @@
/** @file
*****************************************************************************
Implementation of templatized utility functions
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef UTILS_TCC_
#define UTILS_TCC_
namespace libsnark {
template<typename T>
size_t size_in_bits(const std::vector<T> &v)
{
return v.size() * T::size_in_bits();
}
} // libsnark
#endif // UTILS_TCC_