Duke Leto
4 years ago
28 changed files with 848 additions and 2561 deletions
@ -1,123 +0,0 @@ |
|||
# =========================================================================== |
|||
# https://www.gnu.org/software/autoconf-archive/ax_openmp.html |
|||
# =========================================================================== |
|||
# |
|||
# SYNOPSIS |
|||
# |
|||
# AX_OPENMP([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) |
|||
# |
|||
# DESCRIPTION |
|||
# |
|||
# This macro tries to find out how to compile programs that use OpenMP a |
|||
# standard API and set of compiler directives for parallel programming |
|||
# (see http://www-unix.mcs/) |
|||
# |
|||
# On success, it sets the OPENMP_CFLAGS/OPENMP_CXXFLAGS/OPENMP_F77FLAGS |
|||
# output variable to the flag (e.g. -omp) used both to compile *and* link |
|||
# OpenMP programs in the current language. |
|||
# |
|||
# NOTE: You are assumed to not only compile your program with these flags, |
|||
# but also link it with them as well. |
|||
# |
|||
# If you want to compile everything with OpenMP, you should set: |
|||
# |
|||
# CFLAGS="$CFLAGS $OPENMP_CFLAGS" |
|||
# #OR# CXXFLAGS="$CXXFLAGS $OPENMP_CXXFLAGS" |
|||
# #OR# FFLAGS="$FFLAGS $OPENMP_FFLAGS" |
|||
# |
|||
# (depending on the selected language). |
|||
# |
|||
# The user can override the default choice by setting the corresponding |
|||
# environment variable (e.g. OPENMP_CFLAGS). |
|||
# |
|||
# ACTION-IF-FOUND is a list of shell commands to run if an OpenMP flag is |
|||
# found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it is |
|||
# not found. If ACTION-IF-FOUND is not specified, the default action will |
|||
# define HAVE_OPENMP. |
|||
# |
|||
# LICENSE |
|||
# |
|||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> |
|||
# Copyright (c) 2015 John W. Peterson <jwpeterson@gmail.com> |
|||
# Copyright (c) 2016 Nick R. Papior <nickpapior@gmail.com> |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify it |
|||
# under the terms of the GNU General Public License as published by the |
|||
# Free Software Foundation, either version 3 of the License, or (at your |
|||
# option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, but |
|||
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|||
# Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
# |
|||
# As a special exception, the respective Autoconf Macro's copyright owner |
|||
# gives unlimited permission to copy, distribute and modify the configure |
|||
# scripts that are the output of Autoconf when processing the Macro. You |
|||
# need not follow the terms of the GNU General Public License when using |
|||
# or distributing such scripts, even though portions of the text of the |
|||
# Macro appear in them. The GNU General Public License (GPL) does govern |
|||
# all other use of the material that constitutes the Autoconf Macro. |
|||
# |
|||
# This special exception to the GPL applies to versions of the Autoconf |
|||
# Macro released by the Autoconf Archive. When you make and distribute a |
|||
# modified version of the Autoconf Macro, you may extend this special |
|||
# exception to the GPL to apply to your modified version as well. |
|||
|
|||
#serial 13 |
|||
|
|||
AC_DEFUN([AX_OPENMP], [ |
|||
AC_PREREQ([2.69]) dnl for _AC_LANG_PREFIX |
|||
|
|||
AC_CACHE_CHECK([for OpenMP flag of _AC_LANG compiler], ax_cv_[]_AC_LANG_ABBREV[]_openmp, [save[]_AC_LANG_PREFIX[]FLAGS=$[]_AC_LANG_PREFIX[]FLAGS |
|||
ax_cv_[]_AC_LANG_ABBREV[]_openmp=unknown |
|||
# Flags to try: -fopenmp (gcc), -mp (SGI & PGI), |
|||
# -qopenmp (icc>=15), -openmp (icc), |
|||
# -xopenmp (Sun), -omp (Tru64), |
|||
# -qsmp=omp (AIX), |
|||
# none |
|||
ax_openmp_flags="-fopenmp -openmp -qopenmp -mp -xopenmp -omp -qsmp=omp none" |
|||
if test "x$OPENMP_[]_AC_LANG_PREFIX[]FLAGS" != x; then |
|||
ax_openmp_flags="$OPENMP_[]_AC_LANG_PREFIX[]FLAGS $ax_openmp_flags" |
|||
fi |
|||
for ax_openmp_flag in $ax_openmp_flags; do |
|||
case $ax_openmp_flag in |
|||
none) []_AC_LANG_PREFIX[]FLAGS=$save[]_AC_LANG_PREFIX[] ;; |
|||
*) []_AC_LANG_PREFIX[]FLAGS="$save[]_AC_LANG_PREFIX[]FLAGS $ax_openmp_flag" ;; |
|||
esac |
|||
AC_LINK_IFELSE([AC_LANG_SOURCE([[ |
|||
@%:@include <omp.h> |
|||
|
|||
static void |
|||
parallel_fill(int * data, int n) |
|||
{ |
|||
int i; |
|||
@%:@pragma omp parallel for |
|||
for (i = 0; i < n; ++i) |
|||
data[i] = i; |
|||
} |
|||
|
|||
int |
|||
main() |
|||
{ |
|||
int arr[100000]; |
|||
omp_set_num_threads(2); |
|||
parallel_fill(arr, 100000); |
|||
return 0; |
|||
} |
|||
]])],[ax_cv_[]_AC_LANG_ABBREV[]_openmp=$ax_openmp_flag; break],[]) |
|||
done |
|||
[]_AC_LANG_PREFIX[]FLAGS=$save[]_AC_LANG_PREFIX[]FLAGS |
|||
]) |
|||
if test "x$ax_cv_[]_AC_LANG_ABBREV[]_openmp" = "xunknown"; then |
|||
m4_default([$2],:) |
|||
else |
|||
if test "x$ax_cv_[]_AC_LANG_ABBREV[]_openmp" != "xnone"; then |
|||
OPENMP_[]_AC_LANG_PREFIX[]FLAGS=$ax_cv_[]_AC_LANG_ABBREV[]_openmp |
|||
fi |
|||
m4_default([$1], [AC_DEFINE(HAVE_OPENMP,1,[Define if OpenMP is enabled])]) |
|||
fi |
|||
])dnl AX_OPENMP |
@ -1,44 +0,0 @@ |
|||
package=libgmp |
|||
|
|||
ifeq ($(host_os),mingw32) |
|||
$(package)_download_path=https://github.com/joshuayabut/$(package)/archive |
|||
$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz |
|||
$(package)_download_file=$($(package)_git_commit).tar.gz |
|||
$(package)_sha256_hash=193836c1acc9dc00fe2521205d7bbe1ba13263f6cbef6f02584bf6f8b34b108f |
|||
$(package)_git_commit=053c03b1cab347671d936f43ef66b48ab5e380ee |
|||
$(package)_dependencies= |
|||
$(package)_config_opts=--enable-cxx --disable-shared |
|||
else ifeq ($(build_os),darwin) |
|||
$(package)_download_path=https://github.com/ca333/$(package)/archive |
|||
$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz |
|||
$(package)_download_file=$($(package)_git_commit).tar.gz |
|||
$(package)_sha256_hash=59b2c2b5d58fdf5943bfde1fa709e9eb53e7e072c9699d28dc1c2cbb3c8cc32c |
|||
$(package)_git_commit=aece03c7b6967f91f3efdac8c673f55adff53ab1 |
|||
$(package)_dependencies= |
|||
$(package)_config_opts=--enable-cxx --disable-shared |
|||
else |
|||
$(package)_version=6.1.1 |
|||
$(package)_download_path=https://github.com/MyHush/libgmp/releases/download/v6.1.1 |
|||
$(package)_file_name=gmp-$($(package)_version).tar.bz2 |
|||
$(package)_sha256_hash=a8109865f2893f1373b0a8ed5ff7429de8db696fc451b1036bd7bdf95bbeffd6 |
|||
$(package)_dependencies= |
|||
$(package)_config_opts=--enable-cxx --disable-shared |
|||
endif |
|||
|
|||
define $(package)_config_cmds |
|||
$($(package)_autoconf) --host=$(host) --build=$(build) |
|||
endef |
|||
|
|||
ifeq ($(build_os),darwin) |
|||
define $(package)_build_cmds |
|||
$(MAKE) |
|||
endef |
|||
else |
|||
define $(package)_build_cmds |
|||
$(MAKE) CPPFLAGS='-fPIC' |
|||
endef |
|||
endif |
|||
|
|||
define $(package)_stage_cmds |
|||
$(MAKE) DESTDIR=$($(package)_staging_dir) install ; echo '=== staging find for $(package):' ; find $($(package)_staging_dir) |
|||
endef |
@ -1,29 +0,0 @@ |
|||
noinst_PROGRAMS += \
|
|||
zcash/GenerateParams \
|
|||
zcash/CreateJoinSplit |
|||
|
|||
# tool for generating our public parameters
|
|||
zcash_GenerateParams_SOURCES = zcash/GenerateParams.cpp |
|||
zcash_GenerateParams_CPPFLAGS = $(AM_CPPFLAGS) |
|||
zcash_GenerateParams_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) |
|||
zcash_GenerateParams_LDADD = \
|
|||
$(BOOST_LIBS) \
|
|||
$(LIBZCASH) \
|
|||
$(LIBBITCOIN_UTIL) \
|
|||
$(LIBBITCOIN_CRYPTO) \
|
|||
$(LIBZCASH_LIBS) |
|||
|
|||
# tool for profiling the creation of joinsplits
|
|||
zcash_CreateJoinSplit_SOURCES = zcash/CreateJoinSplit.cpp |
|||
zcash_CreateJoinSplit_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) |
|||
zcash_CreateJoinSplit_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) |
|||
zcash_CreateJoinSplit_LDADD = \
|
|||
$(LIBBITCOIN_COMMON) \
|
|||
$(LIBZCASH) \
|
|||
$(LIBBITCOIN_UTIL) \
|
|||
$(LIBBITCOIN_CRYPTO) \
|
|||
$(BOOST_LIBS) \
|
|||
$(LIBZCASH_LIBS) \
|
|||
$(LIBCRYPTOCONDITIONS) \
|
|||
$(LIBSECP256K1) |
|||
|
@ -1,183 +0,0 @@ |
|||
#include <gtest/gtest.h> |
|||
#include "uint256.h" |
|||
|
|||
#include "zcash/util.h" |
|||
|
|||
#include <boost/foreach.hpp> |
|||
#include <boost/format.hpp> |
|||
#include <boost/optional.hpp> |
|||
|
|||
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp> |
|||
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp> |
|||
#include <libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp> |
|||
#include <libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp> |
|||
|
|||
#include "zcash/IncrementalMerkleTree.hpp" |
|||
|
|||
using namespace libsnark; |
|||
using namespace libzcash; |
|||
|
|||
#include "zcash/circuit/utils.tcc" |
|||
#include "zcash/circuit/merkle.tcc" |
|||
|
|||
template<typename FieldT> |
|||
void test_value_equals(uint64_t i) { |
|||
protoboard<FieldT> pb; |
|||
pb_variable_array<FieldT> num; |
|||
num.allocate(pb, 64, ""); |
|||
num.fill_with_bits(pb, uint64_to_bool_vector(i)); |
|||
pb.add_r1cs_constraint(r1cs_constraint<FieldT>( |
|||
packed_addition(num), |
|||
FieldT::one(), |
|||
FieldT::one() * i |
|||
), ""); |
|||
ASSERT_TRUE(pb.is_satisfied()); |
|||
} |
|||
|
|||
TEST(circuit, values) |
|||
{ |
|||
typedef Fr<default_r1cs_ppzksnark_pp> FieldT; |
|||
test_value_equals<FieldT>(0); |
|||
test_value_equals<FieldT>(1); |
|||
test_value_equals<FieldT>(3); |
|||
test_value_equals<FieldT>(5391); |
|||
test_value_equals<FieldT>(883128374); |
|||
test_value_equals<FieldT>(173419028459); |
|||
test_value_equals<FieldT>(2205843009213693953); |
|||
} |
|||
|
|||
TEST(circuit, endianness) |
|||
{ |
|||
std::vector<unsigned char> before = { |
|||
0, 1, 2, 3, 4, 5, 6, 7, |
|||
8, 9, 10, 11, 12, 13, 14, 15, |
|||
16, 17, 18, 19, 20, 21, 22, 23, |
|||
24, 25, 26, 27, 28, 29, 30, 31, |
|||
32, 33, 34, 35, 36, 37, 38, 39, |
|||
40, 41, 42, 43, 44, 45, 46, 47, |
|||
48, 49, 50, 51, 52, 53, 54, 55, |
|||
56, 57, 58, 59, 60, 61, 62, 63 |
|||
}; |
|||
auto result = swap_endianness_u64(before); |
|||
|
|||
std::vector<unsigned char> after = { |
|||
56, 57, 58, 59, 60, 61, 62, 63, |
|||
48, 49, 50, 51, 52, 53, 54, 55, |
|||
40, 41, 42, 43, 44, 45, 46, 47, |
|||
32, 33, 34, 35, 36, 37, 38, 39, |
|||
24, 25, 26, 27, 28, 29, 30, 31, |
|||
16, 17, 18, 19, 20, 21, 22, 23, |
|||
8, 9, 10, 11, 12, 13, 14, 15, |
|||
0, 1, 2, 3, 4, 5, 6, 7 |
|||
}; |
|||
|
|||
EXPECT_EQ(after, result); |
|||
|
|||
std::vector<unsigned char> bad = {0, 1, 2, 3}; |
|||
|
|||
ASSERT_THROW(swap_endianness_u64(bad), std::length_error); |
|||
} |
|||
|
|||
template<typename FieldT> |
|||
bool test_merkle_gadget( |
|||
bool enforce_a, |
|||
bool enforce_b, |
|||
bool write_root_first |
|||
) |
|||
{ |
|||
protoboard<FieldT> pb; |
|||
digest_variable<FieldT> root(pb, 256, "root"); |
|||
pb.set_input_sizes(256); |
|||
|
|||
digest_variable<FieldT> commitment1(pb, 256, "commitment1"); |
|||
digest_variable<FieldT> commitment2(pb, 256, "commitment2"); |
|||
|
|||
pb_variable<FieldT> commitment1_read; |
|||
commitment1_read.allocate(pb); |
|||
pb_variable<FieldT> commitment2_read; |
|||
commitment2_read.allocate(pb); |
|||
|
|||
merkle_tree_gadget<FieldT> mgadget1(pb, commitment1, root, commitment1_read); |
|||
merkle_tree_gadget<FieldT> mgadget2(pb, commitment2, root, commitment2_read); |
|||
|
|||
commitment1.generate_r1cs_constraints(); |
|||
commitment2.generate_r1cs_constraints(); |
|||
root.generate_r1cs_constraints(); |
|||
mgadget1.generate_r1cs_constraints(); |
|||
mgadget2.generate_r1cs_constraints(); |
|||
|
|||
SproutMerkleTree tree; |
|||
uint256 commitment1_data = uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c"); |
|||
uint256 commitment2_data = uint256S("59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7"); |
|||
tree.append(commitment1_data); |
|||
auto wit1 = tree.witness(); |
|||
tree.append(commitment2_data); |
|||
wit1.append(commitment2_data); |
|||
auto wit2 = tree.witness(); |
|||
auto expected_root = tree.root(); |
|||
tree.append(uint256S("3e243c8798678570bb8d42616c23a536af44be15c4eef073490c2a44ae5f32c3")); |
|||
auto unexpected_root = tree.root(); |
|||
tree.append(uint256S("26d9b20c7f1c3d2528bbcd43cd63344b0afd3b6a0a8ebd37ec51cba34907bec7")); |
|||
auto badwit1 = tree.witness(); |
|||
tree.append(uint256S("02c2467c9cd15e0d150f74cd636505ed675b0b71b66a719f6f52fdb49a5937bb")); |
|||
auto badwit2 = tree.witness(); |
|||
|
|||
// Perform the test
|
|||
|
|||
pb.val(commitment1_read) = enforce_a ? FieldT::one() : FieldT::zero(); |
|||
pb.val(commitment2_read) = enforce_b ? FieldT::one() : FieldT::zero(); |
|||
|
|||
commitment1.bits.fill_with_bits(pb, uint256_to_bool_vector(commitment1_data)); |
|||
commitment2.bits.fill_with_bits(pb, uint256_to_bool_vector(commitment2_data)); |
|||
|
|||
if (write_root_first) { |
|||
root.bits.fill_with_bits(pb, uint256_to_bool_vector(expected_root)); |
|||
} |
|||
|
|||
mgadget1.generate_r1cs_witness(wit1.path()); |
|||
mgadget2.generate_r1cs_witness(wit2.path()); |
|||
|
|||
// Overwrite with our expected root
|
|||
root.bits.fill_with_bits(pb, uint256_to_bool_vector(expected_root)); |
|||
|
|||
return pb.is_satisfied(); |
|||
} |
|||
|
|||
TEST(circuit, merkle_tree_gadget_weirdness) |
|||
{ |
|||
/*
|
|||
The merkle tree gadget takes a leaf in the merkle tree (the Note commitment), |
|||
a merkle tree authentication path, and a root (anchor). It also takes a parameter |
|||
called read_success, which is used to determine if the commitment actually needs to |
|||
appear in the tree. |
|||
|
|||
If two input notes use the same root (which our protocol does) then if `read_success` |
|||
is disabled on the first note but enabled on the second note (i.e., the first note |
|||
has value of zero and second note has nonzero value) then there is an edge case in |
|||
the witnessing behavior. The first witness will accidentally constrain the root to |
|||
equal null (the default value of the anchor) and the second witness will actually |
|||
copy the bits, violating the constraint system. |
|||
|
|||
Notice that this edge case is not in the constraint system but in the witnessing |
|||
behavior. |
|||
*/ |
|||
|
|||
typedef Fr<default_r1cs_ppzksnark_pp> FieldT; |
|||
|
|||
// Test the normal case
|
|||
ASSERT_TRUE(test_merkle_gadget<FieldT>(true, true, false)); |
|||
ASSERT_TRUE(test_merkle_gadget<FieldT>(true, true, true)); |
|||
|
|||
// Test the case where the first commitment is enforced but the second isn't
|
|||
// Works because the first read is performed before the second one
|
|||
ASSERT_TRUE(test_merkle_gadget<FieldT>(true, false, false)); |
|||
ASSERT_TRUE(test_merkle_gadget<FieldT>(true, false, true)); |
|||
|
|||
// Test the case where the first commitment isn't enforced but the second is
|
|||
// Doesn't work because the first multipacker witnesses the existing root (which
|
|||
// is null)
|
|||
ASSERT_TRUE(!test_merkle_gadget<FieldT>(false, true, false)); |
|||
|
|||
// Test the last again, except this time write the root first.
|
|||
ASSERT_TRUE(test_merkle_gadget<FieldT>(false, true, true)); |
|||
} |
@ -1,702 +0,0 @@ |
|||
#include <gtest/gtest.h> |
|||
#include "zcash/Proof.hpp" |
|||
|
|||
#include <iostream> |
|||
|
|||
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp> |
|||
#include <libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp> |
|||
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp> |
|||
|
|||
using namespace libzcash; |
|||
|
|||
typedef libsnark::default_r1cs_ppzksnark_pp curve_pp; |
|||
typedef libsnark::default_r1cs_ppzksnark_pp::G1_type curve_G1; |
|||
typedef libsnark::default_r1cs_ppzksnark_pp::G2_type curve_G2; |
|||
typedef libsnark::default_r1cs_ppzksnark_pp::GT_type curve_GT; |
|||
typedef libsnark::default_r1cs_ppzksnark_pp::Fp_type curve_Fr; |
|||
typedef libsnark::default_r1cs_ppzksnark_pp::Fq_type curve_Fq; |
|||
typedef libsnark::default_r1cs_ppzksnark_pp::Fqe_type curve_Fq2; |
|||
|
|||
#include "streams.h" |
|||
#include "version.h" |
|||
#include "utilstrencodings.h" |
|||
|
|||
TEST(proofs, g1_pairing_at_infinity) |
|||
{ |
|||
for (size_t i = 0; i < 100; i++) { |
|||
auto r1 = curve_G1::random_element(); |
|||
auto r2 = curve_G2::random_element(); |
|||
ASSERT_TRUE( |
|||
curve_pp::reduced_pairing(curve_G1::zero(), r2) == |
|||
curve_GT::one() |
|||
); |
|||
ASSERT_TRUE( |
|||
curve_pp::final_exponentiation( |
|||
curve_pp::double_miller_loop( |
|||
curve_pp::precompute_G1(curve_G1::zero()), |
|||
curve_pp::precompute_G2(r2), |
|||
curve_pp::precompute_G1(curve_G1::zero()), |
|||
curve_pp::precompute_G2(r2) |
|||
) |
|||
) == |
|||
curve_GT::one() |
|||
); |
|||
ASSERT_TRUE( |
|||
curve_pp::final_exponentiation( |
|||
curve_pp::double_miller_loop( |
|||
curve_pp::precompute_G1(r1), |
|||
curve_pp::precompute_G2(r2), |
|||
curve_pp::precompute_G1(curve_G1::zero()), |
|||
curve_pp::precompute_G2(r2) |
|||
) |
|||
) == |
|||
curve_pp::reduced_pairing(r1, r2) |
|||
); |
|||
ASSERT_TRUE( |
|||
curve_pp::final_exponentiation( |
|||
curve_pp::double_miller_loop( |
|||
curve_pp::precompute_G1(curve_G1::zero()), |
|||
curve_pp::precompute_G2(r2), |
|||
curve_pp::precompute_G1(r1), |
|||
curve_pp::precompute_G2(r2) |
|||
) |
|||
) == |
|||
curve_pp::reduced_pairing(r1, r2) |
|||
); |
|||
} |
|||
} |
|||
|
|||
TEST(proofs, g2_subgroup_check) |
|||
{ |
|||
// all G2 elements are order r
|
|||
ASSERT_TRUE(libsnark::alt_bn128_modulus_r * curve_G2::random_element() == curve_G2::zero()); |
|||
|
|||
// but that doesn't mean all elements that satisfy the curve equation are in G2...
|
|||
curve_G2 p = curve_G2::one(); |
|||
|
|||
while (1) { |
|||
// This will construct an order r(2q-r) point with high probability
|
|||
p.X = curve_Fq2::random_element(); |
|||
try { |
|||
p.Y = ((p.X.squared() * p.X) + libsnark::alt_bn128_twist_coeff_b).sqrt(); |
|||
break; |
|||
} catch(...) {} |
|||
} |
|||
|
|||
ASSERT_TRUE(p.is_well_formed()); // it's on the curve
|
|||
ASSERT_TRUE(libsnark::alt_bn128_modulus_r * p != curve_G2::zero()); // but not the order r subgroup..
|
|||
|
|||
{ |
|||
// libsnark unfortunately doesn't check, and the pairing will complete
|
|||
auto e = curve_Fr("149"); |
|||
auto a = curve_pp::reduced_pairing(curve_G1::one(), p); |
|||
auto b = curve_pp::reduced_pairing(e * curve_G1::one(), p); |
|||
|
|||
// though it will not preserve bilinearity
|
|||
ASSERT_TRUE((a^e) != b); |
|||
} |
|||
|
|||
{ |
|||
// so, our decompression API should not allow you to decompress G2 elements of that form!
|
|||
CompressedG2 badp(p); |
|||
try { |
|||
auto newp = badp.to_libsnark_g2<curve_G2>(); |
|||
FAIL() << "Expected std::runtime_error"; |
|||
} catch (std::runtime_error const & err) { |
|||
EXPECT_EQ(err.what(), std::string("point is not in G2")); |
|||
} catch(...) { |
|||
FAIL() << "Expected std::runtime_error"; |
|||
} |
|||
} |
|||
|
|||
// educational purposes: showing that E'(Fp2) is of order r(2q-r),
|
|||
// by multiplying our random point in E' by (2q-r) = (q + q - r) to
|
|||
// get an element in G2
|
|||
{ |
|||
auto p1 = libsnark::alt_bn128_modulus_q * p; |
|||
p1 = p1 + p1; |
|||
p1 = p1 - (libsnark::alt_bn128_modulus_r * p); |
|||
|
|||
ASSERT_TRUE(p1.is_well_formed()); |
|||
ASSERT_TRUE(libsnark::alt_bn128_modulus_r * p1 == curve_G2::zero()); |
|||
|
|||
CompressedG2 goodp(p1); |
|||
auto newp = goodp.to_libsnark_g2<curve_G2>(); |
|||
|
|||
ASSERT_TRUE(newp == p1); |
|||
} |
|||
} |
|||
|
|||
TEST(proofs, sqrt_zero) |
|||
{ |
|||
ASSERT_TRUE(curve_Fq::zero() == curve_Fq::zero().sqrt()); |
|||
ASSERT_TRUE(curve_Fq2::zero() == curve_Fq2::zero().sqrt()); |
|||
} |
|||
|
|||
TEST(proofs, sqrt_fq) |
|||
{ |
|||
// Poor man's PRNG
|
|||
curve_Fq acc = curve_Fq("348957923485290374852379485") ^ 1000; |
|||
|
|||
size_t quadratic_residues = 0; |
|||
size_t quadratic_nonresidues = 0; |
|||
|
|||
for (size_t i = 1; i < 1000; i++) { |
|||
try { |
|||
acc += curve_Fq("45634563456") ^ i; |
|||
|
|||
curve_Fq x = acc.sqrt(); |
|||
ASSERT_TRUE((x*x) == acc); |
|||
quadratic_residues += 1; |
|||
} catch (std::runtime_error &e) { |
|||
quadratic_nonresidues += 1; |
|||
} |
|||
} |
|||
|
|||
// Half of all nonzero elements in Fp are quadratic residues
|
|||
ASSERT_TRUE(quadratic_residues == 511); |
|||
ASSERT_TRUE(quadratic_nonresidues == 488); |
|||
|
|||
for (size_t i = 0; i < 1000; i++) { |
|||
curve_Fq x = curve_Fq::random_element(); |
|||
curve_Fq x2 = x * x; |
|||
|
|||
ASSERT_TRUE((x2.sqrt() == x) || (x2.sqrt() == -x)); |
|||
} |
|||
|
|||
// Test vectors
|
|||
ASSERT_TRUE( |
|||
curve_Fq("5204065062716160319596273903996315000119019512886596366359652578430118331601") |
|||
== |
|||
curve_Fq("348579348568").sqrt() |
|||
); |
|||
ASSERT_THROW(curve_Fq("348579348569").sqrt(), std::runtime_error); |
|||
} |
|||
|
|||
TEST(proofs, sqrt_fq2) |
|||
{ |
|||
curve_Fq2 acc = curve_Fq2( |
|||
curve_Fq("3456293840592348059238409578239048769348760238476029347885092384059238459834") ^ 1000, |
|||
curve_Fq("2394578084760439457823945729347502374590283479582739485723945729384759823745") ^ 1000 |
|||
); |
|||
|
|||
size_t quadratic_residues = 0; |
|||
size_t quadratic_nonresidues = 0; |
|||
|
|||
for (size_t i = 1; i < 1000; i++) { |
|||
try { |
|||
acc = acc + curve_Fq2( |
|||
curve_Fq("5204065062716160319596273903996315000119019512886596366359652578430118331601") ^ i, |
|||
curve_Fq("348957923485290374852379485348957923485290374852379485348957923485290374852") ^ i |
|||
); |
|||
|
|||
curve_Fq2 x = acc.sqrt(); |
|||
ASSERT_TRUE((x*x) == acc); |
|||
quadratic_residues += 1; |
|||
} catch (std::runtime_error &e) { |
|||
quadratic_nonresidues += 1; |
|||
} |
|||
} |
|||
|
|||
// Half of all nonzero elements in Fp^k are quadratic residues as long
|
|||
// as p != 2
|
|||
ASSERT_TRUE(quadratic_residues == 505); |
|||
ASSERT_TRUE(quadratic_nonresidues == 494); |
|||
|
|||
for (size_t i = 0; i < 1000; i++) { |
|||
curve_Fq2 x = curve_Fq2::random_element(); |
|||
curve_Fq2 x2 = x * x; |
|||
|
|||
ASSERT_TRUE((x2.sqrt() == x) || (x2.sqrt() == -x)); |
|||
} |
|||
|
|||
// Test vectors
|
|||
ASSERT_THROW(curve_Fq2( |
|||
curve_Fq("2"), |
|||
curve_Fq("1") |
|||
).sqrt(), std::runtime_error); |
|||
|
|||
ASSERT_THROW(curve_Fq2( |
|||
curve_Fq("3345897230485723946872934576923485762803457692345760237495682347502347589473"), |
|||
curve_Fq("1234912378405347958234756902345768290345762348957605678245967234857634857676") |
|||
).sqrt(), std::runtime_error); |
|||
|
|||
curve_Fq2 x = curve_Fq2( |
|||
curve_Fq("12844195307879678418043983815760255909500142247603239203345049921980497041944"), |
|||
curve_Fq("7476417578426924565731404322659619974551724117137577781074613937423560117731") |
|||
); |
|||
|
|||
curve_Fq2 nx = -x; |
|||
|
|||
curve_Fq2 x2 = curve_Fq2( |
|||
curve_Fq("3345897230485723946872934576923485762803457692345760237495682347502347589474"), |
|||
curve_Fq("1234912378405347958234756902345768290345762348957605678245967234857634857676") |
|||
); |
|||
|
|||
ASSERT_TRUE(x == x2.sqrt()); |
|||
ASSERT_TRUE(nx == -x2.sqrt()); |
|||
ASSERT_TRUE(x*x == x2); |
|||
ASSERT_TRUE(nx*nx == x2); |
|||
} |
|||
|
|||
TEST(proofs, size_is_expected) |
|||
{ |
|||
PHGRProof p; |
|||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|||
ss << p; |
|||
|
|||
ASSERT_EQ(ss.size(), 296); |
|||
} |
|||
|
|||
TEST(proofs, fq_serializes_properly) |
|||
{ |
|||
for (size_t i = 0; i < 1000; i++) { |
|||
curve_Fq e = curve_Fq::random_element(); |
|||
|
|||
Fq e2(e); |
|||
|
|||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|||
ss << e2; |
|||
|
|||
Fq e3; |
|||
ss >> e3; |
|||
|
|||
curve_Fq e4 = e3.to_libsnark_fq<curve_Fq>(); |
|||
|
|||
ASSERT_TRUE(e == e4); |
|||
} |
|||
} |
|||
|
|||
TEST(proofs, fq2_serializes_properly) |
|||
{ |
|||
for (size_t i = 0; i < 1000; i++) { |
|||
curve_Fq2 e = curve_Fq2::random_element(); |
|||
|
|||
Fq2 e2(e); |
|||
|
|||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|||
ss << e2; |
|||
|
|||
Fq2 e3; |
|||
ss >> e3; |
|||
|
|||
curve_Fq2 e4 = e3.to_libsnark_fq2<curve_Fq2>(); |
|||
|
|||
ASSERT_TRUE(e == e4); |
|||
} |
|||
} |
|||
|
|||
template<typename T> |
|||
T deserialize_tv(std::string s) |
|||
{ |
|||
T e; |
|||
CDataStream ss(ParseHex(s), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss >> e; |
|||
|
|||
return e; |
|||
} |
|||
|
|||
curve_Fq deserialize_fq(std::string s) |
|||
{ |
|||
return deserialize_tv<Fq>(s).to_libsnark_fq<curve_Fq>(); |
|||
} |
|||
|
|||
curve_Fq2 deserialize_fq2(std::string s) |
|||
{ |
|||
return deserialize_tv<Fq2>(s).to_libsnark_fq2<curve_Fq2>(); |
|||
} |
|||
|
|||
TEST(proofs, fq_valid) |
|||
{ |
|||
curve_Fq e = deserialize_fq("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"); |
|||
|
|||
ASSERT_TRUE(e == curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582")); |
|||
ASSERT_TRUE(e != curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208581")); |
|||
|
|||
curve_Fq e2 = deserialize_fq("30644e72e131a029b75045b68181585d97816a916871ca8d3c208c16d87cfd46"); |
|||
|
|||
ASSERT_TRUE(e2 == curve_Fq("21888242871839275222221885816603420866962577604863418715751138068690288573766")); |
|||
} |
|||
|
|||
TEST(proofs, fq_invalid) |
|||
{ |
|||
// Should not be able to deserialize the modulus
|
|||
ASSERT_THROW( |
|||
deserialize_fq("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"), |
|||
std::logic_error |
|||
); |
|||
|
|||
// Should not be able to deserialize the modulus plus one
|
|||
ASSERT_THROW( |
|||
deserialize_fq("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd48"), |
|||
std::logic_error |
|||
); |
|||
|
|||
// Should not be able to deserialize a ridiculously out of bound int
|
|||
ASSERT_THROW( |
|||
deserialize_fq("ff644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), |
|||
std::logic_error |
|||
); |
|||
} |
|||
|
|||
TEST(proofs, fq2_valid) |
|||
{ |
|||
// (q - 1) * q + q
|
|||
curve_Fq2 e = deserialize_fq2("0925c4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b0"); |
|||
ASSERT_TRUE(e.c0 == curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582")); |
|||
ASSERT_TRUE(e.c1 == curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582")); |
|||
|
|||
curve_Fq2 e2 = deserialize_fq2("000000000000000000000000000000000000000000000000010245be1c91e3186bbbe1c430a93fcfc5aada4ab10c3492f70eea97a91c7b29554db55acffa34d2"); |
|||
ASSERT_TRUE(e2.c0 == curve_Fq("238769481237490823")); |
|||
ASSERT_TRUE(e2.c1 == curve_Fq("384579238459723485")); |
|||
|
|||
curve_Fq2 e3 = deserialize_fq2("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); |
|||
ASSERT_TRUE(e3.c0 == curve_Fq("0")); |
|||
ASSERT_TRUE(e3.c1 == curve_Fq("0")); |
|||
|
|||
curve_Fq2 e4 = deserialize_fq2("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); |
|||
ASSERT_TRUE(e4.c0 == curve_Fq("1")); |
|||
ASSERT_TRUE(e4.c1 == curve_Fq("0")); |
|||
} |
|||
|
|||
TEST(proofs, fq2_invalid) |
|||
{ |
|||
// (q - 1) * q + q is invalid
|
|||
ASSERT_THROW( |
|||
deserialize_fq2("0925c4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b1"), |
|||
std::logic_error |
|||
); |
|||
|
|||
// q * q + (q - 1) is invalid
|
|||
ASSERT_THROW( |
|||
deserialize_fq2("0925c4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d34cced085b43e2f202a05e52ef18233a3d8371be725c8b8e7774e4b8ffda66f7"), |
|||
std::logic_error |
|||
); |
|||
|
|||
// Ridiculously out of bounds
|
|||
ASSERT_THROW( |
|||
deserialize_fq2("0fffc4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b0"), |
|||
std::logic_error |
|||
); |
|||
ASSERT_THROW( |
|||
deserialize_fq2("ffffffff763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b0"), |
|||
std::logic_error |
|||
); |
|||
} |
|||
|
|||
TEST(proofs, g1_serializes_properly) |
|||
{ |
|||
// Cannot serialize zero
|
|||
{ |
|||
ASSERT_THROW({CompressedG1 g = CompressedG1(curve_G1::zero());}, std::domain_error); |
|||
} |
|||
|
|||
for (size_t i = 0; i < 1000; i++) { |
|||
curve_G1 e = curve_G1::random_element(); |
|||
|
|||
CompressedG1 e2(e); |
|||
|
|||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|||
ss << e2; |
|||
|
|||
CompressedG1 e3; |
|||
ss >> e3; |
|||
|
|||
ASSERT_TRUE(e2 == e3); |
|||
|
|||
curve_G1 e4 = e3.to_libsnark_g1<curve_G1>(); |
|||
|
|||
ASSERT_TRUE(e == e4); |
|||
} |
|||
} |
|||
|
|||
TEST(proofs, g2_serializes_properly) |
|||
{ |
|||
// Cannot serialize zero
|
|||
{ |
|||
ASSERT_THROW({CompressedG2 g = CompressedG2(curve_G2::zero());}, std::domain_error); |
|||
} |
|||
|
|||
for (size_t i = 0; i < 1000; i++) { |
|||
curve_G2 e = curve_G2::random_element(); |
|||
|
|||
CompressedG2 e2(e); |
|||
|
|||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|||
ss << e2; |
|||
|
|||
CompressedG2 e3; |
|||
ss >> e3; |
|||
|
|||
ASSERT_TRUE(e2 == e3); |
|||
|
|||
curve_G2 e4 = e3.to_libsnark_g2<curve_G2>(); |
|||
|
|||
ASSERT_TRUE(e == e4); |
|||
} |
|||
} |
|||
|
|||
TEST(proofs, zksnark_serializes_properly) |
|||
{ |
|||
auto example = libsnark::generate_r1cs_example_with_field_input<curve_Fr>(250, 4); |
|||
example.constraint_system.swap_AB_if_beneficial(); |
|||
auto kp = libsnark::r1cs_ppzksnark_generator<curve_pp>(example.constraint_system); |
|||
auto vkprecomp = libsnark::r1cs_ppzksnark_verifier_process_vk(kp.vk); |
|||
|
|||
for (size_t i = 0; i < 20; i++) { |
|||
auto badproof = PHGRProof::random_invalid(); |
|||
auto proof = badproof.to_libsnark_proof<libsnark::r1cs_ppzksnark_proof<curve_pp>>(); |
|||
|
|||
auto verifierEnabled = ProofVerifier::Strict(); |
|||
auto verifierDisabled = ProofVerifier::Disabled(); |
|||
// This verifier should catch the bad proof
|
|||
ASSERT_FALSE(verifierEnabled.check( |
|||
kp.vk, |
|||
vkprecomp, |
|||
example.primary_input, |
|||
proof |
|||
)); |
|||
// This verifier won't!
|
|||
ASSERT_TRUE(verifierDisabled.check( |
|||
kp.vk, |
|||
vkprecomp, |
|||
example.primary_input, |
|||
proof |
|||
)); |
|||
} |
|||
|
|||
for (size_t i = 0; i < 20; i++) { |
|||
auto proof = libsnark::r1cs_ppzksnark_prover<curve_pp>( |
|||
kp.pk, |
|||
example.primary_input, |
|||
example.auxiliary_input, |
|||
example.constraint_system |
|||
); |
|||
|
|||
{ |
|||
auto verifierEnabled = ProofVerifier::Strict(); |
|||
auto verifierDisabled = ProofVerifier::Disabled(); |
|||
ASSERT_TRUE(verifierEnabled.check( |
|||
kp.vk, |
|||
vkprecomp, |
|||
example.primary_input, |
|||
proof |
|||
)); |
|||
ASSERT_TRUE(verifierDisabled.check( |
|||
kp.vk, |
|||
vkprecomp, |
|||
example.primary_input, |
|||
proof |
|||
)); |
|||
} |
|||
|
|||
ASSERT_TRUE(libsnark::r1cs_ppzksnark_verifier_strong_IC<curve_pp>( |
|||
kp.vk, |
|||
example.primary_input, |
|||
proof |
|||
)); |
|||
|
|||
PHGRProof compressed_proof_0(proof); |
|||
|
|||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|||
ss << compressed_proof_0; |
|||
|
|||
PHGRProof compressed_proof_1; |
|||
ss >> compressed_proof_1; |
|||
|
|||
ASSERT_TRUE(compressed_proof_0 == compressed_proof_1); |
|||
|
|||
auto newproof = compressed_proof_1.to_libsnark_proof<libsnark::r1cs_ppzksnark_proof<curve_pp>>(); |
|||
|
|||
ASSERT_TRUE(proof == newproof); |
|||
ASSERT_TRUE(libsnark::r1cs_ppzksnark_verifier_strong_IC<curve_pp>( |
|||
kp.vk, |
|||
example.primary_input, |
|||
newproof |
|||
)); |
|||
} |
|||
} |
|||
|
|||
TEST(proofs, g1_deserialization) |
|||
{ |
|||
CompressedG1 g; |
|||
curve_G1 expected; |
|||
|
|||
// Valid G1 element.
|
|||
{ |
|||
CDataStream ss(ParseHex("0230644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss >> g; |
|||
|
|||
expected.X = curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"); |
|||
expected.Y = curve_Fq("3969792565221544645472939191694882283483352126195956956354061729942568608776"); |
|||
expected.Z = curve_Fq::one(); |
|||
|
|||
ASSERT_TRUE(g.to_libsnark_g1<curve_G1>() == expected); |
|||
} |
|||
|
|||
// Its negation.
|
|||
{ |
|||
CDataStream ss(ParseHex("0330644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss >> g; |
|||
|
|||
expected.X = curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"); |
|||
expected.Y = curve_Fq("3969792565221544645472939191694882283483352126195956956354061729942568608776"); |
|||
expected.Z = curve_Fq::one(); |
|||
|
|||
ASSERT_TRUE(g.to_libsnark_g1<curve_G1>() == -expected); |
|||
} |
|||
|
|||
// Invalid leading bytes
|
|||
{ |
|||
CDataStream ss(ParseHex("ff30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), SER_NETWORK, PROTOCOL_VERSION); |
|||
|
|||
ASSERT_THROW(ss >> g, std::ios_base::failure); |
|||
} |
|||
|
|||
// Invalid point
|
|||
{ |
|||
CDataStream ss(ParseHex("0208c6d2adffacbc8438f09f321874ea66e2fcc29f8dcfec2caefa21ec8c96a77c"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss >> g; |
|||
|
|||
ASSERT_THROW(g.to_libsnark_g1<curve_G1>(), std::runtime_error); |
|||
} |
|||
|
|||
// Point with out of bounds Fq
|
|||
{ |
|||
CDataStream ss(ParseHex("02ffc6d2adffacbc8438f09f321874ea66e2fcc29f8dcfec2caefa21ec8c96a77c"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss >> g; |
|||
|
|||
ASSERT_THROW(g.to_libsnark_g1<curve_G1>(), std::logic_error); |
|||
} |
|||
|
|||
// Randomly produce valid G1 representations and fail/succeed to
|
|||
// turn them into G1 points based on whether they are valid.
|
|||
for (size_t i = 0; i < 5000; i++) { |
|||
curve_Fq e = curve_Fq::random_element(); |
|||
CDataStream ss(ParseHex("02"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss << Fq(e); |
|||
CompressedG1 g; |
|||
ss >> g; |
|||
|
|||
try { |
|||
curve_G1 g_real = g.to_libsnark_g1<curve_G1>(); |
|||
} catch(...) { |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
TEST(proofs, g2_deserialization) |
|||
{ |
|||
CompressedG2 g; |
|||
curve_G2 expected = curve_G2::random_element(); |
|||
|
|||
// Valid G2 point
|
|||
{ |
|||
CDataStream ss(ParseHex("0a023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss >> g; |
|||
|
|||
expected.X = curve_Fq2( |
|||
curve_Fq("5923585509243758863255447226263146374209884951848029582715967108651637186684"), |
|||
curve_Fq("5336385337059958111259504403491065820971993066694750945459110579338490853570") |
|||
); |
|||
expected.Y = curve_Fq2( |
|||
curve_Fq("10374495865873200088116930399159835104695426846400310764827677226300185211748"), |
|||
curve_Fq("5256529835065685814318509161957442385362539991735248614869838648137856366932") |
|||
); |
|||
expected.Z = curve_Fq2::one(); |
|||
|
|||
ASSERT_TRUE(g.to_libsnark_g2<curve_G2>() == expected); |
|||
} |
|||
|
|||
// Its negation
|
|||
{ |
|||
CDataStream ss(ParseHex("0b023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss >> g; |
|||
|
|||
expected.X = curve_Fq2( |
|||
curve_Fq("5923585509243758863255447226263146374209884951848029582715967108651637186684"), |
|||
curve_Fq("5336385337059958111259504403491065820971993066694750945459110579338490853570") |
|||
); |
|||
expected.Y = curve_Fq2( |
|||
curve_Fq("10374495865873200088116930399159835104695426846400310764827677226300185211748"), |
|||
curve_Fq("5256529835065685814318509161957442385362539991735248614869838648137856366932") |
|||
); |
|||
expected.Z = curve_Fq2::one(); |
|||
|
|||
ASSERT_TRUE(g.to_libsnark_g2<curve_G2>() == -expected); |
|||
} |
|||
|
|||
// Invalid leading bytes
|
|||
{ |
|||
CDataStream ss(ParseHex("ff023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION); |
|||
|
|||
ASSERT_THROW(ss >> g, std::ios_base::failure); |
|||
} |
|||
|
|||
|
|||
// Invalid point
|
|||
{ |
|||
CDataStream ss(ParseHex("0b023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984b"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss >> g; |
|||
|
|||
ASSERT_THROW(g.to_libsnark_g2<curve_G2>(), std::runtime_error); |
|||
} |
|||
|
|||
// Point with out of bounds Fq2
|
|||
{ |
|||
CDataStream ss(ParseHex("0a0f3aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss >> g; |
|||
|
|||
ASSERT_THROW(g.to_libsnark_g2<curve_G2>(), std::logic_error); |
|||
} |
|||
|
|||
// Randomly produce valid G2 representations and fail/succeed to
|
|||
// turn them into G2 points based on whether they are valid.
|
|||
for (size_t i = 0; i < 5000; i++) { |
|||
curve_Fq2 e = curve_Fq2::random_element(); |
|||
CDataStream ss(ParseHex("0a"), SER_NETWORK, PROTOCOL_VERSION); |
|||
ss << Fq2(e); |
|||
CompressedG2 g; |
|||
ss >> g; |
|||
|
|||
try { |
|||
curve_G2 g_real = g.to_libsnark_g2<curve_G2>(); |
|||
} catch(...) { |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#include "json_test_vectors.h" |
|||
#include "test/data/g1_compressed.json.h" |
|||
|
|||
TEST(proofs, g1_test_vectors) |
|||
{ |
|||
UniValue v = read_json(std::string(json_tests::g1_compressed, json_tests::g1_compressed + sizeof(json_tests::g1_compressed))); |
|||
|
|||
curve_G1 e = curve_Fr("34958239045823") * curve_G1::one(); |
|||
for (size_t i = 0; i < 10000; i++) { |
|||
e = (curve_Fr("34958239045823") ^ i) * e; |
|||
auto expected = CompressedG1(e); |
|||
|
|||
expect_test_vector(v[i], expected); |
|||
ASSERT_TRUE(expected.to_libsnark_g1<curve_G1>() == e); |
|||
} |
|||
} |
|||
|
|||
#include "test/data/g2_compressed.json.h" |
|||
|
|||
TEST(proofs, g2_test_vectors) |
|||
{ |
|||
UniValue v = read_json(std::string(json_tests::g2_compressed, json_tests::g2_compressed + sizeof(json_tests::g2_compressed))); |
|||
|
|||
curve_G2 e = curve_Fr("34958239045823") * curve_G2::one(); |
|||
for (size_t i = 0; i < 10000; i++) { |
|||
e = (curve_Fr("34958239045823") ^ i) * e; |
|||
auto expected = CompressedG2(e); |
|||
|
|||
expect_test_vector(v[i], expected); |
|||
ASSERT_TRUE(expected.to_libsnark_g2<curve_G2>() == e); |
|||
} |
|||
} |
@ -1,40 +0,0 @@ |
|||
// Copyright (c) 2016 The Zcash developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#include "../util.h" |
|||
#include "primitives/transaction.h" |
|||
#include "zcash/JoinSplit.hpp" |
|||
|
|||
#include "libsnark/common/profiling.hpp" |
|||
#include "komodo_defs.h" |
|||
char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; |
|||
uint16_t BITCOIND_RPCPORT = 7771; |
|||
uint32_t ASSETCHAINS_CC = 0; |
|||
|
|||
using namespace libzcash; |
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
libsnark::start_profiling(); |
|||
|
|||
auto p = ZCJoinSplit::Prepared((ZC_GetParamsDir() / "sprout-verifying.key").string(), |
|||
(ZC_GetParamsDir() / "sprout-proving.key").string()); |
|||
|
|||
// construct a proof.
|
|||
|
|||
for (int i = 0; i < 5; i++) { |
|||
uint256 anchor = ZCIncrementalMerkleTree().root(); |
|||
uint256 pubKeyHash; |
|||
|
|||
JSDescription jsdesc(*p, |
|||
pubKeyHash, |
|||
anchor, |
|||
{JSInput(), JSInput()}, |
|||
{JSOutput(), JSOutput()}, |
|||
0, |
|||
0); |
|||
} |
|||
|
|||
delete p; // not that it matters
|
|||
} |
@ -1,26 +0,0 @@ |
|||
#include "zcash/JoinSplit.hpp" |
|||
|
|||
#include <iostream> |
|||
#include "crypto/common.h" |
|||
|
|||
int64_t MAX_MONEY = 200000000 * 100000000LL; |
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
if (init_and_check_sodium() == -1) { |
|||
return 1; |
|||
} |
|||
|
|||
if(argc != 4) { |
|||
std::cerr << "Usage: " << argv[0] << " provingKeyFileName verificationKeyFileName r1csFileName" << std::endl; |
|||
return 1; |
|||
} |
|||
|
|||
std::string pkFile = argv[1]; |
|||
std::string vkFile = argv[2]; |
|||
std::string r1csFile = argv[3]; |
|||
|
|||
ZCJoinSplit::Generate(r1csFile, vkFile, pkFile); |
|||
|
|||
return 0; |
|||
} |
@ -1,100 +0,0 @@ |
|||
template<typename FieldT> |
|||
class note_commitment_gadget : gadget<FieldT> { |
|||
private: |
|||
std::shared_ptr<block_variable<FieldT>> block1; |
|||
std::shared_ptr<block_variable<FieldT>> block2; |
|||
std::shared_ptr<sha256_compression_function_gadget<FieldT>> hasher1; |
|||
std::shared_ptr<digest_variable<FieldT>> intermediate_hash; |
|||
std::shared_ptr<sha256_compression_function_gadget<FieldT>> hasher2; |
|||
|
|||
public: |
|||
note_commitment_gadget( |
|||
protoboard<FieldT> &pb, |
|||
pb_variable<FieldT>& ZERO, |
|||
pb_variable_array<FieldT>& a_pk, |
|||
pb_variable_array<FieldT>& v, |
|||
pb_variable_array<FieldT>& rho, |
|||
pb_variable_array<FieldT>& r, |
|||
std::shared_ptr<digest_variable<FieldT>> result |
|||
) : gadget<FieldT>(pb) { |
|||
pb_variable_array<FieldT> leading_byte = |
|||
from_bits({1, 0, 1, 1, 0, 0, 0, 0}, ZERO); |
|||
|
|||
pb_variable_array<FieldT> first_of_rho(rho.begin(), rho.begin()+184); |
|||
pb_variable_array<FieldT> last_of_rho(rho.begin()+184, rho.end()); |
|||
|
|||
intermediate_hash.reset(new digest_variable<FieldT>(pb, 256, "")); |
|||
|
|||
// final padding |
|||
pb_variable_array<FieldT> length_padding = |
|||
from_bits({ |
|||
// padding |
|||
1,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
|
|||
// length of message (840 bits) |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
0,0,0,0,0,0,1,1, |
|||
0,1,0,0,1,0,0,0 |
|||
}, ZERO); |
|||
|
|||
block1.reset(new block_variable<FieldT>(pb, { |
|||
leading_byte, |
|||
a_pk, |
|||
v, |
|||
first_of_rho |
|||
}, "")); |
|||
|
|||
block2.reset(new block_variable<FieldT>(pb, { |
|||
last_of_rho, |
|||
r, |
|||
length_padding |
|||
}, "")); |
|||
|
|||
pb_linear_combination_array<FieldT> IV = SHA256_default_IV(pb); |
|||
|
|||
hasher1.reset(new sha256_compression_function_gadget<FieldT>( |
|||
pb, |
|||
IV, |
|||
block1->bits, |
|||
*intermediate_hash, |
|||
"")); |
|||
|
|||
pb_linear_combination_array<FieldT> IV2(intermediate_hash->bits); |
|||
|
|||
hasher2.reset(new sha256_compression_function_gadget<FieldT>( |
|||
pb, |
|||
IV2, |
|||
block2->bits, |
|||
*result, |
|||
"")); |
|||
} |
|||
|
|||
void generate_r1cs_constraints() { |
|||
hasher1->generate_r1cs_constraints(); |
|||
hasher2->generate_r1cs_constraints(); |
|||
} |
|||
|
|||
void generate_r1cs_witness() { |
|||
hasher1->generate_r1cs_witness(); |
|||
hasher2->generate_r1cs_witness(); |
|||
} |
|||
}; |
@ -1,349 +0,0 @@ |
|||
#include "zcash/circuit/utils.tcc" |
|||
#include "zcash/circuit/prfs.tcc" |
|||
#include "zcash/circuit/commitment.tcc" |
|||
#include "zcash/circuit/merkle.tcc" |
|||
#include "zcash/circuit/note.tcc" |
|||
|
|||
template<typename FieldT, size_t NumInputs, size_t NumOutputs> |
|||
class joinsplit_gadget : gadget<FieldT> { |
|||
private: |
|||
// Verifier inputs |
|||
pb_variable_array<FieldT> zk_packed_inputs; |
|||
pb_variable_array<FieldT> zk_unpacked_inputs; |
|||
std::shared_ptr<multipacking_gadget<FieldT>> unpacker; |
|||
|
|||
std::shared_ptr<digest_variable<FieldT>> zk_merkle_root; |
|||
std::shared_ptr<digest_variable<FieldT>> zk_h_sig; |
|||
std::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_nullifiers; |
|||
std::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_macs; |
|||
std::array<std::shared_ptr<digest_variable<FieldT>>, NumOutputs> zk_output_commitments; |
|||
pb_variable_array<FieldT> zk_vpub_old; |
|||
pb_variable_array<FieldT> zk_vpub_new; |
|||
|
|||
// Aux inputs |
|||
pb_variable<FieldT> ZERO; |
|||
std::shared_ptr<digest_variable<FieldT>> zk_phi; |
|||
pb_variable_array<FieldT> zk_total_uint64; |
|||
|
|||
// Input note gadgets |
|||
std::array<std::shared_ptr<input_note_gadget<FieldT>>, NumInputs> zk_input_notes; |
|||
std::array<std::shared_ptr<PRF_pk_gadget<FieldT>>, NumInputs> zk_mac_authentication; |
|||
|
|||
// Output note gadgets |
|||
std::array<std::shared_ptr<output_note_gadget<FieldT>>, NumOutputs> zk_output_notes; |
|||
|
|||
public: |
|||
// PRF_pk only has a 1-bit domain separation "nonce" |
|||
// for different macs. |
|||
BOOST_STATIC_ASSERT(NumInputs <= 2); |
|||
|
|||
// PRF_rho only has a 1-bit domain separation "nonce" |
|||
// for different output `rho`. |
|||
BOOST_STATIC_ASSERT(NumOutputs <= 2); |
|||
|
|||
joinsplit_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) { |
|||
// Verification |
|||
{ |
|||
// The verification inputs are all bit-strings of various |
|||
// lengths (256-bit digests and 64-bit integers) and so we |
|||
// pack them into as few field elements as possible. (The |
|||
// more verification inputs you have, the more expensive |
|||
// verification is.) |
|||
zk_packed_inputs.allocate(pb, verifying_field_element_size()); |
|||
pb.set_input_sizes(verifying_field_element_size()); |
|||
|
|||
alloc_uint256(zk_unpacked_inputs, zk_merkle_root); |
|||
alloc_uint256(zk_unpacked_inputs, zk_h_sig); |
|||
|
|||
for (size_t i = 0; i < NumInputs; i++) { |
|||
alloc_uint256(zk_unpacked_inputs, zk_input_nullifiers[i]); |
|||
alloc_uint256(zk_unpacked_inputs, zk_input_macs[i]); |
|||
} |
|||
|
|||
for (size_t i = 0; i < NumOutputs; i++) { |
|||
alloc_uint256(zk_unpacked_inputs, zk_output_commitments[i]); |
|||
} |
|||
|
|||
alloc_uint64(zk_unpacked_inputs, zk_vpub_old); |
|||
alloc_uint64(zk_unpacked_inputs, zk_vpub_new); |
|||
|
|||
assert(zk_unpacked_inputs.size() == verifying_input_bit_size()); |
|||
|
|||
// This gadget will ensure that all of the inputs we provide are |
|||
// boolean constrained. |
|||
unpacker.reset(new multipacking_gadget<FieldT>( |
|||
pb, |
|||
zk_unpacked_inputs, |
|||
zk_packed_inputs, |
|||
FieldT::capacity(), |
|||
"unpacker" |
|||
)); |
|||
} |
|||
|
|||
// We need a constant "zero" variable in some contexts. In theory |
|||
// it should never be necessary, but libsnark does not synthesize |
|||
// optimal circuits. |
|||
// |
|||
// The first variable of our constraint system is constrained |
|||
// to be one automatically for us, and is known as `ONE`. |
|||
ZERO.allocate(pb); |
|||
|
|||
zk_phi.reset(new digest_variable<FieldT>(pb, 252, "")); |
|||
|
|||
zk_total_uint64.allocate(pb, 64); |
|||
|
|||
for (size_t i = 0; i < NumInputs; i++) { |
|||
// Input note gadget for commitments, macs, nullifiers, |
|||
// and spend authority. |
|||
zk_input_notes[i].reset(new input_note_gadget<FieldT>( |
|||
pb, |
|||
ZERO, |
|||
zk_input_nullifiers[i], |
|||
*zk_merkle_root |
|||
)); |
|||
|
|||
// The input keys authenticate h_sig to prevent |
|||
// malleability. |
|||
zk_mac_authentication[i].reset(new PRF_pk_gadget<FieldT>( |
|||
pb, |
|||
ZERO, |
|||
zk_input_notes[i]->a_sk->bits, |
|||
zk_h_sig->bits, |
|||
i ? true : false, |
|||
zk_input_macs[i] |
|||
)); |
|||
} |
|||
|
|||
for (size_t i = 0; i < NumOutputs; i++) { |
|||
zk_output_notes[i].reset(new output_note_gadget<FieldT>( |
|||
pb, |
|||
ZERO, |
|||
zk_phi->bits, |
|||
zk_h_sig->bits, |
|||
i ? true : false, |
|||
zk_output_commitments[i] |
|||
)); |
|||
} |
|||
} |
|||
|
|||
void generate_r1cs_constraints() { |
|||
// The true passed here ensures all the inputs |
|||
// are boolean constrained. |
|||
unpacker->generate_r1cs_constraints(true); |
|||
|
|||
// Constrain `ZERO` |
|||
generate_r1cs_equals_const_constraint<FieldT>(this->pb, ZERO, FieldT::zero(), "ZERO"); |
|||
|
|||
// Constrain bitness of phi |
|||
zk_phi->generate_r1cs_constraints(); |
|||
|
|||
for (size_t i = 0; i < NumInputs; i++) { |
|||
// Constrain the JoinSplit input constraints. |
|||
zk_input_notes[i]->generate_r1cs_constraints(); |
|||
|
|||
// Authenticate h_sig with a_sk |
|||
zk_mac_authentication[i]->generate_r1cs_constraints(); |
|||
} |
|||
|
|||
for (size_t i = 0; i < NumOutputs; i++) { |
|||
// Constrain the JoinSplit output constraints. |
|||
zk_output_notes[i]->generate_r1cs_constraints(); |
|||
} |
|||
|
|||
// Value balance |
|||
{ |
|||
linear_combination<FieldT> left_side = packed_addition(zk_vpub_old); |
|||
for (size_t i = 0; i < NumInputs; i++) { |
|||
left_side = left_side + packed_addition(zk_input_notes[i]->value); |
|||
} |
|||
|
|||
linear_combination<FieldT> right_side = packed_addition(zk_vpub_new); |
|||
for (size_t i = 0; i < NumOutputs; i++) { |
|||
right_side = right_side + packed_addition(zk_output_notes[i]->value); |
|||
} |
|||
|
|||
// Ensure that both sides are equal |
|||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>( |
|||
1, |
|||
left_side, |
|||
right_side |
|||
)); |
|||
|
|||
// #854: Ensure that left_side is a 64-bit integer. |
|||
for (size_t i = 0; i < 64; i++) { |
|||
generate_boolean_r1cs_constraint<FieldT>( |
|||
this->pb, |
|||
zk_total_uint64[i], |
|||
"" |
|||
); |
|||
} |
|||
|
|||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>( |
|||
1, |
|||
left_side, |
|||
packed_addition(zk_total_uint64) |
|||
)); |
|||
} |
|||
} |
|||
|
|||
void generate_r1cs_witness( |
|||
const uint252& phi, |
|||
const uint256& rt, |
|||
const uint256& h_sig, |
|||
const std::array<JSInput, NumInputs>& inputs, |
|||
const std::array<SproutNote, NumOutputs>& outputs, |
|||
uint64_t vpub_old, |
|||
uint64_t vpub_new |
|||
) { |
|||
// Witness `zero` |
|||
this->pb.val(ZERO) = FieldT::zero(); |
|||
|
|||
// Witness rt. This is not a sanity check. |
|||
// |
|||
// This ensures the read gadget constrains |
|||
// the intended root in the event that |
|||
// both inputs are zero-valued. |
|||
zk_merkle_root->bits.fill_with_bits( |
|||
this->pb, |
|||
uint256_to_bool_vector(rt) |
|||
); |
|||
|
|||
// Witness public balance values |
|||
zk_vpub_old.fill_with_bits( |
|||
this->pb, |
|||
uint64_to_bool_vector(vpub_old) |
|||
); |
|||
zk_vpub_new.fill_with_bits( |
|||
this->pb, |
|||
uint64_to_bool_vector(vpub_new) |
|||
); |
|||
|
|||
{ |
|||
// Witness total_uint64 bits |
|||
uint64_t left_side_acc = vpub_old; |
|||
for (size_t i = 0; i < NumInputs; i++) { |
|||
left_side_acc += inputs[i].note.value(); |
|||
} |
|||
|
|||
zk_total_uint64.fill_with_bits( |
|||
this->pb, |
|||
uint64_to_bool_vector(left_side_acc) |
|||
); |
|||
} |
|||
|
|||
// Witness phi |
|||
zk_phi->bits.fill_with_bits( |
|||
this->pb, |
|||
uint252_to_bool_vector(phi) |
|||
); |
|||
|
|||
// Witness h_sig |
|||
zk_h_sig->bits.fill_with_bits( |
|||
this->pb, |
|||
uint256_to_bool_vector(h_sig) |
|||
); |
|||
|
|||
for (size_t i = 0; i < NumInputs; i++) { |
|||
// Witness the input information. |
|||
auto merkle_path = inputs[i].witness.path(); |
|||
zk_input_notes[i]->generate_r1cs_witness( |
|||
merkle_path, |
|||
inputs[i].key, |
|||
inputs[i].note |
|||
); |
|||
|
|||
// Witness macs |
|||
zk_mac_authentication[i]->generate_r1cs_witness(); |
|||
} |
|||
|
|||
for (size_t i = 0; i < NumOutputs; i++) { |
|||
// Witness the output information. |
|||
zk_output_notes[i]->generate_r1cs_witness(outputs[i]); |
|||
} |
|||
|
|||
// [SANITY CHECK] Ensure that the intended root |
|||
// was witnessed by the inputs, even if the read |
|||
// gadget overwrote it. This allows the prover to |
|||
// fail instead of the verifier, in the event that |
|||
// the roots of the inputs do not match the |
|||
// treestate provided to the proving API. |
|||
zk_merkle_root->bits.fill_with_bits( |
|||
this->pb, |
|||
uint256_to_bool_vector(rt) |
|||
); |
|||
|
|||
// This happens last, because only by now are all the |
|||
// verifier inputs resolved. |
|||
unpacker->generate_r1cs_witness_from_bits(); |
|||
} |
|||
|
|||
static r1cs_primary_input<FieldT> witness_map( |
|||
const uint256& rt, |
|||
const uint256& h_sig, |
|||
const std::array<uint256, NumInputs>& macs, |
|||
const std::array<uint256, NumInputs>& nullifiers, |
|||
const std::array<uint256, NumOutputs>& commitments, |
|||
uint64_t vpub_old, |
|||
uint64_t vpub_new |
|||
) { |
|||
std::vector<bool> verify_inputs; |
|||
|
|||
insert_uint256(verify_inputs, rt); |
|||
insert_uint256(verify_inputs, h_sig); |
|||
|
|||
for (size_t i = 0; i < NumInputs; i++) { |
|||
insert_uint256(verify_inputs, nullifiers[i]); |
|||
insert_uint256(verify_inputs, macs[i]); |
|||
} |
|||
|
|||
for (size_t i = 0; i < NumOutputs; i++) { |
|||
insert_uint256(verify_inputs, commitments[i]); |
|||
} |
|||
|
|||
insert_uint64(verify_inputs, vpub_old); |
|||
insert_uint64(verify_inputs, vpub_new); |
|||
|
|||
assert(verify_inputs.size() == verifying_input_bit_size()); |
|||
auto verify_field_elements = pack_bit_vector_into_field_element_vector<FieldT>(verify_inputs); |
|||
assert(verify_field_elements.size() == verifying_field_element_size()); |
|||
return verify_field_elements; |
|||
} |
|||
|
|||
static size_t verifying_input_bit_size() { |
|||
size_t acc = 0; |
|||
|
|||
acc += 256; // the merkle root (anchor) |
|||
acc += 256; // h_sig |
|||
for (size_t i = 0; i < NumInputs; i++) { |
|||
acc += 256; // nullifier |
|||
acc += 256; // mac |
|||
} |
|||
for (size_t i = 0; i < NumOutputs; i++) { |
|||
acc += 256; // new commitment |
|||
} |
|||
acc += 64; // vpub_old |
|||
acc += 64; // vpub_new |
|||
|
|||
return acc; |
|||
} |
|||
|
|||
static size_t verifying_field_element_size() { |
|||
return div_ceil(verifying_input_bit_size(), FieldT::capacity()); |
|||
} |
|||
|
|||
void alloc_uint256( |
|||
pb_variable_array<FieldT>& packed_into, |
|||
std::shared_ptr<digest_variable<FieldT>>& var |
|||
) { |
|||
var.reset(new digest_variable<FieldT>(this->pb, 256, "")); |
|||
packed_into.insert(packed_into.end(), var->bits.begin(), var->bits.end()); |
|||
} |
|||
|
|||
void alloc_uint64( |
|||
pb_variable_array<FieldT>& packed_into, |
|||
pb_variable_array<FieldT>& integer |
|||
) { |
|||
integer.allocate(this->pb, 64, ""); |
|||
packed_into.insert(packed_into.end(), integer.begin(), integer.end()); |
|||
} |
|||
}; |
@ -1,60 +0,0 @@ |
|||
template<typename FieldT> |
|||
class merkle_tree_gadget : gadget<FieldT> { |
|||
private: |
|||
typedef sha256_two_to_one_hash_gadget<FieldT> sha256_gadget; |
|||
|
|||
pb_variable_array<FieldT> positions; |
|||
std::shared_ptr<merkle_authentication_path_variable<FieldT, sha256_gadget>> authvars; |
|||
std::shared_ptr<merkle_tree_check_read_gadget<FieldT, sha256_gadget>> auth; |
|||
|
|||
public: |
|||
merkle_tree_gadget( |
|||
protoboard<FieldT>& pb, |
|||
digest_variable<FieldT> leaf, |
|||
digest_variable<FieldT> root, |
|||
pb_variable<FieldT>& enforce |
|||
) : gadget<FieldT>(pb) { |
|||
positions.allocate(pb, INCREMENTAL_MERKLE_TREE_DEPTH); |
|||
authvars.reset(new merkle_authentication_path_variable<FieldT, sha256_gadget>( |
|||
pb, INCREMENTAL_MERKLE_TREE_DEPTH, "auth" |
|||
)); |
|||
auth.reset(new merkle_tree_check_read_gadget<FieldT, sha256_gadget>( |
|||
pb, |
|||
INCREMENTAL_MERKLE_TREE_DEPTH, |
|||
positions, |
|||
leaf, |
|||
root, |
|||
*authvars, |
|||
enforce, |
|||
"" |
|||
)); |
|||
} |
|||
|
|||
void generate_r1cs_constraints() { |
|||
for (size_t i = 0; i < INCREMENTAL_MERKLE_TREE_DEPTH; i++) { |
|||
// TODO: This might not be necessary, and doesn't |
|||
// appear to be done in libsnark's tests, but there |
|||
// is no documentation, so let's do it anyway to |
|||
// be safe. |
|||
generate_boolean_r1cs_constraint<FieldT>( |
|||
this->pb, |
|||
positions[i], |
|||
"boolean_positions" |
|||
); |
|||
} |
|||
|
|||
authvars->generate_r1cs_constraints(); |
|||
auth->generate_r1cs_constraints(); |
|||
} |
|||
|
|||
void generate_r1cs_witness(const MerklePath& path) { |
|||
// TODO: Change libsnark so that it doesn't require this goofy |
|||
// number thing in its API. |
|||
size_t path_index = convertVectorToInt(path.index); |
|||
|
|||
positions.fill_with_bits_of_uint64(this->pb, path_index); |
|||
|
|||
authvars->generate_r1cs_witness(path_index, path.authentication_path); |
|||
auth->generate_r1cs_witness(); |
|||
} |
|||
}; |
@ -1,244 +0,0 @@ |
|||
template<typename FieldT> |
|||
class note_gadget : public gadget<FieldT> { |
|||
public: |
|||
pb_variable_array<FieldT> value; |
|||
std::shared_ptr<digest_variable<FieldT>> r; |
|||
|
|||
note_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) { |
|||
value.allocate(pb, 64); |
|||
r.reset(new digest_variable<FieldT>(pb, 256, "")); |
|||
} |
|||
|
|||
void generate_r1cs_constraints() { |
|||
for (size_t i = 0; i < 64; i++) { |
|||
generate_boolean_r1cs_constraint<FieldT>( |
|||
this->pb, |
|||
value[i], |
|||
"boolean_value" |
|||
); |
|||
} |
|||
|
|||
r->generate_r1cs_constraints(); |
|||
} |
|||
|
|||
void generate_r1cs_witness(const SproutNote& note) { |
|||
r->bits.fill_with_bits(this->pb, uint256_to_bool_vector(note.r)); |
|||
value.fill_with_bits(this->pb, uint64_to_bool_vector(note.value())); |
|||
} |
|||
}; |
|||
|
|||
template<typename FieldT> |
|||
class input_note_gadget : public note_gadget<FieldT> { |
|||
private: |
|||
std::shared_ptr<digest_variable<FieldT>> a_pk; |
|||
std::shared_ptr<digest_variable<FieldT>> rho; |
|||
|
|||
std::shared_ptr<digest_variable<FieldT>> commitment; |
|||
std::shared_ptr<note_commitment_gadget<FieldT>> commit_to_inputs; |
|||
|
|||
pb_variable<FieldT> value_enforce; |
|||
std::shared_ptr<merkle_tree_gadget<FieldT>> witness_input; |
|||
|
|||
std::shared_ptr<PRF_addr_a_pk_gadget<FieldT>> spend_authority; |
|||
std::shared_ptr<PRF_nf_gadget<FieldT>> expose_nullifiers; |
|||
public: |
|||
std::shared_ptr<digest_variable<FieldT>> a_sk; |
|||
|
|||
input_note_gadget( |
|||
protoboard<FieldT>& pb, |
|||
pb_variable<FieldT>& ZERO, |
|||
std::shared_ptr<digest_variable<FieldT>> nullifier, |
|||
digest_variable<FieldT> rt |
|||
) : note_gadget<FieldT>(pb) { |
|||
a_sk.reset(new digest_variable<FieldT>(pb, 252, "")); |
|||
a_pk.reset(new digest_variable<FieldT>(pb, 256, "")); |
|||
rho.reset(new digest_variable<FieldT>(pb, 256, "")); |
|||
commitment.reset(new digest_variable<FieldT>(pb, 256, "")); |
|||
|
|||
spend_authority.reset(new PRF_addr_a_pk_gadget<FieldT>( |
|||
pb, |
|||
ZERO, |
|||
a_sk->bits, |
|||
a_pk |
|||
)); |
|||
|
|||
expose_nullifiers.reset(new PRF_nf_gadget<FieldT>( |
|||
pb, |
|||
ZERO, |
|||
a_sk->bits, |
|||
rho->bits, |
|||
nullifier |
|||
)); |
|||
|
|||
commit_to_inputs.reset(new note_commitment_gadget<FieldT>( |
|||
pb, |
|||
ZERO, |
|||
a_pk->bits, |
|||
this->value, |
|||
rho->bits, |
|||
this->r->bits, |
|||
commitment |
|||
)); |
|||
|
|||
value_enforce.allocate(pb); |
|||
|
|||
witness_input.reset(new merkle_tree_gadget<FieldT>( |
|||
pb, |
|||
*commitment, |
|||
rt, |
|||
value_enforce |
|||
)); |
|||
} |
|||
|
|||
void generate_r1cs_constraints() { |
|||
note_gadget<FieldT>::generate_r1cs_constraints(); |
|||
|
|||
a_sk->generate_r1cs_constraints(); |
|||
rho->generate_r1cs_constraints(); |
|||
|
|||
spend_authority->generate_r1cs_constraints(); |
|||
expose_nullifiers->generate_r1cs_constraints(); |
|||
|
|||
commit_to_inputs->generate_r1cs_constraints(); |
|||
|
|||
// value * (1 - enforce) = 0 |
|||
// Given `enforce` is boolean constrained: |
|||
// If `value` is zero, `enforce` _can_ be zero. |
|||
// If `value` is nonzero, `enforce` _must_ be one. |
|||
generate_boolean_r1cs_constraint<FieldT>(this->pb, value_enforce,""); |
|||
|
|||
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>( |
|||
packed_addition(this->value), |
|||
(1 - value_enforce), |
|||
0 |
|||
), ""); |
|||
|
|||
witness_input->generate_r1cs_constraints(); |
|||
} |
|||
|
|||
void generate_r1cs_witness( |
|||
const MerklePath& path, |
|||
const SproutSpendingKey& key, |
|||
const SproutNote& note |
|||
) { |
|||
note_gadget<FieldT>::generate_r1cs_witness(note); |
|||
|
|||
// Witness a_sk for the input |
|||
a_sk->bits.fill_with_bits( |
|||
this->pb, |
|||
uint252_to_bool_vector(key) |
|||
); |
|||
|
|||
// Witness a_pk for a_sk with PRF_addr |
|||
spend_authority->generate_r1cs_witness(); |
|||
|
|||
// [SANITY CHECK] Witness a_pk with note information |
|||
a_pk->bits.fill_with_bits( |
|||
this->pb, |
|||
uint256_to_bool_vector(note.a_pk) |
|||
); |
|||
|
|||
// Witness rho for the input note |
|||
rho->bits.fill_with_bits( |
|||
this->pb, |
|||
uint256_to_bool_vector(note.rho) |
|||
); |
|||
|
|||
// Witness the nullifier for the input note |
|||
expose_nullifiers->generate_r1cs_witness(); |
|||
|
|||
// Witness the commitment of the input note |
|||
commit_to_inputs->generate_r1cs_witness(); |
|||
|
|||
// [SANITY CHECK] Ensure the commitment is |
|||
// valid. |
|||
commitment->bits.fill_with_bits( |
|||
this->pb, |
|||
uint256_to_bool_vector(note.cm()) |
|||
); |
|||
|
|||
// Set enforce flag for nonzero input value |
|||
this->pb.val(value_enforce) = (note.value() != 0) ? FieldT::one() : FieldT::zero(); |
|||
|
|||
// Witness merkle tree authentication path |
|||
witness_input->generate_r1cs_witness(path); |
|||
} |
|||
}; |
|||
|
|||
template<typename FieldT> |
|||
class output_note_gadget : public note_gadget<FieldT> { |
|||
private: |
|||
std::shared_ptr<digest_variable<FieldT>> rho; |
|||
std::shared_ptr<digest_variable<FieldT>> a_pk; |
|||
|
|||
std::shared_ptr<PRF_rho_gadget<FieldT>> prevent_faerie_gold; |
|||
std::shared_ptr<note_commitment_gadget<FieldT>> commit_to_outputs; |
|||
|
|||
public: |
|||
output_note_gadget( |
|||
protoboard<FieldT>& pb, |
|||
pb_variable<FieldT>& ZERO, |
|||
pb_variable_array<FieldT>& phi, |
|||
pb_variable_array<FieldT>& h_sig, |
|||
bool nonce, |
|||
std::shared_ptr<digest_variable<FieldT>> commitment |
|||
) : note_gadget<FieldT>(pb) { |
|||
rho.reset(new digest_variable<FieldT>(pb, 256, "")); |
|||
a_pk.reset(new digest_variable<FieldT>(pb, 256, "")); |
|||
|
|||
// Do not allow the caller to choose the same "rho" |
|||
// for any two valid notes in a given view of the |
|||
// blockchain. See protocol specification for more |
|||
// details. |
|||
prevent_faerie_gold.reset(new PRF_rho_gadget<FieldT>( |
|||
pb, |
|||
ZERO, |
|||
phi, |
|||
h_sig, |
|||
nonce, |
|||
rho |
|||
)); |
|||
|
|||
// Commit to the output notes publicly without |
|||
// disclosing them. |
|||
commit_to_outputs.reset(new note_commitment_gadget<FieldT>( |
|||
pb, |
|||
ZERO, |
|||
a_pk->bits, |
|||
this->value, |
|||
rho->bits, |
|||
this->r->bits, |
|||
commitment |
|||
)); |
|||
} |
|||
|
|||
void generate_r1cs_constraints() { |
|||
note_gadget<FieldT>::generate_r1cs_constraints(); |
|||
|
|||
a_pk->generate_r1cs_constraints(); |
|||
|
|||
prevent_faerie_gold->generate_r1cs_constraints(); |
|||
|
|||
commit_to_outputs->generate_r1cs_constraints(); |
|||
} |
|||
|
|||
void generate_r1cs_witness(const SproutNote& note) { |
|||
note_gadget<FieldT>::generate_r1cs_witness(note); |
|||
|
|||
prevent_faerie_gold->generate_r1cs_witness(); |
|||
|
|||
// [SANITY CHECK] Witness rho ourselves with the |
|||
// note information. |
|||
rho->bits.fill_with_bits( |
|||
this->pb, |
|||
uint256_to_bool_vector(note.rho) |
|||
); |
|||
|
|||
a_pk->bits.fill_with_bits( |
|||
this->pb, |
|||
uint256_to_bool_vector(note.a_pk) |
|||
); |
|||
|
|||
commit_to_outputs->generate_r1cs_witness(); |
|||
} |
|||
}; |
@ -1,109 +0,0 @@ |
|||
template<typename FieldT> |
|||
class PRF_gadget : gadget<FieldT> { |
|||
private: |
|||
std::shared_ptr<block_variable<FieldT>> block; |
|||
std::shared_ptr<sha256_compression_function_gadget<FieldT>> hasher; |
|||
std::shared_ptr<digest_variable<FieldT>> result; |
|||
|
|||
public: |
|||
PRF_gadget( |
|||
protoboard<FieldT>& pb, |
|||
pb_variable<FieldT>& ZERO, |
|||
bool a, |
|||
bool b, |
|||
bool c, |
|||
bool d, |
|||
pb_variable_array<FieldT> x, |
|||
pb_variable_array<FieldT> y, |
|||
std::shared_ptr<digest_variable<FieldT>> result |
|||
) : gadget<FieldT>(pb), result(result) { |
|||
|
|||
pb_linear_combination_array<FieldT> IV = SHA256_default_IV(pb); |
|||
|
|||
pb_variable_array<FieldT> discriminants; |
|||
discriminants.emplace_back(a ? ONE : ZERO); |
|||
discriminants.emplace_back(b ? ONE : ZERO); |
|||
discriminants.emplace_back(c ? ONE : ZERO); |
|||
discriminants.emplace_back(d ? ONE : ZERO); |
|||
|
|||
block.reset(new block_variable<FieldT>(pb, { |
|||
discriminants, |
|||
x, |
|||
y |
|||
}, "PRF_block")); |
|||
|
|||
hasher.reset(new sha256_compression_function_gadget<FieldT>( |
|||
pb, |
|||
IV, |
|||
block->bits, |
|||
*result, |
|||
"PRF_hasher")); |
|||
} |
|||
|
|||
void generate_r1cs_constraints() { |
|||
hasher->generate_r1cs_constraints(); |
|||
} |
|||
|
|||
void generate_r1cs_witness() { |
|||
hasher->generate_r1cs_witness(); |
|||
} |
|||
}; |
|||
|
|||
template<typename FieldT> |
|||
pb_variable_array<FieldT> gen256zeroes(pb_variable<FieldT>& ZERO) { |
|||
pb_variable_array<FieldT> ret; |
|||
while (ret.size() < 256) { |
|||
ret.emplace_back(ZERO); |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
template<typename FieldT> |
|||
class PRF_addr_a_pk_gadget : public PRF_gadget<FieldT> { |
|||
public: |
|||
PRF_addr_a_pk_gadget( |
|||
protoboard<FieldT>& pb, |
|||
pb_variable<FieldT>& ZERO, |
|||
pb_variable_array<FieldT>& a_sk, |
|||
std::shared_ptr<digest_variable<FieldT>> result |
|||
) : PRF_gadget<FieldT>(pb, ZERO, 1, 1, 0, 0, a_sk, gen256zeroes(ZERO), result) {} |
|||
}; |
|||
|
|||
template<typename FieldT> |
|||
class PRF_nf_gadget : public PRF_gadget<FieldT> { |
|||
public: |
|||
PRF_nf_gadget( |
|||
protoboard<FieldT>& pb, |
|||
pb_variable<FieldT>& ZERO, |
|||
pb_variable_array<FieldT>& a_sk, |
|||
pb_variable_array<FieldT>& rho, |
|||
std::shared_ptr<digest_variable<FieldT>> result |
|||
) : PRF_gadget<FieldT>(pb, ZERO, 1, 1, 1, 0, a_sk, rho, result) {} |
|||
}; |
|||
|
|||
template<typename FieldT> |
|||
class PRF_pk_gadget : public PRF_gadget<FieldT> { |
|||
public: |
|||
PRF_pk_gadget( |
|||
protoboard<FieldT>& pb, |
|||
pb_variable<FieldT>& ZERO, |
|||
pb_variable_array<FieldT>& a_sk, |
|||
pb_variable_array<FieldT>& h_sig, |
|||
bool nonce, |
|||
std::shared_ptr<digest_variable<FieldT>> result |
|||
) : PRF_gadget<FieldT>(pb, ZERO, 0, nonce, 0, 0, a_sk, h_sig, result) {} |
|||
}; |
|||
|
|||
template<typename FieldT> |
|||
class PRF_rho_gadget : public PRF_gadget<FieldT> { |
|||
public: |
|||
PRF_rho_gadget( |
|||
protoboard<FieldT>& pb, |
|||
pb_variable<FieldT>& ZERO, |
|||
pb_variable_array<FieldT>& phi, |
|||
pb_variable_array<FieldT>& h_sig, |
|||
bool nonce, |
|||
std::shared_ptr<digest_variable<FieldT>> result |
|||
) : PRF_gadget<FieldT>(pb, ZERO, 0, nonce, 1, 0, phi, h_sig, result) {} |
|||
}; |
@ -1,75 +0,0 @@ |
|||
#include "uint252.h" |
|||
|
|||
template<typename FieldT> |
|||
pb_variable_array<FieldT> from_bits(std::vector<bool> bits, pb_variable<FieldT>& ZERO) { |
|||
pb_variable_array<FieldT> acc; |
|||
|
|||
BOOST_FOREACH(bool bit, bits) { |
|||
acc.emplace_back(bit ? ONE : ZERO); |
|||
} |
|||
|
|||
return acc; |
|||
} |
|||
|
|||
std::vector<bool> trailing252(std::vector<bool> input) { |
|||
if (input.size() != 256) { |
|||
throw std::length_error("trailing252 input invalid length"); |
|||
} |
|||
|
|||
return std::vector<bool>(input.begin() + 4, input.end()); |
|||
} |
|||
|
|||
template<typename T> |
|||
std::vector<bool> to_bool_vector(T input) { |
|||
std::vector<unsigned char> input_v(input.begin(), input.end()); |
|||
|
|||
return convertBytesVectorToVector(input_v); |
|||
} |
|||
|
|||
std::vector<bool> uint256_to_bool_vector(uint256 input) { |
|||
return to_bool_vector(input); |
|||
} |
|||
|
|||
std::vector<bool> uint252_to_bool_vector(uint252 input) { |
|||
return trailing252(to_bool_vector(input)); |
|||
} |
|||
|
|||
std::vector<bool> uint64_to_bool_vector(uint64_t input) { |
|||
auto num_bv = convertIntToVectorLE(input); |
|||
|
|||
return convertBytesVectorToVector(num_bv); |
|||
} |
|||
|
|||
void insert_uint256(std::vector<bool>& into, uint256 from) { |
|||
std::vector<bool> blob = uint256_to_bool_vector(from); |
|||
into.insert(into.end(), blob.begin(), blob.end()); |
|||
} |
|||
|
|||
void insert_uint64(std::vector<bool>& into, uint64_t from) { |
|||
std::vector<bool> num = uint64_to_bool_vector(from); |
|||
into.insert(into.end(), num.begin(), num.end()); |
|||
} |
|||
|
|||
template<typename T> |
|||
T swap_endianness_u64(T v) { |
|||
if (v.size() != 64) { |
|||
throw std::length_error("invalid bit length for 64-bit unsigned integer"); |
|||
} |
|||
|
|||
for (size_t i = 0; i < 4; i++) { |
|||
for (size_t j = 0; j < 8; j++) { |
|||
std::swap(v[i*8 + j], v[((7-i)*8)+j]); |
|||
} |
|||
} |
|||
|
|||
return v; |
|||
} |
|||
|
|||
template<typename FieldT> |
|||
linear_combination<FieldT> packed_addition(pb_variable_array<FieldT> input) { |
|||
auto input_swapped = swap_endianness_u64(input); |
|||
|
|||
return pb_packing_sum<FieldT>(pb_variable_array<FieldT>( |
|||
input_swapped.rbegin(), input_swapped.rend() |
|||
)); |
|||
} |
Loading…
Reference in new issue