diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..830d3c82f5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000..31d0a43b42 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..94a25f7f4c --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000000..7aeb59971c --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1644842195939 + + + + + + \ No newline at end of file diff --git a/output.txt b/output.txt new file mode 100644 index 0000000000..b38ad8b4d7 --- /dev/null +++ b/output.txt @@ -0,0 +1,5 @@ +Running 1 test case... +PASSED +PASSED +PASSED +PASSED diff --git a/src/Makefile.am b/src/Makefile.am index fdeab1406c..04e8c9b203 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -55,6 +55,7 @@ LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBLELANTUS=liblelantus.a +LIBAURA=libaura.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBBITCOINQT=qt/libfiroqt.a LIBSECP256K1=secp256k1/libsecp256k1.la @@ -86,7 +87,8 @@ EXTRA_LIBRARIES += \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) \ $(LIBFIRO_SIGMA) \ - $(LIBLELANTUS) + $(LIBLELANTUS) \ + $(LIBAURA) lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS) @@ -691,6 +693,24 @@ libsigma_a_SOURCES = \ sigma/params.cpp \ sigma/openssl_context.h +libaura_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -Werror +libaura_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -Werror +libaura_a_SOURCES = \ + libaura/r1_proof.h \ + libaura/r1_proof_generator.h \ + libaura/r1_proof_generator.hpp \ + libaura/r1_proof_verifier.h \ + libaura/r1_proof_verifier.hpp \ + libaura/sigmaplus_proof.h \ + libaura/sigmaplus_prover.h \ + libaura/sigmaplus_prover.hpp \ + libaura/sigmaplus_verifier.h \ + libaura/sigmaplus_verifier.hpp \ + libaura/aura_primitives.h \ + libaura/aura_primitives.hpp \ + libaura/params.h\ + libaura/params.cpp + if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp AM_LDFLAGS += $(COMPAT_LDFLAGS) @@ -729,6 +749,7 @@ firod_LDADD = \ $(LIBBITCOIN_WALLET) \ $(LIBFIRO_SIGMA) \ $(LIBLELANTUS) \ + $(LIBAURA) \ $(LIBBITCOIN_ZMQ) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 2e1e951287..7801649d86 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -97,6 +97,8 @@ BITCOIN_TESTS = \ sigma/test/r1_test.cpp \ sigma/test/serialize_test.cpp \ sigma/test/sigma_primitive_types_test.cpp \ + libaura/test/protocol_tests.cpp \ + libaura/test/hoom_tests.cpp \ test/addrman_tests.cpp \ test/allocator_tests.cpp \ test/amount_tests.cpp \ @@ -198,8 +200,8 @@ endif test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) -ltor test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) -test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS) -test_test_bitcoin_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBFIRO_SIGMA) $(LIBLELANTUS) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \ +test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS) $(LIBBLSSIG_INCLUDES) +test_test_bitcoin_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBFIRO_SIGMA) $(LIBAURA) $(LIBLELANTUS) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \ $(BACKTRACE_LIB) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_PTHREADS_LIBS) $(ZMQ_LIBS) $(ZLIB_LIBS) test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) if ENABLE_WALLET diff --git a/src/libaura/aura_primitives.h b/src/libaura/aura_primitives.h new file mode 100755 index 0000000000..f35717e285 --- /dev/null +++ b/src/libaura/aura_primitives.h @@ -0,0 +1,58 @@ +#ifndef FIRO_AURA_SIGMA_SIGMA_PRIMITIVES_H +#define FIRO_AURA_SIGMA_SIGMA_PRIMITIVES_H + +#include "../secp256k1/include/MultiExponent.h" +#include "../secp256k1/include/GroupElement.h" +#include "../secp256k1/include/Scalar.h" + +#include +#include + +namespace aura { + +template +struct NthPower { + Exponent num; + Exponent pow; + + NthPower(const Exponent& num_) : num(num_), pow(uint64_t(1)) {} + NthPower(const Exponent& num_, const Exponent& pow_) : num(num_), pow(pow_) {} + + void go_next() { + pow *= num; + } +}; + +template +class SigmaPrimitives { + +public: + static void commit(const GroupElement& g, + const std::vector& h, + const std::vector& exp, + const Exponent& r, + GroupElement& result_out); + + static GroupElement commit(const GroupElement& g, const Exponent m, const GroupElement h, const Exponent r); + + static void convert_to_sigma(uint64_t num, uint64_t n, uint64_t m, std::vector& out); + + static std::vector convert_to_nal(uint64_t num, uint64_t n, uint64_t m); + + static void generate_challenge(const std::vector& group_elements, + Exponent& result_out); + + /** \brief Adds a factor of (x*x + a) to the given polynomial in coefficients. + * \param[in,out] coefficients Coefficients of the polynomial created. + */ + static void new_factor(const Exponent& x, const Exponent& a, std::vector& coefficients); + + static GroupElement HelperFunction(const std::vector& commits, const std::vector & x); + + }; + +} // namespace aura + +#include "aura_primitives.hpp" + +#endif // FIRO_AURA_SIGMA_SIGMA_PRIMITIVES_H diff --git a/src/libaura/aura_primitives.hpp b/src/libaura/aura_primitives.hpp new file mode 100755 index 0000000000..bf8c1d70ca --- /dev/null +++ b/src/libaura/aura_primitives.hpp @@ -0,0 +1,103 @@ +#include "../../crypto/sha256.h" + +namespace aura { + +template +void SigmaPrimitives::commit(const GroupElement& g, + const std::vector& h, + const std::vector& exp, + const Exponent& r, + GroupElement& result_out) { + secp_primitives::MultiExponent mult(h, exp); + result_out += g * r + mult.get_multiple(); +} + +template +GroupElement SigmaPrimitives::commit( + const GroupElement& g, + const Exponent m, + const GroupElement h, + const Exponent r){ + return g * m + h * r; +} + +template +void SigmaPrimitives::convert_to_sigma( + uint64_t num, + uint64_t n, + uint64_t m, + std::vector& out) { + uint64_t rem; + uint64_t j = 0; + + for (j = 0; j < m; ++j) + { + rem = num % n; + num /= n; + for (uint64_t i = 0; i < n; ++i) { + if(i == rem) + out.push_back(Exponent(uint64_t(1))); + else + out.push_back(Exponent(uint64_t(0))); + } + } +} + +template +std::vector SigmaPrimitives::convert_to_nal( + uint64_t num, + uint64_t n, + uint64_t m) { + std::vector result; + uint64_t rem; + uint64_t j = 0; + while (num != 0) + { + rem = num % n; + num /= n; + result.push_back(rem); + j++; + } + result.resize(m); + return result; +} + +template +void SigmaPrimitives::generate_challenge( + const std::vector& group_elements, + Exponent& result_out) { + if (group_elements.empty()) + throw std::runtime_error("Group elements empty while generating a challenge."); + CSHA256 hash; + std::vector data(group_elements.size() * group_elements[0].memoryRequired()); + unsigned char* current = data.data(); + for (size_t i = 0; i < group_elements.size(); ++i) { + current = group_elements[i].serialize(current); + } + hash.Write(data.data(), data.size()); + unsigned char result_data[CSHA256::OUTPUT_SIZE]; + hash.Finalize(result_data); + result_out = result_data; +} + +template +void SigmaPrimitives::new_factor( + const Exponent& x, + const Exponent& a, + std::vector& coefficients) { + std::size_t degree = coefficients.size(); + coefficients.push_back(x * coefficients[degree-1]); + for (std::size_t d = degree-1; d >= 1; --d) + coefficients[d] = a * coefficients[d] + x * coefficients[d-1]; + coefficients[0] *= a; +} + + template + GroupElement SigmaPrimitives::HelperFunction( + const std::vector& commits, + const std::vector & x) { + secp_primitives::MultiExponent mult(commits, x); + return mult.get_multiple(); + } + +} // namespace aura diff --git a/src/libaura/hoom_proof.h b/src/libaura/hoom_proof.h new file mode 100755 index 0000000000..f8c1eac550 --- /dev/null +++ b/src/libaura/hoom_proof.h @@ -0,0 +1,75 @@ +#ifndef FIRO_AURA_HOOM_PROOF_H +#define FIRO_AURA_HOOM_PROOF_H + +#include "params.h" +#include "sigmaplus_proof.h" + +namespace aura { + +template +class HOOMProof { +public: + int t_n_; + int t_m_; + int m_n_; + int m_m_; + std::vector d_; + SigmaPlusProof d_Proof_; + SigmaPlusProof D_Proof_; + +public: + HOOMProof(int t_n_, int t_m_, int m_n_, int m_m_): + t_n_(t_n_), t_m_(t_m_), m_n_(m_n_), m_m_(m_m_), d_Proof_(m_n_, m_m_), D_Proof_(t_n_, t_m_) {}; + +public: + bool operator==(const HOOMProof& other) const { + return t_n_ == other.t_n_ && + t_m_ == other.t_m_ && + m_n_ == other.t_n_ && + m_m_ == other.t_m_ && + d_ == other.d_ && + d_Proof_ == other.d_Proof_ && + D_Proof_ == other.D_Proof_; + } + + bool operator!=(const HOOMProof& other) const { + return !(*this == other); + } + +public: + inline int memoryRequired() const { + return d_.memoryRequired() + + d_Proof_.memoryRequired(m_n_, m_m_) + + D_Proof_.memoryRequired(t_n_, t_m_); + } + +/* inline unsigned char* serialize(unsigned char* buffer) const { + unsigned char* current = B_.serialize(buffer); + current = r1Proof_.serialize(current); + for (std::size_t i = 0; i < Gk_.size(); ++i) + current = Gk_[i].serialize(current); + return z_.serialize(current); + } + + inline unsigned const char* deserialize(unsigned const char* buffer) { + unsigned const char* current = B_.deserialize(buffer); + current = r1Proof_.deserialize(current, n, m); + Gk_.resize(m); + for(int i = 0; i < m; ++i) + current = Gk_[i].deserialize(current); + return z_.deserialize(current); + } + + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(B_); + READWRITE(r1Proof_); + READWRITE(Gk_); + READWRITE(z_); + } */ +}; + +} //namespace aura + +#endif // FIRO_AURA_HOOM_PROOF_H diff --git a/src/libaura/hoom_prover.h b/src/libaura/hoom_prover.h new file mode 100755 index 0000000000..72f6065780 --- /dev/null +++ b/src/libaura/hoom_prover.h @@ -0,0 +1,36 @@ +#ifndef FIRO_AURA_HOOM_PROVER_H +#define FIRO_AURA_HOOM_PROVER_H + +#include "sigmaplus_prover.h" +#include "hoom_proof.h" + +#include + +namespace aura { + +template +class HOOMProver{ + +public: + HOOMProver(const GroupElement& g, + const std::vector& h_gens, + int t_n, int t_m, int m_n, int m_m); + void proof(const std::vector& commits, + const int& l, + const Exponent& r, + HOOMProof& proof_out); + +private: + GroupElement g_; + std::vector h_; + int t_n_; + int t_m_; + int m_n_; + int m_m_; +}; + +} // namespace aura + +#include "hoom_prover.hpp" + +#endif // FIRO_AURA_SIGMA_SIGMAPLUS_PROVER_H diff --git a/src/libaura/hoom_prover.hpp b/src/libaura/hoom_prover.hpp new file mode 100755 index 0000000000..3c34bc3c5f --- /dev/null +++ b/src/libaura/hoom_prover.hpp @@ -0,0 +1,73 @@ +#include +#include + +namespace aura { + + template + HOOMProver::HOOMProver(const GroupElement& g, + const std::vector& h_gens, + int t_n, int t_m, int m_n, int m_m) + : g_(g) + , h_(h_gens) + , t_n_(t_n) + , t_m_(t_m) + , m_n_(m_n) + , m_m_(m_m) { + } + + template + void HOOMProver::proof(const std::vector& commits, + const int& l, + const Exponent& r, + HOOMProof& proof_out) { + + int t_ = pow(t_n_, t_m_); + int m_ = pow(m_n_, m_m_); + + //binds the commitments to the y challenge + Exponent y; + SigmaPrimitives::generate_challenge(commits, y); + + //generates blinding keys and binds the correct subset + std::vector r_; + r_.resize(m_); + for (int k = 0; k < m_; ++k) { + r_[k].randomize(); + } + proof_out.d_.resize(m_); + int ptr = l / m_ * m_; + for (int k = 0; k < m_; ++k) { + proof_out.d_[k] = commits[ptr + k] + h_[0] * r_[k]; + } + + //generates one out of many sigma proof for the blinded subset + aura::SigmaPlusProver d_prover(g_, h_, m_n_, m_m_); + d_prover.proof(proof_out.d_, l % m_, r_[l % m_] + r, true, y, proof_out.d_Proof_); + + //generates x challenge using the y challenge calculated in the sigma + std::vector x; + x.resize(m_); + SigmaPrimitives::generate_challenge({h_[0] * y}, x[0]); + secp_primitives::Scalar xsum = r_[0] * x[0]; + for (int k = 1; k < m_; ++k) { + SigmaPrimitives::generate_challenge({h_[0] * x[k - 1]}, x[k]); + xsum += r_[k] * x[k]; + } + + //creates digests of the subsets + std::vector D_; + D_.resize(t_); + GroupElement D; + D = SigmaPrimitives::HelperFunction(proof_out.d_, x); + for (int k = 0; k < t_; ++k) { + D_[k] = D + SigmaPrimitives::HelperFunction({commits.begin() + k * m_, commits.begin() + (k + 1) * m_}, x).inverse(); + } + + //generates proof that the digest of our blinded subset is a member of the digests of all subsets + aura::SigmaPlusProver D_prover(g_, h_, t_n_, t_m_); + D_prover.proof(D_, l / m_, xsum, true, x[m_ - 1], proof_out.D_Proof_); + } + + + +} // namespace aura diff --git a/src/libaura/hoom_verifier.h b/src/libaura/hoom_verifier.h new file mode 100755 index 0000000000..5112be7a56 --- /dev/null +++ b/src/libaura/hoom_verifier.h @@ -0,0 +1,37 @@ +#ifndef FIRO_AURA_HOOM_VERIFIER_H +#define FIRO_AURA_HOOM_VERIFIER_H + +#include "sigmaplus_verifier.h" +#include "util.h" + +namespace aura { +template +class HOOMVerifier{ + +public: + HOOMVerifier(const GroupElement& g, + const std::vector& h_gens, + int t_n, int t_m, int m_n, int m_m); + + bool verify(const std::vector& commits, + const HOOMProof& proof) const; + + bool batch_verify(const std::vector& commits, + const std::vector& serials, + const std::vector>& proofs) const; + + +private: + GroupElement g_; + std::vector h_; + int t_n_; + int t_m_; + int m_n_; + int m_m_; +}; + +} // namespace aura + +#include "hoom_verifier.hpp" + +#endif // FIRO_AURA_HOOM_VERIFIER_H diff --git a/src/libaura/hoom_verifier.hpp b/src/libaura/hoom_verifier.hpp new file mode 100755 index 0000000000..2b31487cc4 --- /dev/null +++ b/src/libaura/hoom_verifier.hpp @@ -0,0 +1,142 @@ +#include +#include + +namespace aura { + + template + HOOMVerifier::HOOMVerifier(const GroupElement &g, + const std::vector &h_gens, + int t_n, int t_m, int m_n, int m_m) + : g_(g), h_(h_gens), t_n_(t_n), t_m_(t_m), m_n_(m_n), m_m_(m_m) { + } + + template + bool HOOMVerifier::verify(const std::vector &commits, + const HOOMProof &proof) const { + + + int t_ = pow(t_n_, t_m_); + int m_ = pow(m_n_, m_m_); + aura::SigmaPlusVerifier d_verifier(g_, h_, m_n_, m_m_); + aura::SigmaPlusVerifier D_verifier(g_, h_, t_n_, t_m_); + + //binds the commitments to the y challenge + Exponent y; + SigmaPrimitives::generate_challenge(commits, y); + + //checks one out of many sigma proof for the blinded subset + if(!d_verifier.verify(proof.d_, proof.d_Proof_, true, y)) { + LogPrintf("Hierarchic one out of many proof failed due to d_ check failed."); + return false; + } + + //generates x challenge using the y challenge calculated in the sigma + std::vector x; + x.resize(m_); + SigmaPrimitives::generate_challenge({h_[0] * y}, x[0]); + for (int k = 1; k < m_; ++k) { + SigmaPrimitives::generate_challenge({h_[0] * x[k - 1]}, x[k]); + } + + //creates digests of the subsets + std::vector D_; + D_.resize(t_); + GroupElement D; + D = SigmaPrimitives::HelperFunction(proof.d_, x); + for (int k = 0; k < t_; ++k) { + D_[k] = D + SigmaPrimitives::HelperFunction({commits.begin() + k * m_, commits.begin() + (k + 1) * m_}, x).inverse(); + } + + //the proof that the digest of our blinded subset is a member of the digests of all subsets + if(!D_verifier.verify(D_, proof.D_Proof_, true, x[m_ - 1])) { + LogPrintf("Hierarchic one out of many proof failed due to D_ check failed."); + return false; + } + return true; + } + + template + bool HOOMVerifier::batch_verify(const std::vector& commits, + const std::vector& serials, + const std::vector>& proofs) const { + + //Pre-hashing the commits for challenge generation + Exponent commits_hash; + SigmaPrimitives::generate_challenge(commits, commits_hash); + + int t_ = pow(t_n_, t_m_); + int m_ = pow(m_n_, m_m_); + int batchsize = serials.size(); + + std::vector D_; + D_.resize(t_); + GroupElement D; + + GroupElement left; + GroupElement right; + + aura::SigmaPlusVerifier d_verifier(g_, h_, m_n_, m_m_); + aura::SigmaPlusVerifier D_verifier(g_, h_, t_n_, t_m_); + + std::vector x; + x.resize(m_); + + //where we calculate the powers required for the exponentation + std::vector pows; + pows.resize(commits.size()); + + for(int i = 0; i < batchsize; ++i){ + //those values are used to store the results of SigmaPlusVerifier's calculate_batch() + GroupElement t; + std::vector f_i_; + GroupElement zero_commit; + + //checks the one out of many proof in the blinded subset + Exponent y = commits_hash; + if(!d_verifier.verify(proofs[i].d_, proofs[i].d_Proof_, true, y)){ + LogPrintf("Hierarchic one out of many proof failed due to d_ check failed."); + return false; + } + + //generates the x challenge and the subset digests + SigmaPrimitives::generate_challenge({h_[0] * y}, x[0]); + for (int k = 1; k < m_; ++k) { + SigmaPrimitives::generate_challenge({h_[0] * x[k - 1]}, x[k]); + } + D = SigmaPrimitives::HelperFunction(proofs[i].d_, x); + for (int k = 0; k < t_; ++k) { + D_[k] = D + SigmaPrimitives::HelperFunction({commits.begin() + k * m_, commits.begin() + (k + 1) * m_}, x).inverse(); + } + + //We use temporary variable to not alter the x[m_ - 1] value + Exponent xtemp = x[m_ - 1]; + if(!D_verifier.calculate_batch(D_, proofs[i].D_Proof_, t, f_i_, zero_commit, xtemp)){ + LogPrintf("Hierarchic one out of many proof failed due to one of D_ checks failed."); + return false; + } + + //uses the f_i to add up the powers of commits in the final batch + Exponent f_i_sum; + for(int k = 0; k < t_; ++k){ + for(int j = 0; j < m_; ++j) { + pows[k * m_ + j] += x[j] * f_i_[k] * serials[i]; + } + //used for the right side of the equation + f_i_sum += f_i_[k]; + } + + left += t * serials[i]; + right += zero_commit * serials[i] + D.inverse() * f_i_sum * serials[i]; + } + + secp_primitives::MultiExponent mult(commits, pows); + left += mult.get_multiple().inverse(); + + if(left != right) { + LogPrintf("Hierarchic one out of many proof failed due to final batch check failed."); + return false; + } + return true; + } + +} // namespace aura diff --git a/src/libaura/params.cpp b/src/libaura/params.cpp new file mode 100755 index 0000000000..fc0ec780f6 --- /dev/null +++ b/src/libaura/params.cpp @@ -0,0 +1,94 @@ +#include "chainparams.h" +#include "params.h" + +namespace aura { + +Params* Params::instance; +Params* Params::get_default() { + if(instance != nullptr) + return instance; + else { + //fixing generator G; + GroupElement g; + + if(!(::Params().GetConsensus().IsTestnet())) { + unsigned char buff[32] = {0}; + GroupElement base; + base.set_base_g(); + base.sha256(buff); + g.generate(buff); + } + else + g = GroupElement("9216064434961179932092223867844635691966339998754536116709681652691785432045", + "33986433546870000256104618635743654523665060392313886665479090285075695067131"); + + //fixing n and m; N = n^m = 16,384 + int n = 4; + int m = 7; + int t_n = 4; + int t_m = 7; + int m_n = 4; + int m_m = 7; + instance = new Params(g, n, m, t_m, t_n, m_m, m_n); + return instance; + } +} + +Params::Params(const GroupElement& g, int n, int m, int t_n, int t_m, int m_n, int m_m) : + g_(g), + m_(m), + n_(n), + t_m_(t_m), + t_n_(t_n), + m_m_(m_m), + m_n_(m_n) + +{ + unsigned char buff0[32] = {0}; + g.sha256(buff0); + GroupElement h0; + h0.generate(buff0); + h_.reserve(28); + h_.emplace_back(h0); + for(int i = 1; i < n*m; ++i) { + h_.push_back(GroupElement()); + unsigned char buff[32] = {0}; + h_[i - 1].sha256(buff); + h_[i].generate(buff); + } +} + +Params::~Params(){ + delete instance; +} + +const GroupElement& Params::get_g() const{ + return g_; +} +const GroupElement& Params::get_h0() const{ + return h_[0]; +} + +const std::vector& Params::get_h() const{ + return h_; +} + +uint64_t Params::get_n() const{ + return n_; +} +uint64_t Params::get_m() const{ + return m_; +} + uint64_t Params::get_t_n() const{ + return t_n_; + } + uint64_t Params::get_t_m() const{ + return t_m_; + } + uint64_t Params::get_m_n() const{ + return m_n_; + } + uint64_t Params::get_m_m() const{ + return m_m_; + } +} //namespace aura diff --git a/src/libaura/params.h b/src/libaura/params.h new file mode 100755 index 0000000000..37eca7f489 --- /dev/null +++ b/src/libaura/params.h @@ -0,0 +1,42 @@ +#ifndef FIRO_AURA_PARAMS_H +#define FIRO_AURA_PARAMS_H +#include +#include +#include + +using namespace secp_primitives; + +namespace aura { + +class Params { +public: + static Params* get_default(); + const GroupElement& get_g() const; + const GroupElement& get_h0() const; + const std::vector& get_h() const; + uint64_t get_n() const; + uint64_t get_m() const; + uint64_t get_t_n() const; + uint64_t get_t_m() const; + uint64_t get_m_n() const; + uint64_t get_m_m() const; + +private: + Params(const GroupElement& g, int n, int m, int t_n, int t_m, int m_n, int m_m); + ~Params(); + +private: + static Params* instance; + GroupElement g_; + std::vector h_; + int m_; + int n_; + int t_m_; + int t_n_; + int m_m_; + int m_n_; +}; + +}//namespace aura + +#endif //FIRO_AURA_PARAMS_H diff --git a/src/libaura/r1_proof.h b/src/libaura/r1_proof.h new file mode 100755 index 0000000000..642da4fad7 --- /dev/null +++ b/src/libaura/r1_proof.h @@ -0,0 +1,78 @@ +#ifndef FIRO_AURA_SIGMA_R1_PROOF_H +#define FIRO_AURA_SIGMA_R1_PROOF_H + +#include +#include +#include +#include "../../serialize.h" + +namespace aura { + +template +class R1Proof { +public: + GroupElement A_; + GroupElement C_; + GroupElement D_; + std::vector f_; + Exponent ZA_; + Exponent ZC_; + +public: + R1Proof() = default; + +public: + bool operator==(R1Proof const &other) const { + return A_ == other.A_ && + C_ == other.C_ && + D_ == other.D_ && + f_ == other.f_ && + ZA_ == other.ZA_ && + ZC_ == other.ZC_; + } + + bool operator!=(R1Proof const &other) const { + return !(*this == other); + } + +public: + inline int memoryRequired(int n, int m) const { + return A_.memoryRequired() * 3 + ZA_.memoryRequired() * (m*(n - 1) + 2); + } + + inline unsigned char* serialize(unsigned char* buffer) const { + unsigned char* current = A_.serialize(buffer); + current = C_.serialize(current); + current = D_.serialize(current); + for (std::size_t i = 0; i < f_.size(); ++i) + current = f_[i].serialize(current); + current = ZA_.serialize(current); + return ZC_.serialize(current); + } + inline unsigned const char* deserialize(unsigned const char* buffer, int n, int m) { + unsigned const char* current = A_.deserialize(buffer); + current = C_.deserialize(current); + current = D_.deserialize(current); + int f_size = m * (n - 1); + f_.resize(f_size); + for(int i = 0; i < f_size; ++i) + current = f_[i].deserialize(current); + current = ZA_.deserialize(current); + return ZC_.deserialize(current); + } + + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(A_); + READWRITE(C_); + READWRITE(D_); + READWRITE(f_); + READWRITE(ZA_); + READWRITE(ZC_); + } +}; + +} // namespace aura + +#endif // FIRO_AURA_SIGMA_R1_PROOF_H diff --git a/src/libaura/r1_proof_generator.h b/src/libaura/r1_proof_generator.h new file mode 100755 index 0000000000..9c2aaac896 --- /dev/null +++ b/src/libaura/r1_proof_generator.h @@ -0,0 +1,76 @@ +#ifndef FIRO_AURA_SIGMA_R1_PROOF_GENERATOR_H +#define FIRO_AURA_SIGMA_R1_PROOF_GENERATOR_H + +#include "r1_proof.h" +#include "aura_primitives.h" + +namespace aura { + +template +class R1ProofGenerator{ + +public: + R1ProofGenerator(const GroupElement& g, + const std::vector& h_gens, + const std::vector& b, + const Exponent& r, + int n, + int m); + + // Returns commitment B. + const GroupElement& get_B() const; + + /** \brief Generates r1 proof by randomly selecting the values of vector a. + * \param[out] proof_out - R1 proof generated. + * \param[in] skip_final_response If set to true, will only generate the initial message of the proof. + */ + void proof(R1Proof& proof_out, bool skip_final_response = false); + + /** \brief Generates R1 proof, which proves that the given matrix b of size n*m contains bits, and for each row exactly 1 bit is set to 1. + * \param[out] a_out - List of randomly generated scalars. These values are used in the rest of sigma proof. + * \param[out] proof_out - R1 proof generated. + * \param[in] skip_final_response If set to true, will only generate the initial message of the proof. + */ + void proof(std::vector& a_out, + R1Proof& proof_out, + bool skip_final_response = false); + + /** \brief Finishes generation of R1 proof, the part after receiving the challenge x. + * \param[in] a - List of randomly generated scalars. These values are used in the rest of sigma proof. + * \param[in] challenge_x Value of challenge X. + * \param[out] proof_out - R1 proof generated. + */ + void generate_final_response(const std::vector& a, + const Exponent& challenge_x, + R1Proof& proof_out); +private: + + Exponent rA_; + Exponent rC_; + Exponent rD_; + + // Generators for the commitment. Size of h_ must be n*m. + const GroupElement& g_; + const std::vector& h_; + + // n*m values of a matrix describing index l of the coin being spent. + // Each value in this vector is a bit, I.E. 0 or 1. + std::vector b_; + + // Randomness of commitment B_Commit. + Exponent r; + + // Main commitment B for the [nxm] matrix of b_. + GroupElement B_Commit; + + // Size of the matrix for commitment. Number of coins N < n^m. + int n_; + int m_; + +}; + +} // namespace aura + +#include "r1_proof_generator.hpp" + +#endif // FIRO_AURA_SIGMA_R1_PROOF_GENERATOR_H diff --git a/src/libaura/r1_proof_generator.hpp b/src/libaura/r1_proof_generator.hpp new file mode 100755 index 0000000000..157143423c --- /dev/null +++ b/src/libaura/r1_proof_generator.hpp @@ -0,0 +1,110 @@ +namespace aura { + +template +R1ProofGenerator::R1ProofGenerator( + const GroupElement& g, + const std::vector& h_gens, + const std::vector& b, + const Exponent& r, + int n , + int m) + : g_(g) + , h_(h_gens) + , b_(b) + , r(r) + , n_(n) + , m_(m) +{ + SigmaPrimitives::commit(g_, h_, b_, r, B_Commit); +} + +template +const GroupElement& R1ProofGenerator::get_B() const { + return B_Commit; +} + +template +void R1ProofGenerator::proof( + R1Proof& proof_out, bool skip_final_response) { + std::vector a; + proof(a, proof_out, skip_final_response); +} + +template +void R1ProofGenerator::proof( + std::vector& a_out, + R1Proof& proof_out, + bool skip_final_response) { + rC_.randomize(); + rD_.randomize(); + a_out.resize(n_ * m_); + for(int j = 0; j < m_; ++j) { + for(int i = 1; i < n_; ++i) { + a_out[j * n_ + i].randomize(); + a_out[j * n_] -= a_out[j * n_ + i]; + } + } + + // proof_out.B_ = B_Commit; + + //compute A + GroupElement A; + while(!A.isMember() || A.isInfinity()) { + rA_.randomize(); + SigmaPrimitives::commit(g_, h_, a_out, rA_, A); + } + proof_out.A_ = A; + + //compute C + std::vector c; + c.resize(n_ * m_); + for(int i = 0; i < n_ * m_; ++i) { + c[i] = (a_out[i] * (Exponent(uint64_t(1)) - (Exponent(uint64_t(2)) * b_[i]))); + } + GroupElement C; + while(!C.isMember() || C.isInfinity()) { + rC_.randomize(); + SigmaPrimitives::commit(g_, h_, c, rC_, C); + } + proof_out.C_ = C; + + //compute D + std::vector d; + d.resize(n_ * m_); + for(int i = 0; i < n_ * m_; i++) { + d[i] = ((a_out[i].square()).negate()); + } + GroupElement D; + while(!D.isMember() || D.isInfinity()) { + rD_.randomize(); + SigmaPrimitives::commit(g_, h_, d, rD_, D); + } + proof_out.D_ = D; + + if (!skip_final_response) { + Exponent x; + std::vector group_elements = {A, B_Commit, C, D}; + SigmaPrimitives::generate_challenge(group_elements, x); + generate_final_response(a_out, x, proof_out); + } +} + +template +void R1ProofGenerator::generate_final_response( + const std::vector& a, + const Exponent& challenge_x, + R1Proof& proof_out) { + //f + proof_out.f_.clear(); + proof_out.f_.reserve(m_ * (n_ - 1)); + for(int j = 0; j < m_; j++) { + for(int i = 1; i < n_; i++) + proof_out.f_.emplace_back(b_[(j * n_) + i] * challenge_x + a[(j * n_) + i]); + } + + //zA + proof_out.ZA_ = r * challenge_x + rA_; + proof_out.ZC_ = rC_ * challenge_x + rD_; +} + +} //namespace aura diff --git a/src/libaura/r1_proof_verifier.h b/src/libaura/r1_proof_verifier.h new file mode 100755 index 0000000000..071709319d --- /dev/null +++ b/src/libaura/r1_proof_verifier.h @@ -0,0 +1,38 @@ +#ifndef FIRO_AURA_SIGMA_R1_PROOF_VERIFIER_H +#define FIRO_AURA_SIGMA_R1_PROOF_VERIFIER_H + +namespace aura { + +template +class R1ProofVerifier { + +public: + R1ProofVerifier(const GroupElement& g, + const std::vector& h_gens, + const GroupElement& B, int n , int m); + + bool verify(const R1Proof& proof, + bool skip_final_response_verification = false) const; + + bool verify(const R1Proof& proof, + std::vector& f_out, + bool skip_final_response_verification = false) const; + + bool verify_final_response( + const R1Proof& proof, + const Exponent& challenge_x, + std::vector& f_out) const; + +private: + const GroupElement& g_; + const std::vector& h_; + GroupElement B_Commit; + int n_; + int m_; +}; + +} // namespace aura + +#include "r1_proof_verifier.hpp" + +#endif // FIRO_AURA_SIGMA_R1_PROOF_VERIFIER_H diff --git a/src/libaura/r1_proof_verifier.hpp b/src/libaura/r1_proof_verifier.hpp new file mode 100755 index 0000000000..d59e871fe3 --- /dev/null +++ b/src/libaura/r1_proof_verifier.hpp @@ -0,0 +1,105 @@ +namespace aura { + +template +R1ProofVerifier::R1ProofVerifier( + const GroupElement& g, + const std::vector& h_gens, + const GroupElement& B, + int n , + int m) + : g_(g) + , h_(h_gens) + , B_Commit(B) + , n_(n) + , m_(m){ +} + +template +bool R1ProofVerifier::verify( + const R1Proof& proof, + bool skip_final_response_verification) const { + std::vector f; + return verify(proof, f, skip_final_response_verification); +} + +template +bool R1ProofVerifier::verify( + const R1Proof& proof, + std::vector& f_out, + bool skip_final_response_verification) const{ + + if(!(proof.A_.isMember() && + B_Commit.isMember() && + proof.C_.isMember() && + proof.D_.isMember()) || + (proof.A_.isInfinity() || + B_Commit.isInfinity() || + proof.C_.isInfinity() || + proof.D_.isInfinity())) + return false; + const std::vector& f = proof.f_; + for (std::size_t i = 0; i < f.size(); i++) { + if(!f[i].isMember() || f[i].isZero()) + return false; + } + + if(!(proof.ZA_.isMember() && + proof.ZC_.isMember()) || + (proof.ZA_.isZero() || + proof.ZC_.isZero())) + return false; + + if (!skip_final_response_verification) { + Exponent x; + std::vector group_elements = {proof.A_, B_Commit, proof.C_, proof.D_}; + SigmaPrimitives::generate_challenge(group_elements, x); + return verify_final_response(proof, x, f_out); + } + return true; +} + +template +bool R1ProofVerifier::verify_final_response( + const R1Proof& proof, + const Exponent& challenge_x, + std::vector& f_out) const { + const std::vector& f = proof.f_; + + for(unsigned int j = 0; j < f.size(); ++j) { + if(f[j] == challenge_x) + return false; + } + + f_out.clear(); + f_out.reserve(n_ * m_); + for(int j = 0; j < m_; ++j) { + f_out.push_back(Exponent(uint64_t(0))); + Exponent temp; + int k = n_ - 1; + for(int i = 0; i < k; ++i) { + temp += f[j * k + i]; + f_out.emplace_back(f[j * k + i]); + } + f_out[j * n_] = challenge_x - temp; + } + + GroupElement one; + SigmaPrimitives::commit(g_, h_, f_out, proof.ZA_, one); + if((B_Commit * challenge_x + proof.A_) != one) + return false; + + std::vector f_outprime; + f_outprime.reserve(f_out.size()); + for (std::size_t i = 0; i < f_out.size(); i++) { + f_outprime.emplace_back(f_out[i] * (challenge_x - f_out[i])); + } + + GroupElement two; + SigmaPrimitives::commit(g_, h_, f_outprime, proof.ZC_, two); + if ((proof.C_ * challenge_x + proof.D_) != two) + return false; + + return true; +} + +} // namespace aura diff --git a/src/libaura/sigmaplus_proof.h b/src/libaura/sigmaplus_proof.h new file mode 100755 index 0000000000..f88220ac8d --- /dev/null +++ b/src/libaura/sigmaplus_proof.h @@ -0,0 +1,73 @@ +#ifndef FIRO_AURA_SIGMA_SIGMAPLUS_PROOF_H +#define FIRO_AURA_SIGMA_SIGMAPLUS_PROOF_H + +#include "params.h" +#include "r1_proof.h" + +namespace aura { + +template +class SigmaPlusProof { +public: + int n; + int m; + GroupElement B_; + R1Proof r1Proof_; + std::vector Gk_; + Exponent z_; + +public: + SigmaPlusProof(int n, int m): n(n), m(m) {}; + +public: + bool operator==(const SigmaPlusProof& other) const { + return n == other.n && + m == other.m && + B_ == other.B_ && + r1Proof_ == other.r1Proof_ && + Gk_ == other.Gk_ && + z_ == other.z_; + } + + bool operator!=(const SigmaPlusProof& other) const { + return !(*this == other); + } + +public: + inline int memoryRequired() const { + return B_.memoryRequired() + + r1Proof_.memoryRequired(n, m) + + B_.memoryRequired() * m + + z_.memoryRequired(); + } + + inline unsigned char* serialize(unsigned char* buffer) const { + unsigned char* current = B_.serialize(buffer); + current = r1Proof_.serialize(current); + for (std::size_t i = 0; i < Gk_.size(); ++i) + current = Gk_[i].serialize(current); + return z_.serialize(current); + } + + inline unsigned const char* deserialize(unsigned const char* buffer) { + unsigned const char* current = B_.deserialize(buffer); + current = r1Proof_.deserialize(current, n, m); + Gk_.resize(m); + for(int i = 0; i < m; ++i) + current = Gk_[i].deserialize(current); + return z_.deserialize(current); + } + + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(B_); + READWRITE(r1Proof_); + READWRITE(Gk_); + READWRITE(z_); + } +}; + +} //namespace aura + +#endif // FIRO_AURA_SIGMA_SIGMAPLUS_PROOF_H diff --git a/src/libaura/sigmaplus_prover.h b/src/libaura/sigmaplus_prover.h new file mode 100755 index 0000000000..54516263f6 --- /dev/null +++ b/src/libaura/sigmaplus_prover.h @@ -0,0 +1,35 @@ +#ifndef FIRO_AURA_SIGMA_SIGMAPLUS_PROVER_H +#define FIRO_AURA_SIGMA_SIGMAPLUS_PROVER_H + +#include "r1_proof_generator.h" +#include "sigmaplus_proof.h" + +#include + +namespace aura { + +template +class SigmaPlusProver{ + +public: + SigmaPlusProver(const GroupElement& g, + const std::vector& h_gens, int n, int m); + void proof(const std::vector& commits, + std::size_t l, + const Exponent& r, + bool fPadding, + Exponent challenge, + SigmaPlusProof& proof_out); + +private: + GroupElement g_; + std::vector h_; + int n_; + int m_; +}; + +} // namespace aura + +#include "sigmaplus_prover.hpp" + +#endif // FIRO_AURA_SIGMA_SIGMAPLUS_PROVER_H diff --git a/src/libaura/sigmaplus_prover.hpp b/src/libaura/sigmaplus_prover.hpp new file mode 100755 index 0000000000..022a60f8f1 --- /dev/null +++ b/src/libaura/sigmaplus_prover.hpp @@ -0,0 +1,150 @@ +#include +namespace aura { + +template +SigmaPlusProver::SigmaPlusProver( + const GroupElement& g, + const std::vector& h_gens, + int n, + int m) + : g_(g) + , h_(h_gens) + , n_(n) + , m_(m) { +} + +template +void SigmaPlusProver::proof( + const std::vector& commits, + std::size_t l, + const Exponent& r, + bool fPadding, + Exponent challenge, + SigmaPlusProof& proof_out) { + std::size_t setSize = commits.size(); + assert(setSize > 0); + + Exponent rB; + rB.randomize(); + + // Create table sigma of nxm bits. + std::vector sigma; + SigmaPrimitives::convert_to_sigma(l, n_, m_, sigma); + + // Values of Ro_k from Figure 5. + std::vector Pk; + Pk.resize(m_); + for (int k = 0; k < m_; ++k) { + Pk[k].randomize(); + } + R1ProofGenerator r1prover(g_, h_, sigma, rB, n_, m_); + proof_out.B_ = r1prover.get_B(); + std::vector a; + r1prover.proof(a, proof_out.r1Proof_, true /*Skip generation of final response*/); + + // Compute coefficients of Polynomials P_I(x), for all I from [0..N]. + std::size_t N = setSize; + std::vector > P_i_k; + P_i_k.resize(N); + + // last polynomial is special case if fPadding is true + for (std::size_t i = 0; i < (fPadding ? N-1 : N); ++i) { + std::vector& coefficients = P_i_k[i]; + std::vector I = SigmaPrimitives::convert_to_nal(i, n_, m_); + coefficients.push_back(a[I[0]]); + coefficients.push_back(sigma[I[0]]); + for (int j = 1; j < m_; ++j) { + SigmaPrimitives::new_factor(sigma[j * n_ + I[j]], a[j * n_ + I[j]], coefficients); + } + } + + if (fPadding) { + /* + * To optimize calculation of sum of all polynomials indices 's' = setSize-1 through 'n^m-1' we use the + * fact that sum of all of elements in each row of 'a' array is zero. Computation is done by going + * through n-ary representation of 's' and increasing "digit" at each position to 'n-1' one by one. + * During every step digits at higher positions are fixed and digits at lower positions go through all + * possible combinations with a total corresponding polynomial sum of 'x^j'. + * + * The math behind optimization (TeX notation): + * + * \sum_{i=s+1}^{N-1}p_i(x) = + * \sum_{j=0}^{m-1} + * \left[ + * \left( \sum_{i=s_j+1}^{n-1}(\delta_{l_j,i}x+a_{j,i}) \right) + * \left( \prod_{k=j}^{m-1}(\delta_{l_k,s_k}x+a_{k,s_k}) \right) + * x^j + * \right] + */ + + std::vector I = SigmaPrimitives::convert_to_nal(N-1, n_, m_); + std::vector lj = SigmaPrimitives::convert_to_nal(l, n_, m_); + + std::vector p_i_sum; + p_i_sum.emplace_back(uint64_t(1)); + std::vector> partial_p_s; + + // Pre-calculate product parts and calculate p_s(x) at the same time, put the latter into p_i_sum + for (int j = m_ - 1; j >= 0; j--) { + partial_p_s.push_back(p_i_sum); + SigmaPrimitives::new_factor(sigma[j * n_ + I[j]], a[j * n_ + I[j]], p_i_sum); + } + + for (int j = 0; j < m_; j++) { + // \sum_{i=s_j+1}^{n-1}(\delta_{l_j,i}x+a_{j,i}) + Exponent a_sum(uint64_t(0)); + for (int i = I[j] + 1; i < n_; i++) + a_sum += a[j * n_ + i]; + Exponent x_sum(uint64_t(lj[j] >= I[j]+1 ? 1 : 0)); + + // Multiply by \prod_{k=j}^{m-1}(\delta_{l_k,s_k}x+a_{k,s_k}) + std::vector &polynomial = partial_p_s[m_ - j - 1]; + SigmaPrimitives::new_factor(x_sum, a_sum, polynomial); + + // Multiply by x^j and add to the result + for (int k = 0; k < m_ - j; k++) + p_i_sum[j + k] += polynomial[k]; + } + + P_i_k[N-1] = p_i_sum; + } + + //computing G_k`s; + std::vector Gk; + Gk.reserve(m_); + for (int k = 0; k < m_; ++k) { + std::vector P_i; + P_i.reserve(N); + for (size_t i = 0; i < N; ++i) { + P_i.emplace_back(P_i_k[i][k]); + } + secp_primitives::MultiExponent mult(commits, P_i); + GroupElement c_k = mult.get_multiple(); + c_k += SigmaPrimitives::commit(g_, Exponent(uint64_t(0)), h_[0], Pk[k]); + Gk.emplace_back(c_k); + } + proof_out.Gk_ = Gk; + + // Compute value of challenge X, then continue R1 proof and sigma final response proof. + std::vector group_elements = {h_[0] * challenge, + proof_out.r1Proof_.A_, proof_out.B_, proof_out.r1Proof_.C_, proof_out.r1Proof_.D_}; + + group_elements.insert(group_elements.end(), Gk.begin(), Gk.end()); + Exponent x; + SigmaPrimitives::generate_challenge(group_elements, x); + r1prover.generate_final_response(a, x, proof_out.r1Proof_); + challenge = x; + //computing z + Exponent z; + z = r * x.exponent(uint64_t(m_)); + Exponent sum; + Exponent x_k(uint64_t(1)); + for (int k = 0; k < m_; ++k) { + sum += (Pk[k] * x_k); + x_k *= x; + } + z -= sum; + proof_out.z_ = z; +} + +} // namespace aura diff --git a/src/libaura/sigmaplus_verifier.h b/src/libaura/sigmaplus_verifier.h new file mode 100755 index 0000000000..c140aed139 --- /dev/null +++ b/src/libaura/sigmaplus_verifier.h @@ -0,0 +1,63 @@ +#ifndef FIRO_AURA_SIGMA_SIGMAPLUS_VERIFIER_H +#define FIRO_AURA_SIGMA_SIGMAPLUS_VERIFIER_H + +#include "r1_proof_verifier.h" +#include "util.h" + +namespace aura { +template +class SigmaPlusVerifier{ + +public: + SigmaPlusVerifier(const GroupElement& g, + const std::vector& h_gens, + int n, int m_); + + bool verify(const std::vector& commits, + const SigmaPlusProof& proof, + bool fPadding, + Exponent challenge) const; + + bool batch_verify(const std::vector& commits, + const std::vector& serials, + const std::vector& fPadding, + const std::vector& setSizes, + const std::vector>& proofs) const; + + + bool calculate_batch(const std::vector& commits, + const SigmaPlusProof& proof, + GroupElement& t, + std::vector& f_i_, + GroupElement& zero_commit, + Exponent challenge) const; + + bool membership_checks(const SigmaPlusProof& proof) const; + bool compute_fs(const SigmaPlusProof& proof, const Exponent& x, std::vector& f_) const; + bool abcd_checks(const SigmaPlusProof& proof, const Exponent& x, const std::vector& f_) const; + + void compute_fis(int j, const std::vector& f, std::vector& f_i_) const; + void compute_fis(const Exponent& f_i, int j, const std::vector& f, typename std::vector::iterator& ptr, typename std::vector::iterator end_ptr) const; + void compute_batch_fis( + const Exponent& f_i, + int j, + const std::vector& f, + const Exponent& y, + Exponent& e, + typename std::vector::iterator& ptr, + typename std::vector::iterator start_ptr, + typename std::vector::iterator end_ptr) const; + + +private: + GroupElement g_; + std::vector h_; + int n; + int m; +}; + +} // namespace aura + +#include "sigmaplus_verifier.hpp" + +#endif // FIRO_AURA_SIGMA_SIGMAPLUS_VERIFIER_H diff --git a/src/libaura/sigmaplus_verifier.hpp b/src/libaura/sigmaplus_verifier.hpp new file mode 100755 index 0000000000..b2decf981c --- /dev/null +++ b/src/libaura/sigmaplus_verifier.hpp @@ -0,0 +1,483 @@ + +#include +namespace aura{ + +template +SigmaPlusVerifier::SigmaPlusVerifier( + const GroupElement& g, + const std::vector& h_gens, + int n, + int m) + : g_(g) + , h_(h_gens) + , n(n) + , m(m){ +} + +template +bool SigmaPlusVerifier::calculate_batch( + const std::vector& commits, + const SigmaPlusProof& proof, + GroupElement& t, + std::vector& f_i_, + GroupElement& zero_commit, + Exponent challenge) const { + + R1ProofVerifier r1ProofVerifier(g_, h_, proof.B_, n, m); + std::vector f; + const R1Proof& r1Proof = proof.r1Proof_; + if (!r1ProofVerifier.verify(r1Proof, f, true /* Skip verification of final response */)) { + LogPrintf("Sigma spend failed due to r1 proof incorrect."); + return false; + } + if (!proof.B_.isMember() || proof.B_.isInfinity()) { + LogPrintf("Sigma spend failed due to value of B outside of group."); + return false; + } + + const std::vector & Gk = proof.Gk_; + for (int k = 0; k < m; ++k) { + if (!Gk[k].isMember() || Gk[k].isInfinity()) { + LogPrintf("Sigma spend failed due to value of GK[i] outside of group."); + return false; + } + } + + // Compute value of challenge X, then continue R1 proof and sigma final response proof. + std::vector group_elements = {h_[0] * challenge, + r1Proof.A_, proof.B_, r1Proof.C_, r1Proof.D_}; + + group_elements.insert(group_elements.end(), Gk.begin(), Gk.end()); + Exponent challenge_x; + SigmaPrimitives::generate_challenge(group_elements, challenge_x); + challenge = challenge_x; + + // Now verify the final response of r1 proof. Values of "f" are finalized only after this call. + if (!r1ProofVerifier.verify_final_response(r1Proof, challenge_x, f)) { + LogPrintf("Sigma spend failed due to incorrect final response."); + return false; + } + + if(!proof.z_.isMember() || proof.z_.isZero()) { + LogPrintf("Sigma spend failed due to value of Z outside of group."); + return false; + } + + if (commits.empty()) { + LogPrintf("No mints in the anonymity set"); + return false; + } + + std::size_t N = commits.size(); + f_i_.resize(N); + + compute_fis(m, f, f_i_); + + Exponent x_k(uint64_t(1)); + for(int k = 0; k < m; ++k){ + t += (Gk[k] * (x_k.negate())); + x_k *= challenge_x; + } + + zero_commit = SigmaPrimitives::commit(g_, Exponent(uint64_t(0)), h_[0], proof.z_); + return true; +} + +template +bool SigmaPlusVerifier::verify( + const std::vector& commits, + const SigmaPlusProof& proof, + bool fPadding, + Exponent challenge) const { + + R1ProofVerifier r1ProofVerifier(g_, h_, proof.B_, n, m); + std::vector f; + const R1Proof& r1Proof = proof.r1Proof_; + if (!r1ProofVerifier.verify(r1Proof, f, true /* Skip verification of final response */)) { + LogPrintf("Sigma spend failed due to r1 proof incorrect."); + return false; + } + + if (!proof.B_.isMember() || proof.B_.isInfinity()) { + LogPrintf("Sigma spend failed due to value of B outside of group."); + return false; + } + + const std::vector & Gk = proof.Gk_; + for (int k = 0; k < m; ++k) { + if (!Gk[k].isMember() || Gk[k].isInfinity()) { + LogPrintf("Sigma spend failed due to value of GK[i] outside of group."); + return false; + } + } + + // Compute value of challenge X, then continue R1 proof and sigma final response proof. + std::vector group_elements = {h_[0] * challenge, + r1Proof.A_, proof.B_, r1Proof.C_, r1Proof.D_}; + + group_elements.insert(group_elements.end(), Gk.begin(), Gk.end()); + Exponent challenge_x; + SigmaPrimitives::generate_challenge(group_elements, challenge_x); + challenge = challenge_x; + + // Now verify the final response of r1 proof. Values of "f" are finalized only after this call. + if (!r1ProofVerifier.verify_final_response(r1Proof, challenge_x, f)) { + LogPrintf("Sigma spend failed due to incorrect final response."); + return false; + } + + if(!proof.z_.isMember() || proof.z_.isZero()) { + LogPrintf("Sigma spend failed due to value of Z outside of group."); + return false; + } + + if (commits.empty()) { + LogPrintf("No mints in the anonymity set"); + return false; + } + + std::size_t N = commits.size(); + std::vector f_i_; + f_i_.resize(N); + + compute_fis(m, f, f_i_); + + if (fPadding) { + /* + * Optimization for getting power for last 'commits' array element is done similarly to the one used in creating + * a proof. The fact that sum of any row in 'f' array is 'x' (challenge value) is used. + * + * Math (in TeX notation): + * + * \sum_{i=s+1}^{N-1} \prod_{j=0}^{m-1}f_{j,i_j} = + * \sum_{j=0}^{m-1} + * \left[ + * \left( \sum_{i=s_j+1}^{n-1}f_{j,i} \right) + * \left( \prod_{k=j}^{m-1}f_{k,s_k} \right) + * x^j + * \right] + */ + + Exponent pow(uint64_t(1)); + std::vector I = SigmaPrimitives::convert_to_nal(N - 1, n, m); + std::vector f_part_product; // partial product of f array elements for lastIndex + for (int j = m - 1; j >= 0; j--) { + f_part_product.push_back(pow); + pow *= f[j * n + I[j]]; + } + + Exponent xj(uint64_t(1));; // x^j + for (int j = 0; j < m; j++) { + Exponent fi_sum(uint64_t(0)); + for (int i = I[j] + 1; i < n; i++) + fi_sum += f[j*n + i]; + pow += fi_sum * xj * f_part_product[m - j - 1]; + xj *= challenge_x; + } + f_i_[N - 1] = pow; + } + + secp_primitives::MultiExponent mult(commits, f_i_); + GroupElement t1 = mult.get_multiple(); + + GroupElement t2; + Exponent x_k(uint64_t(1)); + for(int k = 0; k < m; ++k){ + t2 += (Gk[k] * (x_k.negate())); + x_k *= challenge_x; + } + GroupElement left(t1 + t2); + if (left != SigmaPrimitives::commit(g_, Exponent(uint64_t(0)), h_[0], proof.z_)) { + LogPrintf("Sigma spend failed due to final proof verification failure."); + return false; + } + return true; +} + +template +bool SigmaPlusVerifier::batch_verify( + const std::vector& commits, + const std::vector& serials, + const std::vector& fPadding, + const std::vector& setSizes, + const std::vector>& proofs) const { + + int M = proofs.size(); + int N = commits.size(); + + if (commits.empty()) + return false; + + for(int t = 0; t < M; ++t) { + if (!membership_checks(proofs[t])) { + LogPrintf("Sigma spend failed due to membership check failed."); + return false; + } + } + std::vector challenges; + challenges.resize(M); + + std::vector> f_; + f_.resize(M); + for (int t = 0; t < M; ++t) + { + std::vector group_elements = { + proofs[t].r1Proof_.A_, proofs[t].B_, proofs[t].r1Proof_.C_, proofs[t].r1Proof_.D_}; + + group_elements.insert(group_elements.end(), proofs[t].Gk_.begin(), proofs[t].Gk_.end()); + SigmaPrimitives::generate_challenge(group_elements, challenges[t]); + + if(!compute_fs(proofs[t], challenges[t], f_[t]) || !abcd_checks(proofs[t], challenges[t], f_[t])) { + LogPrintf("Sigma spend failed due to compute_fs or abcd_checks failed."); + return false; + } + } + + std::vector y; + y.resize(M); + for (int t = 0; t < M; ++t) + y[t].randomize(); + + std::vector f_i_t; + f_i_t.resize(N); + GroupElement right; + Scalar exp; + + std::vector > I_; + I_.resize(N); + for (int i = 0; i < N ; ++i) + I_[i] = SigmaPrimitives::convert_to_nal(i, n, m); + + for (int t = 0; t < M; ++t) + { + right += (SigmaPrimitives::commit(g_, Scalar(uint64_t(0)), h_[0], proofs[t].z_)) * y[t]; + Scalar e; + size_t size = setSizes[t]; + size_t start = N - size; + + Scalar f_i(uint64_t(1)); + std::vector::iterator ptr = f_i_t.begin() + start; + compute_batch_fis(f_i, m, f_[t], y[t], e, ptr, ptr, ptr + size - 1); + + if(fPadding[t]) { + /* + * Optimization for getting power for last 'commits' array element is done similarly to the one used in creating + * a proof. The fact that sum of any row in 'f' array is 'x' (challenge value) is used. + * + * Math (in TeX notation): + * + * \sum_{i=s+1}^{N-1} \prod_{j=0}^{m-1}f_{j,i_j} = + * \sum_{j=0}^{m-1} + * \left[ + * \left( \sum_{i=s_j+1}^{n-1}f_{j,i} \right) + * \left( \prod_{k=j}^{m-1}f_{k,s_k} \right) + * x^j + * \right] + */ + + Scalar pow(uint64_t(1)); + std::vector f_part_product; // partial product of f array elements for lastIndex + for (int j = m - 1; j >= 0; j--) { + f_part_product.push_back(pow); + pow *= f_[t][j * n + I_[size - 1][j]]; + } + + NthPower xj(challenges[t]); + for (std::size_t j = 0; j < m; j++) { + Scalar fi_sum(uint64_t(0)); + for (std::size_t i = I_[size - 1][j] + 1; i < n; i++) + fi_sum += f_[t][j * n + i]; + pow += fi_sum * xj.pow * f_part_product[m - j - 1]; + xj.go_next(); + } + + f_i_t[N - 1] += pow * y[t]; + e += pow; + } else { + f_i = (uint64_t(1)); + for (std::size_t j = 0; j < m; ++j) + { + f_i *= f_[t][j*n + I_[size - 1][j]]; + } + + f_i_t[N - 1] += f_i * y[t]; + e += f_i; + } + + e *= serials[t] * y[t]; + exp += e; + } + + secp_primitives::MultiExponent mult(commits, f_i_t); + GroupElement t1 = mult.get_multiple(); + + std::vector> x_t_k_neg; + x_t_k_neg.resize(M); + for (int t = 0; t < M; ++t) { + x_t_k_neg[t].reserve(m); + NthPower x_k(challenges[t]); + for (uint64_t k = 0; k < m; ++k) { + x_t_k_neg[t].emplace_back(x_k.pow.negate()); + x_k.go_next(); + } + } + + GroupElement t2; + for (int t = 0; t < M; ++t) { + const std::vector & Gk = proofs[t].Gk_; + GroupElement term; + for (std::size_t k = 0; k < m; ++k) + { + term += ((Gk[k]) * x_t_k_neg[t][k]); + } + term *= y[t]; + t2 += term; + } + GroupElement left(t1 + t2); + + right += g_ * exp; + if(left != right) + return false; + + return true; +} + +template +bool SigmaPlusVerifier::membership_checks(const SigmaPlusProof& proof) const { + if(!(proof.r1Proof_.A_.isMember() && + proof.B_.isMember() && + proof.r1Proof_.C_.isMember() && + proof.r1Proof_.D_.isMember()) || + (proof.r1Proof_.A_.isInfinity() || + proof.B_.isInfinity() || + proof.r1Proof_.C_.isInfinity() || + proof.r1Proof_.D_.isInfinity())) + return false; + + for (std::size_t i = 0; i < proof.r1Proof_.f_.size(); i++) + { + if (!proof.r1Proof_.f_[i].isMember() || proof.r1Proof_.f_[i].isZero()) + return false; + } + const std::vector & Gk = proof.Gk_; + for (std::size_t k = 0; k < m; ++k) + { + if (!Gk[k].isMember() || Gk[k].isInfinity()) + return false; + } + if(!(proof.r1Proof_.ZA_.isMember() && + proof.r1Proof_.ZC_.isMember() && + proof.z_.isMember()) || + (proof.r1Proof_.ZA_.isZero() || + proof.r1Proof_.ZC_.isZero() || + proof.z_.isZero())) + return false; + return true; +} + +template +bool SigmaPlusVerifier::compute_fs( + const SigmaPlusProof& proof, + const Exponent& x, + std::vector& f_) const { + for(unsigned int j = 0; j < proof.r1Proof_.f_.size(); ++j) { + if(proof.r1Proof_.f_[j] == x) + return false; + } + + f_.reserve(n * m); + for (std::size_t j = 0; j < m; ++j) + { + f_.push_back(Scalar(uint64_t(0))); + Scalar temp; + int k = n - 1; + for (int i = 0; i < k; ++i) + { + temp += proof.r1Proof_.f_[j * k + i]; + f_.emplace_back(proof.r1Proof_.f_[j * k + i]); + } + f_[j * n] = x - temp; + } + return true; +} + +template +bool SigmaPlusVerifier::abcd_checks( + const SigmaPlusProof& proof, + const Exponent& x, + const std::vector& f_) const { + Exponent c; + c.randomize(); + + // Aggregating two checks into one, B^x * A = Comm(..) and C^x * D = Comm(..) + std::vector f_plus_f_prime; + f_plus_f_prime.reserve(f_.size()); + for(std::size_t i = 0; i < f_.size(); i++) + f_plus_f_prime.emplace_back(f_[i] * c + f_[i] * (x - f_[i])); + + GroupElement right; + SigmaPrimitives::commit(g_, h_, f_plus_f_prime, proof.r1Proof_.ZA_ * c + proof.r1Proof_.ZC_, right); + if(((proof.B_ * x + proof.r1Proof_.A_) * c + proof.r1Proof_.C_ * x + proof.r1Proof_.D_) != right) + return false; + return true; +} + +template +void SigmaPlusVerifier::compute_fis(int j, const std::vector& f, std::vector& f_i_) const { + Exponent f_i(uint64_t(1)); + typename std::vector::iterator ptr = f_i_.begin(); + compute_fis(f_i, m, f, ptr, f_i_.end()); +} + +template +void SigmaPlusVerifier::compute_fis(const Exponent& f_i, int j, const std::vector& f, typename std::vector::iterator& ptr, typename std::vector::iterator end_ptr) const { + j--; + if (j == -1) + { + if(ptr < end_ptr) + *ptr++ += f_i; + return; + } + + Scalar t; + + for (int i = 0; i < n; i++) + { + t = f[j * n + i]; + t *= f_i; + + compute_fis(t, j, f, ptr, end_ptr); + } +} + +template +void SigmaPlusVerifier::compute_batch_fis( + const Exponent& f_i, + int j, + const std::vector& f, + const Exponent& y, + Exponent& e, + typename std::vector::iterator& ptr, + typename std::vector::iterator start_ptr, + typename std::vector::iterator end_ptr)const { + j--; + if (j == -1) + { + if(ptr >= start_ptr && ptr < end_ptr){ + *ptr++ += f_i * y; + e += f_i; + } + return; + } + + Exponent t; + + for (int i = 0; i < n; i++) + { + t = f[j * n + i]; + t *= f_i; + compute_batch_fis(t, j, f, y, e, ptr, start_ptr, end_ptr); + } +} + +} // namespace aura \ No newline at end of file diff --git a/src/libaura/test/hoom_tests.cpp b/src/libaura/test/hoom_tests.cpp new file mode 100755 index 0000000000..b04af2daa2 --- /dev/null +++ b/src/libaura/test/hoom_tests.cpp @@ -0,0 +1,55 @@ +#include "../params.h" +#include "../sigmaplus_prover.h" +#include "../sigmaplus_verifier.h" +#include "../hoom_prover.h" +#include "../hoom_verifier.h" + +#include + +#include "../../test/fixtures.h" + +BOOST_FIXTURE_TEST_SUITE(aura_hoom_protocol_tests, ZerocoinTestingSetup200) + +BOOST_AUTO_TEST_CASE(one_out_of_n) +{ + int N = 65536; + int t_n = 2; + int t_m = 3; + int m_n = 3; + int m_m = 4; + int index = 20; + + secp_primitives::GroupElement g; + g.randomize(); + std::vector h_gens; + h_gens.resize(1); + h_gens[0].randomize(); + aura::HOOMProver prover(g, h_gens, t_n, t_m, m_n, m_m); + std::vector commits; + secp_primitives::Scalar r; + r.randomize(); + for(int i = 0; i < N; ++i){ + if(i == index){ + secp_primitives::GroupElement c; + secp_primitives::Scalar zero(uint64_t(0)); + c = aura::SigmaPrimitives::commit(g, zero, h_gens[0], r); + commits.push_back(c); + } + else{ + secp_primitives::GroupElement c; + secp_primitives::Scalar value; + value.randomize(); + c = aura::SigmaPrimitives::commit(g, value, h_gens[0], r); + commits.push_back(c); + } + } + aura::HOOMProof proof(t_n, t_m, m_n, m_m); + + prover.proof(commits, index, r, proof); + + aura::HOOMVerifier verifier(g, h_gens, t_n, t_m, m_n, m_m); + + BOOST_CHECK(verifier.verify(commits, proof)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/libaura/test/protocol_tests.cpp b/src/libaura/test/protocol_tests.cpp new file mode 100755 index 0000000000..6bda330446 --- /dev/null +++ b/src/libaura/test/protocol_tests.cpp @@ -0,0 +1,162 @@ +#include "../params.h" +#include "../hoom_prover.h" +#include "../hoom_verifier.h" +#include "../hoom_proof.h" + +#include + +#include "../../test/fixtures.h" + +void test(int t_n, int t_m, int m_n, int m_m, int n, int m, int index) +{ + int N = std::pow(t_n, t_m) * std::pow(m_n, m_m); + + secp_primitives::GroupElement g; + g.randomize(); + std::vector h_gens; + h_gens.resize(1); + h_gens[0].randomize(); + secp_primitives::Scalar y = u_int64_t(3241); + aura::SigmaPlusProver sigmaprover(g, h_gens, n, m); + aura::HOOMProver prover(g, h_gens, t_n, t_m, m_n, m_m); + std::vector commits; + secp_primitives::Scalar r; + r.randomize(); + for (int i = 0; i < N; ++i){ + if (i == index) { + secp_primitives::GroupElement c; + secp_primitives::Scalar zero(uint64_t(0)); + c = aura::SigmaPrimitives::commit(g, zero, + h_gens[0], r); + commits.push_back(c); + } else { + secp_primitives::GroupElement c; + secp_primitives::Scalar value; + value.randomize(); + c = aura::SigmaPrimitives::commit(g, value, + h_gens[0], r); + commits.push_back(c); + } + } + + aura::HOOMProof proof(t_n, t_m, m_n, m_m); + + std::clock_t verify_start = std::clock(); + prover.proof(commits, index, r, proof); + std::cout<< "HOOM PROOF GENERATED" << std::endl; + std::cout <<" t_n = " << t_n <<" t_m = " << t_m <<" m_n = " << m_n <<" m_m = " << m_m; + auto duration_clock = ( std::clock() - verify_start ) / (CLOCKS_PER_SEC / 1000); + std::cout << " Proof time " << duration_clock << " ms \n"; + + aura::HOOMVerifier verifier(g, h_gens, t_n, t_m, m_n, m_m); + + verify_start = std::clock(); + if(verifier.verify(commits, proof)) + std::cout<< "PASSED" << std::endl; + std::cout <<" t_n = " << t_n <<" t_m = " << t_m <<" m_n = " << m_n <<" m_m = " << m_m; + duration_clock = ( std::clock() - verify_start ) / (CLOCKS_PER_SEC / 1000); + std::cout << " Verify time " << duration_clock << " ms \n"; + + aura::SigmaPlusProof sigmaproof(n, m); + + verify_start = std::clock(); + sigmaprover.proof(commits, index, r, true, y, sigmaproof); + std::cout<< "SIGMA PROOF GENERATED" << std::endl; + std::cout <<" N = " << N << " n = " << n << " m = " < sigmaverifier(g, h_gens, n, m); + verify_start = std::clock(); + if(sigmaverifier.verify(commits, sigmaproof, true, y)) + std::cout<< "PASSED" << std::endl; + std::cout <<" N = " << N << " n = " << n << " m = " < index) +{ + int N = std::pow(t_n, t_m) * std::pow(m_n, m_m); + secp_primitives::GroupElement g; + int b = index.size(); + g.randomize(); + std::vector h_gens; + h_gens.resize(1); + h_gens[0].randomize(); + aura::HOOMProver prover(g, h_gens, t_n, t_m, m_n, m_m); + std::vector commits; + secp_primitives::Scalar r; + r.randomize(); + std::vector serials; + serials.resize(b); + for(int i = 0; i < b; ++i){ + serials[i].randomize(); + } + for (int i = 0; i < N; ++i){ + secp_primitives::GroupElement c; + secp_primitives::Scalar value; + value.randomize(); + c = aura::SigmaPrimitives::commit(g, value, + h_gens[0], r); + commits.push_back(c); + } + for (int i : index) { + commits[i] = aura::SigmaPrimitives::commit(g, uint64_t(0), + h_gens[0], + r); + } + + std::vector> proofs; + for(int i = 0; i < b; i++){ + aura::HOOMProof proof(t_n, t_m, m_n, m_m); + prover.proof(commits, index[i], r, proof); + proofs.push_back(proof); + } + + aura::HOOMVerifier verifier(g, h_gens, t_n, t_m, m_n, m_m); + if(verifier.batch_verify(commits, serials, proofs)) + std::cout<< "PASSED" << std::endl; + +/* std::clock_t verify_start = std::clock(); + prover.proof(commits, index, r, proof); + std::cout<< "HOOM PROOF GENERATED" << std::endl; + std::cout <<" t_n = " << t_n <<" t_m = " << t_m <<" m_n = " << m_n <<" m_m = " << m_m; + auto duration_clock = ( std::clock() - verify_start ) / (CLOCKS_PER_SEC / 1000); + std::cout << " Proof time " << duration_clock << " ms \n"; + + aura::HOOMVerifier verifier(g, h_gens, t_n, t_m, m_n, m_m); + + verify_start = std::clock(); + if(verifier.verify(commits, proof)) + std::cout<< "PASSED" << std::endl; + std::cout <<" t_n = " << t_n <<" t_m = " << t_m <<" m_n = " << m_n <<" m_m = " << m_m; + duration_clock = ( std::clock() - verify_start ) / (CLOCKS_PER_SEC / 1000); + std::cout << " Verify time " << duration_clock << " ms \n"; */ +} + + +BOOST_FIXTURE_TEST_SUITE(aura_protocol_tests, ZerocoinTestingSetup200 +) + +BOOST_AUTO_TEST_CASE(one_out_of_n) +{ +// test(2,4,4,4,8,4,11); +// test(2,5,4,5,8,5,11); + batch_test(2,4,4,4,8,4,{10,11,20}); + batch_test(2,2,3,2,6,2,{10,11,12}); + batch_test(2,4,4,4,8,4,{1,2,3,4,5,6}); + batch_test(2,2,3,2,6,2,{3,5,7,9}); + + /* test(2,6,4,6,8,6,11); + test(2,6,5,6,10,6,11); + test(2,7,4,7,8,7,11); + test(2,8,3,8,6,8,11); + test(2,8,3,8,6,8,11); */ + + +} + + + +BOOST_AUTO_TEST_SUITE_END()