diff --git a/.gitignore b/.gitignore index c20f4faa8..2d9cda84d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,13 @@ src/bitcoin-tx src/test/test_bitcoin src/qt/test/test_bitcoin-qt +# zerocash tests and utilities +src/zerocash/GenerateParamsForFiles +src/zerocash/tests/merkleTest +src/zerocash/tests/utilTest +src/zerocash/tests/zerocashTest +src/zerocash/tests/test_zerocash_pour_ppzksnark + # autoreconf Makefile.in aclocal.m4 diff --git a/configure.ac b/configure.ac index 2c8bf75c5..b21a4c7cb 100644 --- a/configure.ac +++ b/configure.ac @@ -717,23 +717,7 @@ CPPFLAGS="-I$LIBSNARK_INCDIR $CPPFLAGS" AC_CHECK_HEADER([libsnark/gadgetlib1/gadget.hpp],,AC_MSG_ERROR(libsnark headers missing)) AC_CHECK_LIB([snark],[main],LIBSNARK_LIBS=-lsnark, [AC_MSG_ERROR(libsnark missing)], [-lgmpxx]) -# Abuse the ugly libsnark hack above further to get the libzerocash directory -[LIBZEROCASH_INCDIR=$(echo "$LIBSNARK_INCDIR" | sed 's,libsnark$,libzerocash,')] -if test -d "$LIBZEROCASH_INCDIR"; then - echo "Found libzerocash include directory: $LIBZEROCASH_INCDIR" -else - AC_MSG_ERROR(libzerocash include directory not found) -fi - -CPPFLAGS="-I$LIBZEROCASH_INCDIR $CPPFLAGS" - -# libzerocash depends on headers in ./src/, so for the following -# AC_CHECK_HEADER, that has to be on the include path list. -CPPFLAGS_TEMP="$CPPFLAGS" -CPPFLAGS="-I ./src/ $CPPFLAGS" -AC_CHECK_HEADER([libzerocash/libzerocash/Zerocash.h],,AC_MSG_ERROR(libzerocash headers missing)) -AC_CHECK_LIB([zerocash], [main],LIBZEROCASH_LIBS="-lzerocash -lsnark -lcryptopp -lgmp -lgmpxx -lboost_system-mt -lcrypto", [AC_MSG_ERROR(libzerocash missing)], [-lsnark -lcryptopp -lgmp -lgmpxx -lboost_system-mt -lcrypto]) -CPPFLAGS="$CPPFLAGS_TEMP" +LIBZEROCASH_LIBS="-lsnark -lcryptopp -lgmp -lgmpxx -lboost_system-mt -lcrypto" AC_CHECK_LIB([crypto],[RAND_egd],[],[ AC_ARG_WITH([libressl], @@ -782,6 +766,7 @@ if test x$build_bitcoin_libs = xyes; then AC_DEFINE(HAVE_CONSENSUS_LIB, 1, [Define this symbol if the consensus lib has been built]) AC_CONFIG_FILES([libbitcoinconsensus.pc:libbitcoinconsensus.pc.in]) fi + AC_MSG_RESULT($build_bitcoin_libs) AC_LANG_POP diff --git a/depends/packages/ate-pairing.mk b/depends/packages/ate-pairing.mk deleted file mode 100644 index dbeab633d..000000000 --- a/depends/packages/ate-pairing.mk +++ /dev/null @@ -1,17 +0,0 @@ -package=ate-pairing -$(package)_version=0.1 -$(package)_download_path=https://github.com/herumi/$(package)/archive/ -$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz -$(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=37c05b4a60653b912a0130d77ac816620890d65a51dd9629ed65c15b54c2d8e0 -$(package)_dependencies=xbyak libgmp - -$(package)_git_commit=dd7889f2881e66f87165fcd180a03cf659bcb073 - -define $(package)_build_cmds - $(MAKE) -j SUPPORT_SNARK=1 INC_DIR='-I../include $($(package)_cppflags)' LIB_DIR='-L../lib $($(package)_ldflags)' -endef - -define $(package)_stage_cmds - cp -rv include/ lib/ $($(package)_staging_dir)$(host_prefix) -endef diff --git a/depends/packages/libsnark.mk b/depends/packages/libsnark.mk index 620edaee1..d8da9f977 100644 --- a/depends/packages/libsnark.mk +++ b/depends/packages/libsnark.mk @@ -6,7 +6,7 @@ $(package)_download_file=$($(package)_git_commit).tar.gz $(package)_sha256_hash=b5ec84a836d0d305407d5f39c8176bae2bb448abe802a8d11ba0f88f17e6d358 $(package)_git_commit=69f312f149cc4bd8def8e2fed26a7941ff41251d -$(package)_dependencies=crypto++ libgmp xbyak ate-pairing +$(package)_dependencies=libgmp $(package)_patches=1_fix_Wl_flag.patch define $(package)_preprocess_cmds diff --git a/depends/packages/libzerocash.mk b/depends/packages/libzerocash.mk deleted file mode 100644 index 538a1e719..000000000 --- a/depends/packages/libzerocash.mk +++ /dev/null @@ -1,33 +0,0 @@ -package=libzerocash -$(package)_download_path=https://github.com/Electric-Coin-Company/$(package)/archive/ -$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz -$(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=1364a739751bcdda86cfd66d3d019844d116c374d7a7634bfb3e1a47c085f3c0 -$(package)_git_commit=dd5db5815be70f0e4895784cc905df6f1c73cb17 - -$(package)_dependencies=libsnark crypto++ openssl boost libgmp -$(package)_patches= - -# FIXME: How do we know, at the point where the _build_cms are run, that the -# $(host_prefix)/include/libsnark folder is there? The lifecycle of that folder -# is as follows: -# 1. First, the _stage_cmds of libsnark.mk create it in the staging directory. -# 2. At some point in time, the depends system moves it from the staging -# directory to the actual $(host_prefix)/include/libsnark directory. -# -# If (2) happens after the libzerocash_build_cmds get run, then what's in the -# $(host_prefix)/include/libsnark directory will be an *old* copy of the -# libsnark headers, and we might have to build twice in order for libzerocash to -# get the changes. If (2) happens before, then all is well, and it works. -# -# ** Which is it? ** -# -$(package)_cppflags += -I$(BASEDIR)/../src -I. -I$(host_prefix)/include -I$(host_prefix)/include/libsnark -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -std=c++11 -pipe -O2 -O0 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -fPIC - -define $(package)_build_cmds - $(MAKE) all DEPINST=$(host_prefix) CXXFLAGS="$($(package)_cppflags)" STATIC=1 MINDEPS=1 USE_MT=1 LINK_RT=1 -endef - -define $(package)_stage_cmds - $(MAKE) install DEPINST=$(host_prefix) PREFIX=$($(package)_staging_dir)$(host_prefix) STATIC=1 MINDEPS=1 USE_MT=1 -endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 2c863438b..abfe8953d 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,4 +1,4 @@ -zerocash_packages := libsnark crypto++ libgmp xbyak ate-pairing libzerocash +zerocash_packages := libsnark crypto++ libgmp packages:=boost openssl $(zerocash_packages) native_packages := native_ccache native_comparisontool diff --git a/depends/packages/xbyak.mk b/depends/packages/xbyak.mk deleted file mode 100644 index a4ad7e37e..000000000 --- a/depends/packages/xbyak.mk +++ /dev/null @@ -1,17 +0,0 @@ -package=xbyak -$(package)_version=0.1 -$(package)_download_path=https://github.com/herumi/$(package)/archive/ -$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz -$(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=467a9037c29bc417840177f3ff5d76910d3f688f2f216dd86ced4a7ac837bfb0 -$(package)_dependencies= - -$(package)_git_commit=62fd6d022acd83209e2a5af8ec359a3a1bed3a50 - -define $(package)_build_cmds - echo 'xbyak build is unnecessary for consumer ate-pairing.' -endef - -define $(package)_stage_cmds - $(MAKE) install PREFIX=$($(package)_staging_dir)$(host_prefix) -endef diff --git a/qa/zcash/full-test-suite.sh b/qa/zcash/full-test-suite.sh index 3cdaab802..d4cdfde9d 100755 --- a/qa/zcash/full-test-suite.sh +++ b/qa/zcash/full-test-suite.sh @@ -29,6 +29,8 @@ cd "${REPOROOT}" # Test phases: run_test_phase "${REPOROOT}/qa/zcash/ensure-no-dot-so-in-depends.py" +run_test_phase "${REPOROOT}/src/zerocash/tests/merkleTest" +run_test_phase "${REPOROOT}/src/zerocash/tests/utilTest" # If make check fails, show test-suite.log as part of our run_test_phase # output (and fail the phase with false): diff --git a/src/Makefile.am b/src/Makefile.am index 7aade4ea3..2d9b0aac4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,6 +31,7 @@ LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBBITCOIN_UNIVALUE=univalue/libbitcoin_univalue.a LIBBITCOINQT=qt/libbitcoinqt.a LIBSECP256K1=secp256k1/libsecp256k1.la +LIBZEROCASH=libzerocash.a $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) @@ -43,7 +44,8 @@ EXTRA_LIBRARIES = \ libbitcoin_common.a \ univalue/libbitcoin_univalue.a \ libbitcoin_server.a \ - libbitcoin_cli.a + libbitcoin_cli.a \ + libzerocash.a if ENABLE_WALLET BITCOIN_INCLUDES += $(BDB_CPPFLAGS) EXTRA_LIBRARIES += libbitcoin_wallet.a @@ -67,6 +69,22 @@ if BUILD_BITCOIN_UTILS bin_PROGRAMS += zcash-cli bitcoin-tx endif +LIBZEROCASH_H = \ + zerocash/Address.h \ + zerocash/CoinCommitment.h \ + zerocash/Coin.h \ + zerocash/IncrementalMerkleTree.h \ + zerocash/MintTransaction.h \ + zerocash/PourInput.h \ + zerocash/PourOutput.h \ + zerocash/PourProver.h \ + zerocash/PourTransaction.h \ + zerocash/Zerocash.h \ + zerocash/ZerocashParams.h \ + zerocash/zerocash_pour_params.hpp \ + zerocash/utils/sha256.h \ + zerocash/utils/util.h + .PHONY: FORCE # bitcoin core # BITCOIN_CORE_H = \ @@ -202,7 +220,8 @@ libbitcoin_server_a_SOURCES = \ txmempool.cpp \ validationinterface.cpp \ $(JSON_H) \ - $(BITCOIN_CORE_H) + $(BITCOIN_CORE_H) \ + $(LIBZEROCASH_H) # wallet: shared between bitcoind and bitcoin-qt, but only linked # when wallet enabled @@ -215,7 +234,8 @@ libbitcoin_wallet_a_SOURCES = \ wallet/wallet.cpp \ wallet/wallet_ismine.cpp \ wallet/walletdb.cpp \ - $(BITCOIN_CORE_H) + $(BITCOIN_CORE_H) \ + $(LIBZEROCASH_H) # crypto primitives library crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES) @@ -269,7 +289,8 @@ libbitcoin_common_a_SOURCES = \ script/script_error.cpp \ script/sign.cpp \ script/standard.cpp \ - $(BITCOIN_CORE_H) + $(BITCOIN_CORE_H) \ + $(LIBZEROCASH_H) # util: shared between all executables. # This library *must* be included to make sure that the glibc @@ -291,7 +312,8 @@ libbitcoin_util_a_SOURCES = \ utilmoneystr.cpp \ utilstrencodings.cpp \ utiltime.cpp \ - $(BITCOIN_CORE_H) + $(BITCOIN_CORE_H) \ + $(LIBZEROCASH_H) if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp @@ -301,7 +323,8 @@ endif libbitcoin_cli_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_cli_a_SOURCES = \ rpcclient.cpp \ - $(BITCOIN_CORE_H) + $(BITCOIN_CORE_H) \ + $(LIBZEROCASH_H) nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h # @@ -321,6 +344,7 @@ zcashd_LDADD = \ $(LIBBITCOIN_UNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) \ + $(LIBZEROCASH) \ $(LIBLEVELDB) \ $(LIBMEMENV) \ $(LIBSECP256K1) @@ -335,6 +359,7 @@ zcashd_LDADD += \ $(SSL_LIBS) \ $(CRYPTO_LIBS) \ $(MINIUPNPC_LIBS) \ + $(LIBZEROCASH) \ $(LIBZEROCASH_LIBS) # @@ -353,6 +378,7 @@ zcash_cli_LDADD = \ $(BOOST_LIBS) \ $(SSL_LIBS) \ $(CRYPTO_LIBS) \ + $(LIBZEROCASH) \ $(LIBZEROCASH_LIBS) # @@ -372,11 +398,29 @@ bitcoin_tx_LDADD = \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) \ $(LIBSECP256K1) \ + $(LIBZEROCASH) \ $(LIBZEROCASH_LIBS) bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # +# zerocash protocol primitives # +libzerocash_a_SOURCES = \ + zerocash/Address.cpp \ + zerocash/CoinCommitment.cpp \ + zerocash/Coin.cpp \ + zerocash/IncrementalMerkleTree.cpp \ + zerocash/MintTransaction.cpp \ + zerocash/PourInput.cpp \ + zerocash/PourOutput.cpp \ + zerocash/PourProver.cpp \ + zerocash/PourTransaction.cpp \ + zerocash/ZerocashParams.cpp \ + zerocash/utils/sha256.cpp \ + zerocash/utils/util.cpp + +libzerocash_a_CPPFLAGS = -fPIC -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -std=c++11 -pipe -O2 -O0 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES) + # bitcoinconsensus library # if BUILD_BITCOIN_LIBS include_HEADERS = script/bitcoinconsensus.h @@ -443,3 +487,5 @@ endif if ENABLE_QT_TESTS include Makefile.qttest.include endif + +include Makefile.zcash.include diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 64d984e9e..825e7130d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -88,7 +88,7 @@ endif test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) test_test_bitcoin_CPPFLAGS = $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ - $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(LIBZEROCASH_LIBS) + $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(LIBZEROCASH) $(LIBZEROCASH_LIBS) if ENABLE_WALLET test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) endif diff --git a/src/Makefile.zcash.include b/src/Makefile.zcash.include new file mode 100644 index 000000000..ccf4b31a9 --- /dev/null +++ b/src/Makefile.zcash.include @@ -0,0 +1,46 @@ +bin_PROGRAMS += \ + zerocash/GenerateParamsForFiles \ + zerocash/tests/merkleTest \ + zerocash/tests/utilTest \ + zerocash/tests/zerocashTest \ + zerocash/tests/test_zerocash_pour_ppzksnark + +# tool for generating our public parameters +zerocash_GenerateParamsForFiles_SOURCES = zerocash/GenerateParamsForFiles.cpp +zerocash_GenerateParamsForFiles_LDADD = \ + $(BOOST_LIBS) \ + $(LIBZEROCASH) \ + $(LIBZEROCASH_LIBS) + +# tests for our incremental merkle tree +zerocash_tests_merkleTest_SOURCES = zerocash/tests/merkleTest.cpp +zerocash_tests_merkleTest_LDADD = \ + $(BOOST_LIBS) \ + $(LIBZEROCASH) \ + $(LIBZEROCASH_LIBS) + +# tests for utilities that come with zerocash +zerocash_tests_utilTest_SOURCES = zerocash/tests/utilTest.cpp +zerocash_tests_utilTest_LDADD = \ + $(BOOST_LIBS) \ + $(LIBZEROCASH) \ + $(LIBZEROCASH_LIBS) + +# tests for libzerocash APIs +zerocash_tests_zerocashTest_SOURCES = \ + zerocash/tests/zerocashTest.cpp \ + zerocash/tests/timer.cpp + +zerocash_tests_zerocashTest_LDADD = \ + $(BOOST_LIBS) \ + $(LIBZEROCASH) \ + $(LIBZEROCASH_LIBS) + +# tests for our zkSNARK circuit + +zerocash_tests_test_zerocash_pour_ppzksnark_SOURCES = zerocash/tests/test_zerocash_pour_ppzksnark.cpp +zerocash_tests_test_zerocash_pour_ppzksnark_LDADD = \ + $(BOOST_LIBS) \ + $(LIBZEROCASH) \ + $(LIBZEROCASH_LIBS) + diff --git a/src/coins.h b/src/coins.h index 7f16aa0ff..d128eb9b3 100644 --- a/src/coins.h +++ b/src/coins.h @@ -16,7 +16,7 @@ #include #include -#include "libzerocash/IncrementalMerkleTree.h" +#include "zerocash/IncrementalMerkleTree.h" static const unsigned int INCREMENTAL_MERKLE_TREE_DEPTH = 20; diff --git a/src/init.h b/src/init.h index 0dd6da7e0..fcc946265 100644 --- a/src/init.h +++ b/src/init.h @@ -8,7 +8,7 @@ #include -#include "libzerocash/ZerocashParams.h" +#include "zerocash/ZerocashParams.h" class CScheduler; class CWallet; diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 0c2522037..81e9af51a 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -9,8 +9,8 @@ #include "tinyformat.h" #include "utilstrencodings.h" -#include "libzerocash/PourProver.h" -#include "libzerocash/PourTransaction.h" +#include "zerocash/PourProver.h" +#include "zerocash/PourTransaction.h" template boost::array, N> uint256_to_array(const boost::array& in) { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index a7c54be10..727cc14aa 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -13,9 +13,9 @@ #include -#include "libzerocash/ZerocashParams.h" -#include "libzerocash/PourInput.h" -#include "libzerocash/PourOutput.h" +#include "zerocash/ZerocashParams.h" +#include "zerocash/PourInput.h" +#include "zerocash/PourOutput.h" using namespace libzerocash; diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 0a5c7853a..d03863cc1 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -11,7 +11,7 @@ #include #include -#include "libzerocash/IncrementalMerkleTree.h" +#include "zerocash/IncrementalMerkleTree.h" namespace { diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 523bd790e..c869e37ff 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -25,12 +25,12 @@ #include #include "json/json_spirit_writer_template.h" -#include "libzerocash/ZerocashParams.h" -#include "libzerocash/IncrementalMerkleTree.h" -#include "libzerocash/PourInput.h" -#include "libzerocash/PourOutput.h" -#include "libzerocash/Address.h" -#include "libzerocash/Coin.h" +#include "zerocash/ZerocashParams.h" +#include "zerocash/IncrementalMerkleTree.h" +#include "zerocash/PourInput.h" +#include "zerocash/PourOutput.h" +#include "zerocash/Address.h" +#include "zerocash/Coin.h" using namespace std; using namespace json_spirit; diff --git a/src/txdb.h b/src/txdb.h index 2992a03e2..bd401b801 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -14,7 +14,7 @@ #include #include -#include "libzerocash/IncrementalMerkleTree.h" +#include "zerocash/IncrementalMerkleTree.h" class CBlockFileInfo; class CBlockIndex; diff --git a/src/zerocash/.gitignore b/src/zerocash/.gitignore new file mode 100644 index 000000000..f4620b0fe --- /dev/null +++ b/src/zerocash/.gitignore @@ -0,0 +1,16 @@ +*.o +*.d +depinst/ +depsrc/ + +libzerocash.a +libzerocash.so + +zerocash_pour_ppzksnark/tests/test_zerocash_pour_ppzksnark +zerocash_pour_ppzksnark/profiling/profile_zerocash_pour_gadget +tests/zerocashTest +tests/merkleTest +tests/utilTest +libzerocash/GenerateParamsForFiles +zerocashTest-proving-key +zerocashTest-verification-key diff --git a/src/zerocash/AUTHORS b/src/zerocash/AUTHORS new file mode 100644 index 000000000..3a23e189a --- /dev/null +++ b/src/zerocash/AUTHORS @@ -0,0 +1,7 @@ +Eli Ben-Sasson +Alessandro Chiesa +Christina Garman +Matthew Green +Ian Miers +Eran Tromer +Madars Virza diff --git a/src/zerocash/Address.cpp b/src/zerocash/Address.cpp new file mode 100644 index 000000000..1209df38c --- /dev/null +++ b/src/zerocash/Address.cpp @@ -0,0 +1,151 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the classes Address and PublicAddress. + + See Address.h . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +using CryptoPP::AutoSeededRandomPool; + +#include +using CryptoPP::ECP; +using CryptoPP::ECIES; + +#include +namespace ASN1 = CryptoPP::ASN1; + +#include +using CryptoPP::StringSink; +using CryptoPP::StringStore; + +#include "Zerocash.h" +#include "Address.h" + +namespace libzerocash { + +PrivateAddress::PrivateAddress(const std::vector a_sk, const std::string sk_enc) { + this->a_sk = a_sk; + this->sk_enc = sk_enc; +} + +PrivateAddress::PrivateAddress() { + +} + +bool PrivateAddress::operator==(const PrivateAddress& rhs) const { + return ((this->a_sk == rhs.a_sk) && (this->sk_enc == rhs.sk_enc)); +} + +bool PrivateAddress::operator!=(const PrivateAddress& rhs) const { + return !(*this == rhs); +} + +const std::string PrivateAddress::getEncryptionSecretKey() const { + return this->sk_enc; +} + +const std::vector& PrivateAddress::getAddressSecret() const { + return this->a_sk; +} + +PublicAddress::PublicAddress(): a_pk(ZC_A_PK_SIZE) { + this->pk_enc = ""; +} + +PublicAddress::PublicAddress(const std::vector& a_pk, std::string& pk_enc) : a_pk(a_pk), pk_enc(pk_enc) {} + +PublicAddress::PublicAddress(const PrivateAddress& addr_sk): a_pk(ZC_A_PK_SIZE) { + std::vector a_sk_bool(ZC_A_SK_SIZE * 8); + convertBytesVectorToVector(addr_sk.getAddressSecret(), a_sk_bool); + + std::vector zeros_256(256, 0); + + std::vector a_pk_internal; + concatenateVectors(a_sk_bool, zeros_256, a_pk_internal); + + std::vector a_pk_bool(ZC_A_PK_SIZE * 8); + hashVector(a_pk_internal, a_pk_bool); + + convertVectorToBytesVector(a_pk_bool, this->a_pk); + + ECIES::PublicKey publicKey; + + ECIES::PrivateKey decodedPrivateKey; + decodedPrivateKey.Load(StringStore(addr_sk.getEncryptionSecretKey()).Ref()); + + decodedPrivateKey.MakePublicKey(publicKey); + + std::string encodedPublicKey; + publicKey.Save(StringSink(encodedPublicKey).Ref()); + + this->pk_enc = encodedPublicKey; +} + +const std::string PublicAddress::getEncryptionPublicKey() const { + return this->pk_enc; +} + +const std::vector& PublicAddress::getPublicAddressSecret() const { + return this->a_pk; +} + +bool PublicAddress::operator==(const PublicAddress& rhs) const { + return ((this->a_pk == rhs.a_pk) && (this->pk_enc == rhs.pk_enc)); +} + +bool PublicAddress::operator!=(const PublicAddress& rhs) const { + return !(*this == rhs); +} + +Address::Address(PrivateAddress& priv) : addr_pk(priv), addr_sk(priv) { + +} + +Address::Address() : addr_pk(), addr_sk() { + +} + +const PublicAddress& Address::getPublicAddress() const { + return this->addr_pk; +} + +const PrivateAddress& Address::getPrivateAddress() const { + return this->addr_sk; +} + +bool Address::operator==(const Address& rhs) const { + return ((this->addr_sk == rhs.addr_sk) && (this->addr_pk == rhs.addr_pk)); +} + +bool Address::operator!=(const Address& rhs) const { + return !(*this == rhs); +} + +Address Address::CreateNewRandomAddress() { + std::vector a_sk(ZC_A_SK_SIZE); + + unsigned char a_sk_bytes[ZC_A_SK_SIZE]; + getRandBytes(a_sk_bytes, ZC_A_SK_SIZE); + convertBytesToBytesVector(a_sk_bytes, a_sk); + + AutoSeededRandomPool prng; + + ECIES::PrivateKey privateKey; + privateKey.Initialize(prng, ASN1::secp256r1()); + + std::string encodedPrivateKey; + + privateKey.Save(StringSink(encodedPrivateKey).Ref()); + + PrivateAddress addr_sk(a_sk, encodedPrivateKey); + return Address(addr_sk); +} + +} /* namespace libzerocash */ diff --git a/src/zerocash/Address.h b/src/zerocash/Address.h new file mode 100644 index 000000000..03cf539ce --- /dev/null +++ b/src/zerocash/Address.h @@ -0,0 +1,86 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the classes Address and PublicAddress. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ADDRESS_H_ +#define ADDRESS_H_ + +#include +#include + + +namespace libzerocash { + +/***************************** Private address ********************************/ + +class PrivateAddress { +public: + /* This constructor is to be used ONLY for deserialization. */ + PrivateAddress(); + PrivateAddress(const std::vector a_sk, const std::string sk_enc); + + bool operator==(const PrivateAddress& rhs) const; + bool operator!=(const PrivateAddress& rhs) const; + + const std::vector& getAddressSecret() const; + const std::string getEncryptionSecretKey() const; + +private: + std::vector a_sk; + std::string sk_enc; + +}; + +/***************************** Public address ********************************/ + +class PublicAddress { +public: + /* This constructor is to be used ONLY for deserialization. */ + PublicAddress(); + PublicAddress(const std::vector& a_pk, std::string& pk_enc); + PublicAddress(const PrivateAddress& addr_sk); + + bool operator==(const PublicAddress& rhs) const; + bool operator!=(const PublicAddress& rhs) const; + + + const std::vector& getPublicAddressSecret() const; + const std::string getEncryptionPublicKey() const; + +private: + std::vector a_pk; + std::string pk_enc; +}; + +/******************************** Address ************************************/ + +class Address { +public: + /* This constructor is to be used ONLY for deserialization. */ + Address(); + Address(PrivateAddress&); + + const PublicAddress& getPublicAddress() const; + const PrivateAddress& getPrivateAddress() const; + + bool operator==(const Address& rhs) const; + bool operator!=(const Address& rhs) const; + + + static Address CreateNewRandomAddress(); + +private: + PublicAddress addr_pk; + PrivateAddress addr_sk; +}; + +} /* namespace libzerocash */ + +#endif /* ADDRESS_H_ */ diff --git a/src/zerocash/Coin.cpp b/src/zerocash/Coin.cpp new file mode 100644 index 000000000..920585a60 --- /dev/null +++ b/src/zerocash/Coin.cpp @@ -0,0 +1,161 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the class Coin. + + See coin.h . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +using CryptoPP::AutoSeededRandomPool; + +#include +using CryptoPP::ECP; +using CryptoPP::ECIES; + +#include +namespace ASN1 = CryptoPP::ASN1; + +#include +using CryptoPP::StringSink; +using CryptoPP::StringStore; + +#include + +#include "Zerocash.h" +#include "Coin.h" + +namespace libzerocash { + +Coin::Coin(): addr_pk(), cm(), rho(ZC_RHO_SIZE), r(ZC_R_SIZE), coinValue(ZC_V_SIZE) { + +} + +Coin::Coin(const std::string bucket, Address& addr): addr_pk(), cm(), rho(ZC_RHO_SIZE), r(ZC_R_SIZE), k(ZC_K_SIZE), coinValue(ZC_V_SIZE) { + // Retreive and decode the private key + ECIES::PrivateKey decodedPrivateKey; + decodedPrivateKey.Load(StringStore(addr.getPrivateAddress().getEncryptionSecretKey()).Ref()); + + // Create the decryption session + AutoSeededRandomPool prng; + ECIES::Decryptor decrypt(decodedPrivateKey); + + // Convert the input string into a vector of bytes + std::vector bucket_bytes(bucket.begin(), bucket.end()); + + // Construct a temporary object to store the plaintext, large enough + // to store the plaintext if it were extended beyond the real size. + std::vector plaintext; + // Size as needed, filling with zeros. + plaintext.resize(decrypt.MaxPlaintextLength(decrypt.CiphertextLength(ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE)), 0); + + // Perform the decryption + decrypt.Decrypt(prng, + &bucket_bytes[0], + decrypt.CiphertextLength(ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE), + &plaintext[0]); + + // Grab the byte vectors + std::vector value_v(plaintext.begin(), + plaintext.begin() + ZC_V_SIZE); + std::vector r_v(plaintext.begin() + ZC_V_SIZE, + plaintext.begin() + ZC_V_SIZE + ZC_R_SIZE); + std::vector rho_v(plaintext.begin() + ZC_V_SIZE + ZC_R_SIZE, + plaintext.begin() + ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE); + + this->coinValue = value_v; + this->r = r_v; + this->rho = rho_v; + this->addr_pk = addr.getPublicAddress(); + + std::vector a_pk = addr.getPublicAddress().getPublicAddressSecret(); + + this->computeCommitments(a_pk); +} + +Coin::Coin(const PublicAddress& addr, uint64_t value): addr_pk(addr), cm(), rho(ZC_RHO_SIZE), r(ZC_R_SIZE), k(ZC_K_SIZE), coinValue(ZC_V_SIZE) +{ + convertIntToBytesVector(value, this->coinValue); + + std::vector a_pk = addr.getPublicAddressSecret(); + + unsigned char rho_bytes[ZC_RHO_SIZE]; + getRandBytes(rho_bytes, ZC_RHO_SIZE); + convertBytesToBytesVector(rho_bytes, this->rho); + + unsigned char r_bytes[ZC_R_SIZE]; + getRandBytes(r_bytes, ZC_R_SIZE); + convertBytesToBytesVector(r_bytes, this->r); + + this->computeCommitments(a_pk); +} + + +Coin::Coin(const PublicAddress& addr, uint64_t value, + const std::vector& rho, const std::vector& r): addr_pk(addr), rho(rho), r(r), k(ZC_K_SIZE), coinValue(ZC_V_SIZE) +{ + convertIntToBytesVector(value, this->coinValue); + + std::vector a_pk = addr.getPublicAddressSecret(); + + this->computeCommitments(a_pk); +} + +void +Coin::computeCommitments(std::vector& a_pk) +{ + std::vector k_internal; + std::vector k_internalhash_trunc(16); + + std::vector k_internalhash_internal; + concatenateVectors(a_pk, this->rho, k_internalhash_internal); + + std::vector k_internalhash(ZC_K_SIZE); + hashVector(k_internalhash_internal, k_internalhash); + + copy(k_internalhash.begin(), k_internalhash.begin()+16, k_internalhash_trunc.begin()); + concatenateVectors(this->r, k_internalhash_trunc, k_internal); + hashVector(k_internal, this->k); + + CoinCommitment com(this->coinValue, this->k); + this->cm = com; +} + +bool Coin::operator==(const Coin& rhs) const { + return ((this->cm == rhs.cm) && (this->rho == rhs.rho) && (this->r == rhs.r) && (this->k == rhs.k) && (this->coinValue == rhs.coinValue) && (this->addr_pk == rhs.addr_pk)); +} + +bool Coin::operator!=(const Coin& rhs) const { + return !(*this == rhs); +} + +const PublicAddress& Coin::getPublicAddress() const { + return this->addr_pk; +} + +const CoinCommitment& Coin::getCoinCommitment() const { + return this->cm; +} + +const std::vector& Coin::getInternalCommitment() const { + return this->k; +} + +const std::vector& Coin::getRho() const { + return this->rho; +} + +const std::vector& Coin::getR() const { + return this->r; +} + +uint64_t Coin::getValue() const { + return convertBytesVectorToInt(this->coinValue); +} + +} /* namespace libzerocash */ diff --git a/src/zerocash/Coin.h b/src/zerocash/Coin.h new file mode 100644 index 000000000..ccd30f4c2 --- /dev/null +++ b/src/zerocash/Coin.h @@ -0,0 +1,74 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the class Coin. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef COIN_H_ +#define COIN_H_ + +#include + +#include "Address.h" +#include "CoinCommitment.h" + +namespace libzerocash { + +/********************************* Coin **************************************/ + +class Coin { + +friend class MintTransaction; +friend class PourTransaction; + +public: + /* This constructor is to be used ONLY for deserialization. */ + Coin(); + /** + * @param addr the address the coin will belong to when minted or poured into + * @param value the monetary value of the coin + */ + Coin(const PublicAddress& addr, + uint64_t value); + + Coin(const PublicAddress& addr, + uint64_t value, + const std::vector& rho, + const std::vector& r); + + Coin(const std::string bucket, Address& addr); + + const PublicAddress& getPublicAddress() const; + + const CoinCommitment& getCoinCommitment() const; + + bool operator==(const Coin& rhs) const; + bool operator!=(const Coin& rhs) const; + + uint64_t getValue() const; + + const std::vector& getRho() const; + + const std::vector& getR() const; + +private: + PublicAddress addr_pk; + CoinCommitment cm; + std::vector rho; + std::vector r; + std::vector k; + std::vector coinValue; + + const std::vector& getInternalCommitment() const; + + void computeCommitments(std::vector& a_pk); +}; + +} /* namespace libzerocash */ + +#endif /* COIN_H_ */ diff --git a/src/zerocash/CoinCommitment.cpp b/src/zerocash/CoinCommitment.cpp new file mode 100644 index 000000000..e77dea9f7 --- /dev/null +++ b/src/zerocash/CoinCommitment.cpp @@ -0,0 +1,58 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the class CoinCommitment. + + See CoinCommitment.h . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +#include + +#include "Zerocash.h" +#include "CoinCommitment.h" + +namespace libzerocash { + +CoinCommitment::CoinCommitment() : commitmentValue(ZC_CM_SIZE) +{ } + +CoinCommitment::CoinCommitment(const std::vector& val, + const std::vector& k) : commitmentValue(ZC_CM_SIZE) +{ + std::vector zeros_192(192, 0); + std::vector cm_internal; + std::vector value_bool(ZC_V_SIZE * 8, 0); + std::vector k_bool(ZC_K_SIZE * 8, 0); + + if (val.size() > ZC_V_SIZE || k.size() > ZC_K_SIZE) { + throw std::runtime_error("CoinCommitment: inputs are too large"); + } + + libzerocash::convertBytesVectorToVector(val, value_bool); + libzerocash::convertBytesVectorToVector(k, k_bool); + + libzerocash::concatenateVectors(k_bool, zeros_192, value_bool, cm_internal); + std::vector cm_bool(ZC_CM_SIZE * 8); + libzerocash::hashVector(cm_internal, cm_bool); + libzerocash::convertVectorToBytesVector(cm_bool, this->commitmentValue); +} + +bool CoinCommitment::operator==(const CoinCommitment& rhs) const { + return (this->commitmentValue == rhs.commitmentValue); +} + +bool CoinCommitment::operator!=(const CoinCommitment& rhs) const { + return !(*this == rhs); +} + +const std::vector& CoinCommitment::getCommitmentValue() const { + return this->commitmentValue; +} + +} /* namespace libzerocash */ diff --git a/src/zerocash/CoinCommitment.h b/src/zerocash/CoinCommitment.h new file mode 100644 index 000000000..0358d67ca --- /dev/null +++ b/src/zerocash/CoinCommitment.h @@ -0,0 +1,44 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the class CoinCommitment. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef COINCOMMITMENT_H_ +#define COINCOMMITMENT_H_ + +#include + +namespace libzerocash { + +/****************************** Coin commitment ******************************/ + +class CoinCommitment { + +friend class PourTransaction; +friend class PourProver; + +public: + CoinCommitment(); + + CoinCommitment(const std::vector& val, + const std::vector& k); + + const std::vector& getCommitmentValue() const; + + bool operator==(const CoinCommitment& rhs) const; + bool operator!=(const CoinCommitment& rhs) const; + + +private: + std::vector commitmentValue; +}; + +} /* namespace libzerocash */ + +#endif /* COINCOMMITMENT_H_ */ diff --git a/src/zerocash/GenerateParamsForFiles.cpp b/src/zerocash/GenerateParamsForFiles.cpp new file mode 100644 index 000000000..12965b91f --- /dev/null +++ b/src/zerocash/GenerateParamsForFiles.cpp @@ -0,0 +1,42 @@ +/** @file + ***************************************************************************** + + Functionality to generate files containing the Zerocash public parameters. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include + +#include "Zerocash.h" +#include "ZerocashParams.h" +#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" +#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" + +using namespace libzerocash; + +int main(int argc, char **argv) +{ + if(argc != 4) { + std::cerr << "Usage: " << argv[0] << " treeDepth provingKeyFileName verificationKeyFileName" << std::endl; + return 1; + } + + unsigned int tree_depth = atoi(argv[1]); + std::string pkFile = argv[2]; + std::string vkFile = argv[3]; + + auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(tree_depth); + libzerocash::ZerocashParams p( + tree_depth, + &keypair + ); + + libzerocash::ZerocashParams::SaveProvingKeyToFile(&p.getProvingKey(), pkFile); + libzerocash::ZerocashParams::SaveVerificationKeyToFile(&p.getVerificationKey(), vkFile); + + return 0; +} diff --git a/src/zerocash/IncrementalMerkleTree.cpp b/src/zerocash/IncrementalMerkleTree.cpp new file mode 100644 index 000000000..326dd2097 --- /dev/null +++ b/src/zerocash/IncrementalMerkleTree.cpp @@ -0,0 +1,607 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the classes IncrementalMerkleTreeCompact, + IncrementalMerkleNode, and IncrementalMerkleTree. + + See IncrementalMerkleTree.h . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "IncrementalMerkleTree.h" +#include "Zerocash.h" + +#include +#include +#include + +namespace libzerocash { + + ///////////////////////////////////////////// + // IncrementalMerkleTreeCompact class + ///////////////////////////////////////////// + + std::vector IncrementalMerkleTreeCompact::serialize() const { + /* Serialization format: + * treeHeight (4 bytes, big endian) + * hashList (ceil(treeHeight / 8) bytes) + * hashVec (32 bytes for every 1 bit in hashList) + */ + std::vector serialized; + + /* treeHeight (4 bytes, big endian) */ + std::vector treeHeightBytes(4); + convertIntToBytesVector((uint64_t)this->treeHeight, treeHeightBytes); + serialized.insert(serialized.end(), treeHeightBytes.begin(), treeHeightBytes.end()); + + /* hashList */ + assert(this->hashList.size() == this->treeHeight); + + /* Pad it out to a multiple of 8 bits. */ + std::vector hashList = this->hashList; + if (hashList.size() % 8 != 0) { + hashList.insert(hashList.begin(), 8 - (hashList.size() % 8), false); + } + assert(hashList.size() % 8 == 0); + + /* Convert it to a byte vector. */ + std::vector hashListBytes(hashList.size() / 8); + convertVectorToBytesVector(hashList, hashListBytes); + serialized.insert(serialized.end(), hashListBytes.begin(), hashListBytes.end()); + + /* hashVec */ + assert(this->hashVec.size() == countOnes(this->hashList)); + for (uint32_t i = 0; i < this->hashVec.size(); i++) { + assert(this->hashVec.at(i).size() == 32); + serialized.insert(serialized.end(), this->hashVec.at(i).begin(), this->hashVec.at(i).end()); + } + + return serialized; + } + + IncrementalMerkleTreeCompact IncrementalMerkleTreeCompact::deserialize(const std::vector& serialized) { + IncrementalMerkleTreeCompact deserialized; + + size_t currentPos = 0; + + + /* treeHeight */ + std::vector treeHeightBytes = vectorSlice(serialized, 0, 4); + currentPos += 4; + deserialized.treeHeight = convertBytesVectorToInt(treeHeightBytes); + + /* hashList */ + uint32_t hashListBytesLength = ceil(deserialized.treeHeight / 8.0); + std::vector hashListBytes = vectorSlice(serialized, currentPos, hashListBytesLength); + currentPos += hashListBytesLength; + convertBytesVectorToVector(hashListBytes, deserialized.hashList); + /* Remove the multiple-of-8-bits padding. */ + deserialized.hashList.erase(deserialized.hashList.begin(), + deserialized.hashList.end() - deserialized.treeHeight + ); + + /* hashVec */ + size_t hashVecSize = countOnes(deserialized.hashList); + for (size_t i = 0; i < hashVecSize; i++) { + std::vector hashVecElement = vectorSlice(serialized, currentPos, 32); + currentPos += 32; + deserialized.hashVec.push_back(hashVecElement); + } + + if (currentPos != serialized.size()) { + throw std::runtime_error("Serialized vector is longer than expected."); + } + + return deserialized; + } + + ///////////////////////////////////////////// + // IncrementalMerkleTree class + ///////////////////////////////////////////// + + // Custom tree constructor (initialize tree of specified height) + IncrementalMerkleTree::IncrementalMerkleTree(uint32_t height) : root(0, height) { + treeHeight = height; + } + + // Vector constructor. Initializes and inserts a list of elements. + IncrementalMerkleTree::IncrementalMerkleTree(std::vector< std::vector > &valueVector, uint32_t height) : root(0, height) + { + // Initialize the tree + treeHeight = height; + + // Load the tree with all the given values + if (this->insertVector(valueVector) == false) { + throw std::runtime_error("Could not insert vector into Merkle Tree: too many elements"); + } + } + + // Custom tree constructor (initialize tree from compact representation) + // + IncrementalMerkleTree::IncrementalMerkleTree(IncrementalMerkleTreeCompact &compact) : root(0, 0) + { + // Initialize the tree + this->treeHeight = compact.getHeight(); + root.treeHeight = treeHeight; + + // Reconstitute tree from compact representation + this->fromCompactRepresentation(compact); + } + + bool + IncrementalMerkleTree::insertElement(const std::vector &hashV, std::vector &index) { + + // Resize the index vector + index.resize(this->treeHeight); + + // Insert the element + return this->root.insertElement(hashV, index); + } + + bool + IncrementalMerkleTree::insertElement(const std::vector &hashV, std::vector &index) { + + // Create a temporary vector to hold hashV + std::vector hashVBool(hashV.size() * 8); + convertBytesVectorToVector(hashV, hashVBool); + + // Create a temporary vector to hold the index + std::vector indexBool(this->treeHeight, 0); + + // Insert the element + bool result = this->insertElement(hashVBool, indexBool); + + // Convert the returned vector + index.resize(index.size() / 8); // this might need to include a ceil + convertVectorToBytesVector(indexBool, index); + + return result; + } + + bool + IncrementalMerkleTree::getWitness(const std::vector &index, merkle_authentication_path &witness) { + + // Resize the witness if necessary + if (witness.size() < this->treeHeight) { + witness.resize(treeHeight); + } + + std::vector indexPadded = index; + + // Discard leading bits of the index if necessary + if (indexPadded.size() > this->treeHeight) { + indexPadded.erase(indexPadded.begin(), indexPadded.begin() + (indexPadded.size() - this->treeHeight)); + } + + // If the index vector is less than 'treeHeight' bits, pad the leftmost bits with 0 (false) + // This is to deal with the situation where somebody encodes e.g., a 32-bit integer as an index + // into a 64 height tree and does not explicitly pad to length. + if (indexPadded.size() < this->treeHeight) { + indexPadded.insert(indexPadded.begin(), (this->treeHeight - 1) - indexPadded.size(), false); + } + + return this->root.getWitness(indexPadded, witness); + } + + bool + IncrementalMerkleTree::insertVector(std::vector< std::vector > &valueVector) + { + std::vector index; + + for (std::vector< std::vector >::iterator iter = valueVector.begin(); + iter != valueVector.end(); ++iter) { + + if (this->insertElement(*iter, index) == false) { + return false; + } + + } + + return true; + } + + bool + IncrementalMerkleTree::getRootValue(std::vector& r) const { + + // Query the root for its hash + this->root.getValue(r); + return true; + } + + bool + IncrementalMerkleTree::getRootValue(std::vector& r) const { + + // Create a temporary byte vector + std::vector tempR(r.size() * 8, 0); + + // Query the root for its hash + this->root.getValue(tempR); + + // Convert the result back into the given vector + convertVectorToBytesVector(tempR, r); + + return true; + } + std::vector + IncrementalMerkleTree::getRoot(){ + std::vector temp(8); + this->getRootValue(temp); + return temp; + } + + bool + IncrementalMerkleTree::prune() + { + return this->root.prune(); + } + + IncrementalMerkleTreeCompact + IncrementalMerkleTree::getCompactRepresentation() const + { + IncrementalMerkleTreeCompact rep; + rep.hashList.resize(this->treeHeight); + rep.treeHeight = this->treeHeight; + std::fill (rep.hashList.begin(), rep.hashList.end(), false); + + this->root.getCompactRepresentation(rep); + return rep; + } + + bool + IncrementalMerkleTree::fromCompactRepresentation(IncrementalMerkleTreeCompact &rep) + { + return this->root.fromCompactRepresentation(rep, 0); + } + + ///////////////////////////////////////////// + // IncrementalMerkleNode class + ///////////////////////////////////////////// + + // Standard constructor + // + IncrementalMerkleNode::IncrementalMerkleNode(uint32_t depth, uint32_t height) : left(NULL), right(NULL), value(SHA256_BLOCK_SIZE * 8, 0), nodeDepth(depth), treeHeight(height), + subtreeFull(false), subtreePruned(false) + { + sha256_init(&ctx256); + } + + // Copy constructor + // + IncrementalMerkleNode::IncrementalMerkleNode(const IncrementalMerkleNode& toCopy) : left(NULL), right(NULL), value(SHA256_BLOCK_SIZE * 8, 0) + { + sha256_init(&ctx256); + this->nodeDepth = toCopy.nodeDepth; + this->subtreePruned = toCopy.subtreePruned; + this->subtreeFull = toCopy.subtreeFull; + this->value = toCopy.value; + this->treeHeight = toCopy.treeHeight; + + // Recursively copy the subtrees + if (toCopy.left) { + this->left = new IncrementalMerkleNode(toCopy.left->nodeDepth, toCopy.left->treeHeight); + *(this->left) = *(toCopy.left); + } + + if (toCopy.right) { + this->right = new IncrementalMerkleNode(toCopy.right->nodeDepth, toCopy.right->treeHeight); + *(this->right) = *(toCopy.right); + } + } + + IncrementalMerkleNode::~IncrementalMerkleNode() + { + if (this->left) { + delete this->left; + this->left = NULL; + } + + if (this->right) { + delete this->right; + this->right = NULL; + } + } + + bool + IncrementalMerkleNode::insertElement(const std::vector &hashV, std::vector &index) + { + bool result = false; + + // Check if we have any free leaves. If not, bail. + if (this->subtreeFull == true) { + return false; + } + + // Are we a leaf? If so, store the hash value. + if (this->isLeaf()) { + // Store the given hash value here and return success. + this->value = hashV; + this->subtreeFull = true; + return true; + } + + // We're not a leaf. Try to insert into subtrees, creating them if necessary. + // Try to recurse on left subtree + if (!this->left) { + this->left = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight); + } + result = this->left->insertElement(hashV, index); + if (result == true) { + // Update the index value to indicate where the new node went + index.at(this->nodeDepth) = false; + } + + // If that failed, try to recurse on right subtree. + if (result == false) { + if (!this->right) { + this->right = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight); + } + result = this->right->insertElement(hashV, index); + if (result == true) { + index.at(this->nodeDepth) = true; + } + } + + // If one of the inserts succeeded, update our 'fullness' status. + if (result == true) { + this->updateHashValue(); + if (this->isLeaf()) { this->subtreeFull = true; } + else { + this->subtreeFull = this->checkIfNodeFull(); + } + } + + // Return the result + return result; + } + + bool + IncrementalMerkleNode::getWitness(const std::vector &index, merkle_authentication_path &witness) + { + bool result = false; + + // If this node is a leaf: do nothing and return success + if (this->isLeaf()) { + return true; + } + + // If this node is pruned, we can't fetch a witness. Return failure. + if (this->isPruned()) { + return false; + } + + // If the index path leads to the left, we grab the hash value on the + // right -- then recurse on the left node. + if (index.at(nodeDepth) == false) { + + // Make sure there is a value on the right. If not we put the 'null' hash (0) into that element. + if (this->right == NULL) { + witness.at(nodeDepth).resize(SHA256_BLOCK_SIZE * 8); + std::fill (witness.at(nodeDepth).begin(), witness.at(nodeDepth).end(), false); + } else { + this->right->getValue(witness.at(nodeDepth)); + //printVectorAsHex(witness.at(nodeDepth)); + } + + // Recurse on the left node + if (this->left) { + result = this->left->getWitness(index, witness); + } + } + + // If the index path leads to the right, we grab the hash value on the + // left -- then recurse on the right node. + if (index.at(nodeDepth) == true) { + this->left->getValue(witness.at(nodeDepth)); + + // Recurse on the right node + if (this->right) { + result = this->right->getWitness(index, witness); + } + } + + return result; + } + + bool + IncrementalMerkleNode::prune() + { + bool result = true; + + // If we're already pruned, return. + if (this->isPruned() == true) { + return true; + } + + // Check to see if this node is full. If so, delete the subtrees. + if (this->subtreeFull == true) { + if (this->left) { + delete this->left; + this->left = NULL; + } + + if (this->right) { + delete this->right; + this->right = NULL; + } + + this->subtreePruned = true; + } else { + // Node is not full. Recurse on left and right. + if (this->left) { + result &= this->left->prune(); + } + + if (this->right) { + result &= this->right->prune(); + } + } + + return result; + } + + void + IncrementalMerkleNode::updateHashValue() + { + // Take no action on leaves or pruned nodes. + if (this->isLeaf() || this->isPruned()) { + return; + } + + // Obtain the hash of the two subtrees and hash the + // concatenation of the two. + std::vector hash(SHA256_BLOCK_SIZE * 8); + std::vector zero(SHA256_BLOCK_SIZE * 8); + std::fill (zero.begin(), zero.end(), false); + + // The following code is ugly and should be refactored. It runs + // four special cases depending on whether left/right is NULL. + // It also ensures that the "hash" of (0 || 0) is 0. + if (this->left && !(this->right)) { + if (VectorIsZero(this->left->getValue())) { + hash = zero; + } else { + hashVectors(&ctx256, this->left->getValue(), zero, hash); + } + } else if (!(this->left) && this->right) { + if (VectorIsZero(this->right->getValue())) { + hash = zero; + } else { + hashVectors(&ctx256, zero, this->left->getValue(), hash); + } + } else if (this->left && this->right) { + if (VectorIsZero(this->left->getValue()) && VectorIsZero(this->right->getValue())) { + hash = zero; + } else { + hashVectors(&ctx256, this->left->getValue(), this->right->getValue(), hash); + } + } else { + hash = zero; + } + + this->value = hash; + } + + bool + IncrementalMerkleNode::checkIfNodeFull() + { + if (this->isPruned()) { + return true; + } + + if (this->left == NULL || this->right == NULL) { + return false; + } + + return (this->left->subtreeFull && this->right->subtreeFull); + } + + void + IncrementalMerkleNode::getCompactRepresentation(IncrementalMerkleTreeCompact &rep) const + { + // Do nothing at the bottom level + if (this->isLeaf()) { + return; + } + + // There's no content below us. We're done + if (!this->left) { + return; + } + + // If we have no right elements, don't include any hashes. Recurse to the left. + if (this->hasRightChildren() == false && this->left->isLeaf() == false && this->left->subtreeFull == false) { + rep.hashList.at(this->nodeDepth) = false; + this->left->getCompactRepresentation(rep); + return; + } + + // Otherwise: Add our left child hash to the tree. + rep.hashList.at(this->nodeDepth) = true; + std::vector hash(SHA256_BLOCK_SIZE, 0); + convertVectorToBytesVector(this->left->getValue(), hash); + rep.hashVec.push_back(hash); + + // If we have a right child, recurse to the right + if (this->hasRightChildren()) { + this->right->getCompactRepresentation(rep); + return; + } + + // We get here in one of the following cases: + // 1. Our left child is a leaf, and there's no right child. + // 2. Our left child is a full tree, and there's no right child. + + // We've gone right for the last time, now we go left until we reach the + // bottom. + for (uint32_t i = this->nodeDepth + 1; i < this->treeHeight; i++) { + rep.hashList.at(i) = false; + } + } + + bool + IncrementalMerkleNode::fromCompactRepresentation(IncrementalMerkleTreeCompact &rep, uint32_t pos) + { + bool result = false; + + // Do nothing at the bottom level + if (this->isLeaf()) { + return true; + } + + // If we have any subtrees (or this tree already has stuff in it), clean it out. + if (this->left) { + // XXX memory leak: left might have the only pointers to its heap + // allocated children! + delete this->left; + this->left = NULL; + } + if (this->right) { + // XXX memory leak: right might have the only pointers to its heap + // allocated children! + delete this->right; + this->right = NULL; + } + this->subtreeFull = this->subtreePruned = false; + + // If the hashList[nodeDepth] is true, insert the next hash into the left tree + // and mark it full AND pruned. Then recurse to the right. + if (rep.hashList.at(this->nodeDepth) == true) { + // Create a left node + this->left = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight); + + // Fill the left node with the value and mark it full/pruned + std::vector hash(SHA256_BLOCK_SIZE * 8, 0); + convertBytesVectorToVector(rep.hashVec.at(pos), hash); + this->left->value = hash; + this->left->subtreePruned = this->left->subtreeFull = true; + + // Create a right node and recurse on it (incrementing pos) + this->right = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight); + result = this->right->fromCompactRepresentation(rep, pos + 1); + } else if (this->nodeDepth < (this->treeHeight - 1)) { + // Otherwise -- + // * If we're about to create a leaf level, do nothing. + // * Else create a left node and recurse on it. + this->left = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight); + + // Otherwise recurse on the left node. Do not increment pos. + result = this->left->fromCompactRepresentation(rep, pos); + } + + // Update the hash value of this node + this->updateHashValue(); + + return result; + } + + IncrementalMerkleNode + IncrementalMerkleNode::operator=(const IncrementalMerkleNode &rhs) { + IncrementalMerkleNode dup(rhs); + return dup; + } + +} /* namespace libzerocash */ diff --git a/src/zerocash/IncrementalMerkleTree.h b/src/zerocash/IncrementalMerkleTree.h new file mode 100644 index 000000000..9d1ae1fed --- /dev/null +++ b/src/zerocash/IncrementalMerkleTree.h @@ -0,0 +1,140 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the classes IncrementalMerkleTreeCompact, + IncrementalMerkleNode, and IncrementalMerkleTree. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef INCREMENTALMERKLETREE_H_ +#define INCREMENTALMERKLETREE_H_ + +#include "utils/sha256.h" + +#include "Zerocash.h" +#include +#include +#include +#include + +#include "libsnark/common/data_structures/merkle_tree.hpp" + +namespace libzerocash { + +/******************* Incremental Merkle tree compact *************************/ + +/* This is a comapct way to represent an incremental merkle tree, where all full + * subtrees are replaced by their hashes. It contains just enough information + * that you can continue addding elements to the tree. + * + * This class can only be constructed by IncrementalMerkleTree, and after that, + * it is immutable. To act on a compact representation, it must first be + * de-compactified by loading it into an IncrementalMerkleTree. + */ +class IncrementalMerkleTreeCompact { + friend class IncrementalMerkleTree; + friend class IncrementalMerkleNode; +public: + uint32_t getHeight() { return this->treeHeight; } + + uint32_t getTreeHeight() { return treeHeight; } + std::vector< std::vector > const& getHashVec() { return hashVec; } + std::vector< bool > const& getHashList() { return hashList; } + + std::vector serialize() const; + static IncrementalMerkleTreeCompact deserialize(const std::vector& serialized); + +private: + IncrementalMerkleTreeCompact() : treeHeight(0) {} + uint32_t treeHeight; + std::vector< std::vector > hashVec; + std::vector< bool > hashList; +}; + +/********************* Incremental Merkle tree node **************************/ + +class IncrementalMerkleNode { +public: + SHA256_CTX_mod ctx256; + IncrementalMerkleNode* left; + IncrementalMerkleNode* right; + std::vector value; + uint32_t nodeDepth; + uint32_t treeHeight; + bool subtreeFull; + bool subtreePruned; + + IncrementalMerkleNode(uint32_t depth, uint32_t height); + IncrementalMerkleNode(const IncrementalMerkleNode& toCopy); + ~IncrementalMerkleNode(); + + // Methods + bool insertElement(const std::vector &hashV, std::vector &index); + bool getWitness(const std::vector &index, merkle_authentication_path &witness); + bool prune(); + void getCompactRepresentation(IncrementalMerkleTreeCompact &rep) const; + bool fromCompactRepresentation(IncrementalMerkleTreeCompact &rep, uint32_t pos); + + // Utility methods + bool isLeaf() const { return (nodeDepth == treeHeight); } + bool isPruned() const { return subtreePruned; } + bool hasFreeLeaves() const { return (!subtreeFull); } + bool hasRightChildren() const { if (!right) return false; return true; } + void getValue(std::vector &r) const { r = value; } + const std::vector& getValue() const { return value; } + + bool checkIfNodeFull(); + void updateHashValue(); + + IncrementalMerkleNode operator=(const IncrementalMerkleNode &rhs); +}; + +/************************ Incremental Merkle tree ****************************/ + +class IncrementalMerkleTree { +protected: + + IncrementalMerkleNode root; + uint32_t treeHeight; + +public: + IncrementalMerkleTree(uint32_t height = ZEROCASH_DEFAULT_TREE_SIZE); + IncrementalMerkleTree(std::vector< std::vector > &valueVector, uint32_t height); + IncrementalMerkleTree(IncrementalMerkleTreeCompact &compact); + + void setTo(const IncrementalMerkleTree &other) { + auto compact = other.getCompactRepresentation(); + fromCompactRepresentation(compact); + } + + bool insertElement(const std::vector &hashV, std::vector &index); + bool insertElement(const std::vector &hashV, std::vector &index); + bool insertVector(std::vector< std::vector > &valueVector); + bool getWitness(const std::vector &index, merkle_authentication_path &witness); + bool getRootValue(std::vector& r) const; + bool getRootValue(std::vector& r) const; + std::vectorgetRoot(); + bool prune(); + IncrementalMerkleTreeCompact getCompactRepresentation() const; + std::vector serialize() const { + auto compact = getCompactRepresentation(); + return compact.serialize(); + } + + static IncrementalMerkleTree deserialize(const std::vector& serialized) { + auto deserialized = IncrementalMerkleTreeCompact::deserialize(serialized); + return IncrementalMerkleTree(deserialized); + } + + bool fromCompactRepresentation(IncrementalMerkleTreeCompact &rep); + +}; + +} /* namespace libzerocash */ + +#endif /* INCREMENTALMERKLETREE_H_ */ + diff --git a/src/zerocash/LICENSE b/src/zerocash/LICENSE new file mode 100644 index 000000000..72dc60d84 --- /dev/null +++ b/src/zerocash/LICENSE @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/zerocash/Makefile b/src/zerocash/Makefile new file mode 100644 index 000000000..04965c3db --- /dev/null +++ b/src/zerocash/Makefile @@ -0,0 +1,161 @@ +OPTFLAGS = -march=native -mtune=native -O2 +CXXFLAGS += -g -Wall -Wextra -Wno-unused-parameter -std=c++11 -fPIC -Wno-unused-variable +LDFLAGS += -flto + +DEPSRC=depsrc +DEPINST=depinst + +LIBZEROCASH=libzerocash +UTILS=$(LIBZEROCASH)/utils +TESTUTILS=tests +LDLIBS += -L $(DEPINST)/lib -Wl,-rpath $(DEPINST)/lib -L . -lsnark -lgmpxx -lgmp + +ifeq ($(USE_MT),1) + LDLIBS += -lboost_system-mt + LDLIBS += -lboost_unit_test_framework-mt +else + LDLIBS += -lboost_system + LDLIBS += -lboost_unit_test_framework +endif + +LDLIBS += -lcrypto -lcryptopp -lz -ldl + +ifeq ($(LINK_RT),1) +LDLIBS += -lrt +endif + + +CXXFLAGS += -I $(DEPINST)/include -I $(DEPINST)/include/libsnark -I . -DUSE_ASM -DCURVE_ALT_BN128 + +LIBPATH = /usr/local/lib + +SRCS= \ + $(UTILS)/sha256.cpp \ + $(UTILS)/util.cpp \ + $(LIBZEROCASH)/IncrementalMerkleTree.cpp \ + $(LIBZEROCASH)/Address.cpp \ + $(LIBZEROCASH)/CoinCommitment.cpp \ + $(LIBZEROCASH)/Coin.cpp \ + $(LIBZEROCASH)/MintTransaction.cpp \ + $(LIBZEROCASH)/PourInput.cpp \ + $(LIBZEROCASH)/PourOutput.cpp \ + $(LIBZEROCASH)/PourProver.cpp \ + $(LIBZEROCASH)/PourTransaction.cpp \ + $(LIBZEROCASH)/ZerocashParams.cpp \ + $(TESTUTILS)/timer.cpp + +EXECUTABLES= \ + zerocash_pour_ppzksnark/tests/test_zerocash_pour_ppzksnark \ + zerocash_pour_ppzksnark/profiling/profile_zerocash_pour_gadget \ + tests/zerocashTest \ + tests/utilTest \ + tests/merkleTest \ + libzerocash/GenerateParamsForFiles + +OBJS=$(patsubst %.cpp,%.o,$(SRCS)) + +ifeq ($(MINDEPS),1) + CXXFLAGS += -DMINDEPS +else + LDLIBS += -lboost_program_options + LDLIBS += -lprocps +endif + +ifeq ($(LOWMEM),1) + CXXFLAGS += -DLOWMEM +endif + +ifeq ($(STATIC),1) + CXXFLAGS += -static -DSTATIC +endif + +ifeq ($(PROFILE_CURVE),1) + CXXFLAGS += -static -DPROFILE_CURVE +endif + +ifeq ($(MULTICORE),1) + # When running ./get-libsnark to prepare for this build, use: + # $ LIBSNARK_FLAGS='MULTICORE=1 STATIC=1' ./get-libsnark. + # If you're missing some static libraries, it may help to also add + # $ NO_PROCPS=1 ... ./get-libsnark + # and pass MINDEPS=1 to this makefile + # and run ./get-cryptopp to build the static cryptopp library. + CXXFLAGS += -static -fopenmp -DMULTICORE +endif + +all: $(EXECUTABLES) libzerocash.a + +cppdebug: CXXFLAGS += -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC +cppdebug: debug + +debug: CXXFLAGS += -DDEBUG -g -ggdb3 +debug: all + +noasserts: CXXFLAGS += -DNDEBUG -Wno-unused-variable -Wno-unused-but-set-variable +noasserts: all + +# In order to detect changes to #include dependencies. -MMD below generates a .d file for .cpp file. Include the .d file. +-include $(SRCS:.cpp=.d) + +$(OBJS) ${patsubst %,%.o,${EXECUTABLES}}: %.o: %.cpp + $(CXX) -o $@ $< -c -MMD $(CXXFLAGS) + +$(EXECUTABLES): %: %.o $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) + +libzerocash: $(OBJS) $(USER_OBJS) + @echo 'Building target: $@' + @echo 'Invoking: Cross G++ Linker' + $(CXX) -shared -o "libzerocash.so" $(OBJS) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) + @echo 'Finished building target: $@' + @echo 'Copying libzerocash.so' + sudo cp libzerocash.so $(LIBPATH)/libzerocash.so + sudo ldconfig + @echo 'Finished copying libzerocash.so' + @echo ' ' + +libzerocash.a: $(OBJS) $(USER_OBJS) + @echo 'Building target: $@' + @echo 'Invoking: Cross G++ Linker' + $(AR) rcvs $@ $(OBJS) + @echo 'Finished building target: $@' + #@echo 'Copying libzerocash.a' + #sudo cp libzerocash.a $(LIBPATH)/libzerocash.a + #sudo ldconfig + #@echo 'Finished copying libzerocash.a' + @echo ' ' + +test_library: %: tests/zerocashTest.o $(OBJS) + $(CXX) -o tests/$@ $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) -lzerocash + +banktest_library: %: bankTest.o $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) -lzerocash + +merkletest_library: %: merkleTest.o $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) -lzerocash + +.PHONY: clean install + +clean: + $(RM) \ + $(OBJS) \ + $(EXECUTABLES) \ + ${patsubst %,%.o,${EXECUTABLES}} \ + ${patsubst %,%.d,${EXECUTABLES}} \ + ${patsubst %.cpp,%.d,${SRCS}} \ + libzerocash.a \ + tests/test_library + + +HEADERS_SRC=$(shell find . -name '*.hpp' -o -name '*.tcc' -o -name '*.h') +HEADERS_DEST=$(patsubst %,$(PREFIX)/include/libzerocash/%,$(HEADERS_SRC)) + +$(HEADERS_DEST): $(PREFIX)/include/libzerocash/%: % + mkdir -p $(shell dirname $@) + cp $< $@ + +install: all $(HEADERS_DEST) + mkdir -p $(PREFIX)/lib + install -m 0755 libzerocash.a $(PREFIX)/lib/ + mkdir -p $(PREFIX)/bin + install -m 0755 -t $(PREFIX)/bin/ $(EXECUTABLES) diff --git a/src/zerocash/MintTransaction.cpp b/src/zerocash/MintTransaction.cpp new file mode 100644 index 000000000..7fb684419 --- /dev/null +++ b/src/zerocash/MintTransaction.cpp @@ -0,0 +1,75 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the class MintTransaction. + + See MintTransaction.h . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "Zerocash.h" +#include "MintTransaction.h" + +namespace libzerocash { + +MintTransaction::MintTransaction(): coinValue(0), internalCommitment(), externalCommitment() +{ } + +/** + * Creates a transaction minting the coin c. + * + * @param c the coin to mint. + */ +MintTransaction::MintTransaction(const Coin& c): coinValue(ZC_V_SIZE) +{ + convertIntToBytesVector(c.getValue(), this->coinValue); + + internalCommitment = c.getInternalCommitment(); + externalCommitment = c.getCoinCommitment(); +} + +/** + * Verify the correctness of a Mint transaction. + * + * @return true if correct, false otherwise. + */ +bool MintTransaction::verify() const{ + + // Check that the internal commitment is the right size + if (this->internalCommitment.size() != ZC_K_SIZE) { + return false; + } + + // The external commitment should formulated as: + // H( internalCommitment || 0^192 || coinValue) + // + // To check the structure of our proof we simply reconstruct + // a version of the external commitment and check that it's + // equal to the value we store. + // + // We use the constructor for CoinCommitment to do this. + + try { + CoinCommitment comp(this->coinValue, this->internalCommitment); + + return (comp == this->externalCommitment); + } catch (std::runtime_error) { + return false; + } + + return false; +} + +const CoinCommitmentValue& MintTransaction::getMintedCoinCommitmentValue() const{ + return this->externalCommitment.getCommitmentValue(); +} + +uint64_t MintTransaction::getMonetaryValue() const { + return convertBytesVectorToInt(this->coinValue); +} + +} /* namespace libzerocash */ diff --git a/src/zerocash/MintTransaction.h b/src/zerocash/MintTransaction.h new file mode 100644 index 000000000..0c1d316cf --- /dev/null +++ b/src/zerocash/MintTransaction.h @@ -0,0 +1,69 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the class MintTransaction. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef MINTTRANSACTION_H_ +#define MINTTRANSACTION_H_ + +#include "CoinCommitment.h" +#include "Coin.h" + +typedef std::vector CoinCommitmentValue; + +namespace libzerocash{ + +/***************************** Mint transaction ******************************/ + +class MintTransaction { +public: + MintTransaction(); + + /** + * Creates a transaction minting the provided coin. + * + * @param c the coin to mint. + */ + MintTransaction(const Coin& c); + + /** + * Verifies the MintTransaction. + * In particular, this checks that output coin commitment + * actually is to a coin of the claimed value. + * + * @return true if valid, false otherwise. + */ + bool verify() const; + + /** + *Gets the commitment to the coin that was minted by this transaction. + * + * @return the commitment + */ + const CoinCommitmentValue& getMintedCoinCommitmentValue() const; + + /** + * Gets the monetary value of the minted coin. + * + * @return the value + */ + uint64_t getMonetaryValue() const; + + +private: + std::vector coinValue; // coin value + std::vector internalCommitment; // "k" in paper notation + CoinCommitment externalCommitment; // "cm" in paper notation + + const CoinCommitment& getCoinCommitment() const { return this->externalCommitment; } +}; + +} /* namespace libzerocash */ + +#endif /* MINTTRANSACTION_H_ */ diff --git a/src/zerocash/PourInput.cpp b/src/zerocash/PourInput.cpp new file mode 100644 index 000000000..7601001d0 --- /dev/null +++ b/src/zerocash/PourInput.cpp @@ -0,0 +1,47 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the class PourInput. + + See PourInput.h . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "IncrementalMerkleTree.h" +#include "PourInput.h" + +namespace libzerocash { + +PourInput::PourInput(int tree_depth): old_coin(), merkle_index(), path() { + this->old_address = Address::CreateNewRandomAddress(); + + this->old_coin = Coin(this->old_address.getPublicAddress(), 0); + + // dummy merkle tree + IncrementalMerkleTree merkleTree(tree_depth); + + // commitment from coin + std::vector commitment(ZC_CM_SIZE * 8); + convertBytesVectorToVector(this->old_coin.getCoinCommitment().getCommitmentValue(), commitment); + + // insert commitment into the merkle tree + std::vector index; + merkleTree.insertElement(commitment, index); + + merkleTree.getWitness(index, this->path); + + this->merkle_index = 1; +} + +PourInput::PourInput(Coin old_coin, + Address old_address, + size_t merkle_index, + merkle_authentication_path path) : old_coin(old_coin), merkle_index(merkle_index), path(path) { + this->old_address = old_address; +}; + +} /* namespace libzerocash */ \ No newline at end of file diff --git a/src/zerocash/PourInput.h b/src/zerocash/PourInput.h new file mode 100644 index 000000000..2bb924c79 --- /dev/null +++ b/src/zerocash/PourInput.h @@ -0,0 +1,37 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the class PourInput. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef POURINPUT_H_ +#define POURINPUT_H_ + +#include "Coin.h" +#include "ZerocashParams.h" + +namespace libzerocash { + +class PourInput { +public: + PourInput(int tree_depth); + + PourInput(Coin old_coin, + Address old_address, + size_t merkle_index, + merkle_authentication_path path); + + Coin old_coin; + Address old_address; + size_t merkle_index; + merkle_authentication_path path; +}; + +} /* namespace libzerocash */ + +#endif /* POURINPUT_H_ */ \ No newline at end of file diff --git a/src/zerocash/PourOutput.cpp b/src/zerocash/PourOutput.cpp new file mode 100644 index 000000000..e4c53f857 --- /dev/null +++ b/src/zerocash/PourOutput.cpp @@ -0,0 +1,29 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the class PourOutput. + + See PourOutput.h . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "PourOutput.h" + +namespace libzerocash { + +PourOutput::PourOutput(uint64_t val) { + Address dummy_to_address = Address::CreateNewRandomAddress(); + + this->to_address = dummy_to_address.getPublicAddress(); + this->new_coin = Coin(dummy_to_address.getPublicAddress(), val); +} + +PourOutput::PourOutput(const Coin new_coin, + const PublicAddress to_address) : new_coin(new_coin), to_address(to_address) { +} + +} /* namespace libzerocash */ \ No newline at end of file diff --git a/src/zerocash/PourOutput.h b/src/zerocash/PourOutput.h new file mode 100644 index 000000000..82021baf4 --- /dev/null +++ b/src/zerocash/PourOutput.h @@ -0,0 +1,32 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the class PourOutput. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef POUROUTPUT_H_ +#define POUROUTPUT_H_ + +#include "Coin.h" +#include "ZerocashParams.h" + +namespace libzerocash { + +class PourOutput { +public: + PourOutput(uint64_t val); + PourOutput(const Coin new_coin, + const PublicAddress to_address); + + Coin new_coin; + PublicAddress to_address; +}; + +} /* namespace libzerocash */ + +#endif /* POUROUTPUT_H_ */ \ No newline at end of file diff --git a/src/zerocash/PourProver.cpp b/src/zerocash/PourProver.cpp new file mode 100644 index 000000000..d23f6a1a5 --- /dev/null +++ b/src/zerocash/PourProver.cpp @@ -0,0 +1,12 @@ +/** @file +***************************************************************************** + +Implementation of interfaces for the class PourProver. + +***************************************************************************** +* @author This file is part of libzerocash, developed by the Zerocash +* project and contributors (see AUTHORS). +* @copyright MIT license (see LICENSE file) +*****************************************************************************/ + +#include "PourProver.h" \ No newline at end of file diff --git a/src/zerocash/PourProver.h b/src/zerocash/PourProver.h new file mode 100644 index 000000000..b38c8c65e --- /dev/null +++ b/src/zerocash/PourProver.h @@ -0,0 +1,65 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the class PourProver. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef POURPROVER_H_ +#define POURPROVER_H_ + +#include "ZerocashParams.h" +#include "boost/array.hpp" +#include "PourTransaction.h" +#include "CoinCommitment.h" + +namespace libzerocash { + +class PourProver { +public: + static bool VerifyProof( + ZerocashParams& params, + const std::vector& pubkeyHash, + const std::vector& rt, + const uint64_t vpub_old, + const uint64_t vpub_new, + const boost::array, 2> serials, + const boost::array, 2> commitments, + const boost::array, 2> macs, + const std::string &zkSNARK + ) { + PourTransaction pourtx; + + pourtx.version = 1; + pourtx.publicOldValue.resize(8); + pourtx.publicNewValue.resize(8); + convertIntToBytesVector(vpub_old, pourtx.publicOldValue); + convertIntToBytesVector(vpub_new, pourtx.publicNewValue); + pourtx.serialNumber_1 = serials[0]; + pourtx.serialNumber_2 = serials[1]; + { + CoinCommitment cm; + cm.commitmentValue = commitments[0]; + pourtx.cm_1 = cm; + } + { + CoinCommitment cm; + cm.commitmentValue = commitments[1]; + pourtx.cm_2 = cm; + } + pourtx.MAC_1 = macs[0]; + pourtx.MAC_2 = macs[1]; + pourtx.zkSNARK = zkSNARK; + + return pourtx.verify(params, pubkeyHash, rt); + } +}; + + +} + +#endif /* POURPROVER_H_ */ diff --git a/src/zerocash/PourTransaction.cpp b/src/zerocash/PourTransaction.cpp new file mode 100644 index 000000000..6a32ca141 --- /dev/null +++ b/src/zerocash/PourTransaction.cpp @@ -0,0 +1,466 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the class PourTransaction. + + See PourTransaction.h . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +using CryptoPP::AutoSeededRandomPool; + +#include +using CryptoPP::ECP; +using CryptoPP::ECIES; + +#include +using CryptoPP::StringSource; +using CryptoPP::StringStore; +using CryptoPP::StringSink; +using CryptoPP::PK_EncryptorFilter; + +#include +#include +#include +#include +#include + +#include "Zerocash.h" +#include "PourTransaction.h" +#include "PourInput.h" +#include "PourOutput.h" + +#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" +#include "zerocash_pour_gadget.hpp" +#include "zerocash_pour_ppzksnark.hpp" + +namespace libzerocash { + +PourTransaction::PourTransaction(): cm_1(), cm_2() { + +} + +PourTransaction::PourTransaction(ZerocashParams& params, + const std::vector& pubkeyHash, + const MerkleRootType& rt, + std::vector inputs, + std::vector outputs, + uint64_t vpub_old, + uint64_t vpub_new + ) : + publicOldValue(ZC_V_SIZE), publicNewValue(ZC_V_SIZE), serialNumber_1(ZC_SN_SIZE), serialNumber_2(ZC_SN_SIZE), MAC_1(ZC_H_SIZE), MAC_2(ZC_H_SIZE) +{ + if (inputs.size() > 2 || outputs.size() > 2) { + throw std::length_error("Too many inputs or outputs specified"); + } + + while (inputs.size() < 2) { + // Push a dummy input of value 0. + inputs.push_back(PourInput(params.getTreeDepth())); + } + + while (outputs.size() < 2) { + // Push a dummy output of value 0. + outputs.push_back(PourOutput(0)); + } + + init(1, + params, + rt, + inputs[0].old_coin, + inputs[1].old_coin, + inputs[0].old_address, + inputs[1].old_address, + inputs[0].merkle_index, + inputs[1].merkle_index, + inputs[0].path, + inputs[1].path, + outputs[0].to_address, + outputs[1].to_address, + vpub_old, + vpub_new, + pubkeyHash, + outputs[0].new_coin, + outputs[1].new_coin); +} + +PourTransaction::PourTransaction(uint16_t version_num, + ZerocashParams& params, + const MerkleRootType& rt, + const Coin& c_1_old, + const Coin& c_2_old, + const Address& addr_1_old, + const Address& addr_2_old, + const size_t patMerkleIdx_1, + const size_t patMerkleIdx_2, + const merkle_authentication_path& patMAC_1, + const merkle_authentication_path& patMAC_2, + const PublicAddress& addr_1_new, + const PublicAddress& addr_2_new, + uint64_t v_pub_old, + uint64_t v_pub_new, + const std::vector& pubkeyHash, + const Coin& c_1_new, + const Coin& c_2_new) : + publicOldValue(ZC_V_SIZE), publicNewValue(ZC_V_SIZE), serialNumber_1(ZC_SN_SIZE), serialNumber_2(ZC_SN_SIZE), MAC_1(ZC_H_SIZE), MAC_2(ZC_H_SIZE) +{ + init(version_num, params, rt, c_1_old, c_2_old, addr_1_old, addr_2_old, patMerkleIdx_1, patMerkleIdx_2, + patMAC_1, patMAC_2, addr_1_new, addr_2_new, v_pub_old, v_pub_new, pubkeyHash, c_1_new, c_2_new); +} + +void PourTransaction::init(uint16_t version_num, + ZerocashParams& params, + const MerkleRootType& rt, + const Coin& c_1_old, + const Coin& c_2_old, + const Address& addr_1_old, + const Address& addr_2_old, + const size_t patMerkleIdx_1, + const size_t patMerkleIdx_2, + const merkle_authentication_path& patMAC_1, + const merkle_authentication_path& patMAC_2, + const PublicAddress& addr_1_new, + const PublicAddress& addr_2_new, + uint64_t v_pub_old, + uint64_t v_pub_new, + const std::vector& pubkeyHash, + const Coin& c_1_new, + const Coin& c_2_new) +{ + this->version = version_num; + + convertIntToBytesVector(v_pub_old, this->publicOldValue); + convertIntToBytesVector(v_pub_new, this->publicNewValue); + + this->cm_1 = c_1_new.getCoinCommitment(); + this->cm_2 = c_2_new.getCoinCommitment(); + + std::vector root_bv(ZC_ROOT_SIZE * 8); + std::vector addr_pk_new_1_bv(ZC_A_PK_SIZE * 8); + std::vector addr_pk_new_2_bv(ZC_A_PK_SIZE * 8); + std::vector addr_sk_old_1_bv(ZC_A_SK_SIZE * 8); + std::vector addr_sk_old_2_bv(ZC_A_SK_SIZE * 8); + std::vector rand_new_1_bv(ZC_R_SIZE * 8); + std::vector rand_new_2_bv(ZC_R_SIZE * 8); + std::vector rand_old_1_bv(ZC_R_SIZE * 8); + std::vector rand_old_2_bv(ZC_R_SIZE * 8); + std::vector nonce_new_1_bv(ZC_RHO_SIZE * 8); + std::vector nonce_new_2_bv(ZC_RHO_SIZE * 8); + std::vector nonce_old_1_bv(ZC_RHO_SIZE * 8); + std::vector nonce_old_2_bv(ZC_RHO_SIZE * 8); + std::vector val_new_1_bv(ZC_V_SIZE * 8); + std::vector val_new_2_bv(ZC_V_SIZE * 8); + std::vector val_old_pub_bv(ZC_V_SIZE * 8); + std::vector val_new_pub_bv(ZC_V_SIZE * 8); + std::vector val_old_1_bv(ZC_V_SIZE * 8); + std::vector val_old_2_bv(ZC_V_SIZE * 8); + std::vector cm_new_1_bv(ZC_CM_SIZE * 8); + std::vector cm_new_2_bv(ZC_CM_SIZE * 8); + + convertBytesVectorToVector(rt, root_bv); + + convertBytesVectorToVector(c_1_new.getCoinCommitment().getCommitmentValue(), cm_new_1_bv); + convertBytesVectorToVector(c_2_new.getCoinCommitment().getCommitmentValue(), cm_new_2_bv); + + convertBytesVectorToVector(addr_1_old.getPrivateAddress().getAddressSecret(), addr_sk_old_1_bv); + convertBytesVectorToVector(addr_2_old.getPrivateAddress().getAddressSecret(), addr_sk_old_2_bv); + + convertBytesVectorToVector(addr_1_new.getPublicAddressSecret(), addr_pk_new_1_bv); + convertBytesVectorToVector(addr_2_new.getPublicAddressSecret(), addr_pk_new_2_bv); + + convertBytesVectorToVector(c_1_old.getR(), rand_old_1_bv); + convertBytesVectorToVector(c_2_old.getR(), rand_old_2_bv); + + convertBytesVectorToVector(c_1_new.getR(), rand_new_1_bv); + convertBytesVectorToVector(c_2_new.getR(), rand_new_2_bv); + + convertBytesVectorToVector(c_1_old.getRho(), nonce_old_1_bv); + convertBytesVectorToVector(c_2_old.getRho(), nonce_old_2_bv); + + convertBytesVectorToVector(c_1_new.getRho(), nonce_new_1_bv); + convertBytesVectorToVector(c_2_new.getRho(), nonce_new_2_bv); + + std::vector v_old_1_conv(ZC_V_SIZE, 0); + convertIntToBytesVector(c_1_old.getValue(), v_old_1_conv); + libzerocash::convertBytesVectorToVector(v_old_1_conv, val_old_1_bv); + + std::vector v_old_2_conv(ZC_V_SIZE, 0); + convertIntToBytesVector(c_2_old.getValue(), v_old_2_conv); + libzerocash::convertBytesVectorToVector(v_old_2_conv, val_old_2_bv); + + std::vector v_new_1_conv(ZC_V_SIZE, 0); + convertIntToBytesVector(c_1_new.getValue(), v_new_1_conv); + libzerocash::convertBytesVectorToVector(v_new_1_conv, val_new_1_bv); + + std::vector v_new_2_conv(ZC_V_SIZE, 0); + convertIntToBytesVector(c_2_new.getValue(), v_new_2_conv); + libzerocash::convertBytesVectorToVector(v_new_2_conv, val_new_2_bv); + + convertBytesVectorToVector(this->publicOldValue, val_old_pub_bv); + convertBytesVectorToVector(this->publicNewValue, val_new_pub_bv); + + std::vector nonce_old_1(ZC_RHO_SIZE * 8); + copy(nonce_old_1_bv.begin(), nonce_old_1_bv.end(), nonce_old_1.begin()); + nonce_old_1.erase(nonce_old_1.end()-2, nonce_old_1.end()); + + nonce_old_1.insert(nonce_old_1.begin(), 1); + nonce_old_1.insert(nonce_old_1.begin(), 0); + + std::vector sn_internal_1; + concatenateVectors(addr_sk_old_1_bv, nonce_old_1, sn_internal_1); + std::vector sn_old_1_bv(ZC_SN_SIZE * 8); + hashVector(sn_internal_1, sn_old_1_bv); + + convertVectorToBytesVector(sn_old_1_bv, this->serialNumber_1); + + std::vector nonce_old_2(ZC_RHO_SIZE * 8); + copy(nonce_old_2_bv.begin(), nonce_old_2_bv.end(), nonce_old_2.begin()); + nonce_old_2.erase(nonce_old_2.end()-2, nonce_old_2.end()); + + nonce_old_2.insert(nonce_old_2.begin(), 1); + nonce_old_2.insert(nonce_old_2.begin(), 0); + + std::vector sn_internal_2; + concatenateVectors(addr_sk_old_2_bv, nonce_old_2, sn_internal_2); + std::vector sn_old_2_bv(ZC_SN_SIZE * 8); + hashVector(sn_internal_2, sn_old_2_bv); + + convertVectorToBytesVector(sn_old_2_bv, this->serialNumber_2); + + unsigned char h_S_bytes[ZC_H_SIZE]; + unsigned char pubkeyHash_bytes[ZC_H_SIZE]; + convertBytesVectorToBytes(pubkeyHash, pubkeyHash_bytes); + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, pubkeyHash_bytes, ZC_H_SIZE); + SHA256_Final(h_S_bytes, &sha256); + + std::vector h_S_bv(ZC_H_SIZE * 8); + convertBytesToVector(h_S_bytes, h_S_bv); + + std::vector h_S_internal1(ZC_H_SIZE * 8); + convertBytesToVector(h_S_bytes, h_S_internal1); + h_S_internal1.erase(h_S_internal1.end()-3, h_S_internal1.end()); + std::vector h_S_internal2 = h_S_internal1; + + h_S_internal1.insert(h_S_internal1.begin(), 0); + h_S_internal1.insert(h_S_internal1.begin(), 0); + h_S_internal1.insert(h_S_internal1.begin(), 1); + + h_S_internal2.insert(h_S_internal2.begin(), 1); + h_S_internal2.insert(h_S_internal2.begin(), 0); + h_S_internal2.insert(h_S_internal2.begin(), 1); + + std::vector MAC_1_internal; + concatenateVectors(addr_sk_old_1_bv, h_S_internal1, MAC_1_internal); + std::vector MAC_1_bv(ZC_H_SIZE * 8); + hashVector(MAC_1_internal, MAC_1_bv); + convertVectorToBytesVector(MAC_1_bv, this->MAC_1); + + std::vector MAC_2_internal; + concatenateVectors(addr_sk_old_2_bv, h_S_internal2, MAC_2_internal); + std::vector MAC_2_bv(ZC_H_SIZE * 8); + hashVector(MAC_2_internal, MAC_2_bv); + convertVectorToBytesVector(MAC_2_bv, this->MAC_2); + + if(this->version > 0){ + auto proofObj = zerocash_pour_ppzksnark_prover(params.getProvingKey(), + { patMAC_1, patMAC_2 }, + { patMerkleIdx_1, patMerkleIdx_2 }, + root_bv, + { addr_pk_new_1_bv, addr_pk_new_2_bv }, + { addr_sk_old_1_bv, addr_sk_old_2_bv }, + { rand_new_1_bv, rand_new_2_bv }, + { rand_old_1_bv, rand_old_2_bv }, + { nonce_new_1_bv, nonce_new_2_bv }, + { nonce_old_1_bv, nonce_old_2_bv }, + { val_new_1_bv, val_new_2_bv }, + val_old_pub_bv, + val_new_pub_bv, + { val_old_1_bv, val_old_2_bv }, + h_S_bv); + + std::stringstream ss; + ss << proofObj; + this->zkSNARK = ss.str(); + } else { + this->zkSNARK = std::string(1235,'A'); + } + + unsigned char val_new_1_bytes[ZC_V_SIZE]; + unsigned char val_new_2_bytes[ZC_V_SIZE]; + unsigned char nonce_new_1_bytes[ZC_RHO_SIZE]; + unsigned char nonce_new_2_bytes[ZC_RHO_SIZE]; + unsigned char rand_new_1_bytes[ZC_R_SIZE]; + unsigned char rand_new_2_bytes[ZC_R_SIZE]; + + convertVectorToBytes(val_new_1_bv, val_new_1_bytes); + convertVectorToBytes(val_new_2_bv, val_new_2_bytes); + convertVectorToBytes(rand_new_1_bv, rand_new_1_bytes); + convertVectorToBytes(rand_new_2_bv, rand_new_2_bytes); + convertVectorToBytes(nonce_new_1_bv, nonce_new_1_bytes); + convertVectorToBytes(nonce_new_2_bv, nonce_new_2_bytes); + + std::string val_new_1_string(val_new_1_bytes, val_new_1_bytes + ZC_V_SIZE); + std::string val_new_2_string(val_new_2_bytes, val_new_2_bytes + ZC_V_SIZE); + std::string nonce_new_1_string(nonce_new_1_bytes, nonce_new_1_bytes + ZC_RHO_SIZE); + std::string nonce_new_2_string(nonce_new_2_bytes, nonce_new_2_bytes + ZC_RHO_SIZE); + std::string rand_new_1_string(rand_new_1_bytes, rand_new_1_bytes + ZC_R_SIZE); + std::string rand_new_2_string(rand_new_2_bytes, rand_new_2_bytes + ZC_R_SIZE); + + AutoSeededRandomPool prng_1; + AutoSeededRandomPool prng_2; + + ECIES::PublicKey publicKey_1; + publicKey_1.Load(StringStore(addr_1_new.getEncryptionPublicKey()).Ref()); + ECIES::Encryptor encryptor_1(publicKey_1); + + std::vector ciphertext_1_internals; + ciphertext_1_internals.insert(ciphertext_1_internals.end(), c_1_new.coinValue.begin(), c_1_new.coinValue.end()); + ciphertext_1_internals.insert(ciphertext_1_internals.end(), c_1_new.r.begin(), c_1_new.r.end()); + ciphertext_1_internals.insert(ciphertext_1_internals.end(), c_1_new.rho.begin(), c_1_new.rho.end()); + + assert(ciphertext_1_internals.size() == (ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE)); + + byte gEncryptBuf[encryptor_1.CiphertextLength(ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE)]; + + encryptor_1.Encrypt(prng_1, &ciphertext_1_internals[0], ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE, gEncryptBuf); + + std::string C_1_string(gEncryptBuf, gEncryptBuf + sizeof gEncryptBuf / sizeof gEncryptBuf[0]); + this->ciphertext_1 = C_1_string; + + ECIES::PublicKey publicKey_2; + publicKey_2.Load(StringStore(addr_2_new.getEncryptionPublicKey()).Ref()); + ECIES::Encryptor encryptor_2(publicKey_2); + + std::vector ciphertext_2_internals; + ciphertext_2_internals.insert(ciphertext_2_internals.end(), c_2_new.coinValue.begin(), c_2_new.coinValue.end()); + ciphertext_2_internals.insert(ciphertext_2_internals.end(), c_2_new.r.begin(), c_2_new.r.end()); + ciphertext_2_internals.insert(ciphertext_2_internals.end(), c_2_new.rho.begin(), c_2_new.rho.end()); + + assert(ciphertext_2_internals.size() == (ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE)); + + byte gEncryptBuf_2[encryptor_2.CiphertextLength(ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE)]; + + encryptor_2.Encrypt(prng_1, &ciphertext_2_internals[0], ZC_V_SIZE + ZC_R_SIZE + ZC_RHO_SIZE, gEncryptBuf_2); + + std::string C_2_string(gEncryptBuf_2, gEncryptBuf_2 + sizeof gEncryptBuf_2 / sizeof gEncryptBuf_2[0]); + this->ciphertext_2 = C_2_string; +} + +bool PourTransaction::verify(ZerocashParams& params, + const std::vector &pubkeyHash, + const MerkleRootType &merkleRoot) const +{ + if(this->version == 0){ + return true; + } + + zerocash_pour_proof proof_SNARK; + std::stringstream ss; + ss.str(this->zkSNARK); + ss >> proof_SNARK; + + if (merkleRoot.size() != ZC_ROOT_SIZE) { return false; } + if (pubkeyHash.size() != ZC_H_SIZE) { return false; } + if (this->serialNumber_1.size() != ZC_SN_SIZE) { return false; } + if (this->serialNumber_2.size() != ZC_SN_SIZE) { return false; } + if (this->publicOldValue.size() != ZC_V_SIZE) { return false; } + if (this->publicNewValue.size() != ZC_V_SIZE) { return false; } + if (this->MAC_1.size() != ZC_H_SIZE) { return false; } + if (this->MAC_2.size() != ZC_H_SIZE) { return false; } + + std::vector root_bv(ZC_ROOT_SIZE * 8); + std::vector sn_old_1_bv(ZC_SN_SIZE * 8); + std::vector sn_old_2_bv(ZC_SN_SIZE * 8); + std::vector cm_new_1_bv(ZC_CM_SIZE * 8); + std::vector cm_new_2_bv(ZC_CM_SIZE * 8); + std::vector val_old_pub_bv(ZC_V_SIZE * 8); + std::vector val_new_pub_bv(ZC_V_SIZE * 8); + std::vector MAC_1_bv(ZC_H_SIZE * 8); + std::vector MAC_2_bv(ZC_H_SIZE * 8); + + convertBytesVectorToVector(merkleRoot, root_bv); + convertBytesVectorToVector(this->serialNumber_1, sn_old_1_bv); + convertBytesVectorToVector(this->serialNumber_2, sn_old_2_bv); + convertBytesVectorToVector(this->cm_1.getCommitmentValue(), cm_new_1_bv); + convertBytesVectorToVector(this->cm_2.getCommitmentValue(), cm_new_2_bv); + convertBytesVectorToVector(this->publicOldValue, val_old_pub_bv); + convertBytesVectorToVector(this->publicNewValue, val_new_pub_bv); + convertBytesVectorToVector(this->MAC_1, MAC_1_bv); + convertBytesVectorToVector(this->MAC_2, MAC_2_bv); + + unsigned char h_S_bytes[ZC_H_SIZE]; + unsigned char pubkeyHash_bytes[ZC_H_SIZE]; + convertBytesVectorToBytes(pubkeyHash, pubkeyHash_bytes); + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, pubkeyHash_bytes, ZC_H_SIZE); + SHA256_Final(h_S_bytes, &sha256); + + std::vector h_S_internal(ZC_H_SIZE * 8); + convertBytesToVector(h_S_bytes, h_S_internal); + h_S_internal.erase(h_S_internal.end()-2, h_S_internal.end()); + h_S_internal.insert(h_S_internal.begin(), 0); + h_S_internal.insert(h_S_internal.begin(), 1); + + std::vector h_S_bv(ZC_H_SIZE * 8); + convertBytesToVector(h_S_bytes, h_S_bv); + + bool snark_result = zerocash_pour_ppzksnark_verifier(params.getVerificationKey(), + root_bv, + { sn_old_1_bv, sn_old_2_bv }, + { cm_new_1_bv, cm_new_2_bv }, + val_old_pub_bv, + val_new_pub_bv, + h_S_bv, + { MAC_1_bv, MAC_2_bv }, + proof_SNARK); + + return snark_result; +} + +const std::vector& PourTransaction::getSpentSerial1() const{ + return this->serialNumber_1; +} + +const std::vector& PourTransaction::getSpentSerial2() const{ + return this->serialNumber_2; +} + +const std::string& PourTransaction::getCiphertext1() const { + return this->ciphertext_1; +} + +const std::string& PourTransaction::getCiphertext2() const { + return this->ciphertext_2; +} + +/** + * Returns the hash of the first new coin commitment output by this Pour. + */ +const CoinCommitmentValue& PourTransaction::getNewCoinCommitmentValue1() const{ + return this->cm_1.getCommitmentValue(); +} + +/** + * Returns the hash of the second new coin commitment output by this Pour. + */ +const CoinCommitmentValue& PourTransaction::getNewCoinCommitmentValue2() const{ + return this->cm_2.getCommitmentValue(); +} + +uint64_t PourTransaction::getPublicValueIn() const{ + return convertBytesVectorToInt(this->publicOldValue); +} + +uint64_t PourTransaction::getPublicValueOut() const{ + return convertBytesVectorToInt(this->publicNewValue); +} + +} /* namespace libzerocash */ diff --git a/src/zerocash/PourTransaction.h b/src/zerocash/PourTransaction.h new file mode 100644 index 000000000..dfd406041 --- /dev/null +++ b/src/zerocash/PourTransaction.h @@ -0,0 +1,198 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the class PourTransaction. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef POURTRANSACTION_H_ +#define POURTRANSACTION_H_ + +#include "Coin.h" +#include "ZerocashParams.h" +#include "Zerocash.h" +#include "PourInput.h" +#include "PourOutput.h" +#include +#include + +#include + +typedef std::vector CoinCommitmentValue; + +namespace libzerocash { + +/***************************** Pour transaction ******************************/ + +class PourTransaction { +friend class PourProver; +public: + PourTransaction(); + PourTransaction(ZerocashParams& params, + const std::vector& pubkeyHash, + const MerkleRootType& rt, + const std::vector inputs, + const std::vector outputs, + uint64_t vpub_old, + uint64_t vpub_new + ); + /** + * Generates a transaction pouring the funds in two existing coins into two new coins and optionally + * converting some of those funds back into the base currency. + * + * + * @param version_num the version of the transaction to create + * @param params the cryptographic parameters used to generate the proofs. + * @param root the root of the merkle tree containing the two existing coins + * @param c_1_old the first existing coin + * @param c_2_old the second existing coin + * @param addr_1_old the address the first coin was paid to + * @param addr_2_old the address the second coin was paid to + * @param path_1 path showing that first coin is in the merkle tree rooted at root + * @param path_2 path showing that the second coin is n the merkle tree rooted at root + * @param addr_1_new the public address to pay the first new coin to + * @param addr_2_new the public address to pay the second new coin to + * @param v_pub the amount of funds to convert back to the base currency + * @param pubkeyHash the hash of a public key to bind into the transaction + * @param c_1_new the first of the new coins the funds are being poured into + * @param c_2_new the second of the new coins the funds are being poured into + */ + PourTransaction(uint16_t version_num, + ZerocashParams& params, + const MerkleRootType& roott, + const Coin& c_1_old, + const Coin& c_2_old, + const Address& addr_1_old, + const Address& addr_2_old, + const size_t patMerkleIdx_1, + const size_t patMerkleIdx_2, + const merkle_authentication_path& path_1, + const merkle_authentication_path& path_2, + const PublicAddress& addr_1_new, + const PublicAddress& addr_2_new, + uint64_t v_pub_in, + uint64_t v_pub_out, + const std::vector& pubkeyHash, + const Coin& c_1_new, + const Coin& c_2_new); + + void init(uint16_t version_num, + ZerocashParams& params, + const MerkleRootType& roott, + const Coin& c_1_old, + const Coin& c_2_old, + const Address& addr_1_old, + const Address& addr_2_old, + const size_t patMerkleIdx_1, + const size_t patMerkleIdx_2, + const merkle_authentication_path& path_1, + const merkle_authentication_path& path_2, + const PublicAddress& addr_1_new, + const PublicAddress& addr_2_new, + uint64_t v_pub_in, + uint64_t v_pub_out, + const std::vector& pubkeyHash, + const Coin& c_1_new, + const Coin& c_2_new); + + /** + * Verifies the pour transaction. + * + * @param params the cryptographic parameters used to verify the proofs. + * @param pubkeyHash the hash of a public key that we verify is bound to the transaction + * @param merkleRoot the root of the merkle tree the coins were in. + * @return ture if correct, false otherwise. + */ + bool verify(ZerocashParams& params, + const std::vector &pubkeyHash, + const MerkleRootType &merkleRoot) const; + + const std::vector& getSpentSerial1() const; + const std::vector& getSpentSerial2() const; + const std::string& getCiphertext1() const; + const std::string& getCiphertext2() const; + + /** + * Returns the hash of the first new coin generated by this Pour. + * + * @return the coin hash + */ + const CoinCommitmentValue& getNewCoinCommitmentValue1() const; + + /** + * Returns the hash of the second new coin generated by this Pour. + * + * @return the coin hash + */ + const CoinCommitmentValue& getNewCoinCommitmentValue2() const; + + uint64_t getPublicValueIn() const; + + uint64_t getPublicValueOut() const; + + std::string unpack(boost::array, 2>& serials, + boost::array, 2>& commitments, + boost::array, 2>& macs, + boost::array& ciphertexts + ) const { + serials[0] = this->serialNumber_1; + serials[1] = this->serialNumber_2; + commitments[0] = this->cm_1.getCommitmentValue(); + commitments[1] = this->cm_2.getCommitmentValue(); + macs[0] = this->MAC_1; + macs[1] = this->MAC_2; + ciphertexts[0] = this->ciphertext_1; + ciphertexts[1] = this->ciphertext_2; + + return this->zkSNARK; + } + + // just hashes a few fields to see if integrity is correct. + // useful for debugging since there's such bad error handling + // currently + void debug_print() { + #define DEBUG_PRINT_POUR_FIELD(X, NAME) {\ + std::hash h; \ + std::cout << NAME << ": " << h(std::string(X.begin(), X.end())) << std::endl;\ + } + + DEBUG_PRINT_POUR_FIELD(publicOldValue, "publicOldValue"); + DEBUG_PRINT_POUR_FIELD(publicNewValue, "publicNewValue"); + DEBUG_PRINT_POUR_FIELD(serialNumber_1, "serialNumber_1"); + DEBUG_PRINT_POUR_FIELD(serialNumber_2, "serialNumber_2"); + { + auto v = cm_1.getCommitmentValue(); + DEBUG_PRINT_POUR_FIELD(v, "cm_1"); + } + { + auto v = cm_2.getCommitmentValue(); + DEBUG_PRINT_POUR_FIELD(v, "cm_2"); + } + DEBUG_PRINT_POUR_FIELD(MAC_1, "MAC_1"); + DEBUG_PRINT_POUR_FIELD(MAC_2, "MAC_2"); + + } + +private: + + std::vector publicOldValue; // public input value of the Pour transaction + std::vector publicNewValue; // public output value of the Pour transaction + std::vector serialNumber_1; // serial number of input (old) coin #1 + std::vector serialNumber_2; // serial number of input (old) coin #1 + CoinCommitment cm_1; // coin commitment for output coin #1 + CoinCommitment cm_2; // coin commitment for output coin #2 + std::vector MAC_1; // first MAC (h_1 in paper notation) + std::vector MAC_2; // second MAC (h_2 in paper notation) + std::string ciphertext_1; // ciphertext #1 + std::string ciphertext_2; // ciphertext #2 + std::string zkSNARK; // contents of the zkSNARK proof itself + uint16_t version; // version for the Pour transaction +}; + +} /* namespace libzerocash */ + +#endif /* POURTRANSACTION_H_ */ diff --git a/src/zerocash/Zerocash.h b/src/zerocash/Zerocash.h new file mode 100644 index 000000000..e77bf12b7 --- /dev/null +++ b/src/zerocash/Zerocash.h @@ -0,0 +1,64 @@ +/** @file + ***************************************************************************** + + Declaration of exceptions and constants for Zerocash. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ZEROCASH_H_ +#define ZEROCASH_H_ + +#include +#include + +/* The version of this library. */ +#define ZEROCASH_VERSION_STRING "0.1" +#define ZEROCASH_VERSION_INT 1 +#define ZEROCASH_PROTOCOL_VERSION "1" +#define ZEROCASH_DEFAULT_TREE_SIZE 64 + +#define ZC_A_PK_SIZE 32 +#define ZC_PK_ENC_SIZE 311 +#define ZC_SIG_PK_SIZE 32 +#define ZC_ADDR_PK_SIZE (ZC_A_PK_SIZE+ZC_PK_ENC_SIZE) + +#define ZC_A_SK_SIZE 32 +#define ZC_SK_ENC_SIZE 287 +#define ZC_ADDR_SK_SIZE (ZC_A_SK_SIZE+ZC_SK_ENC_SIZE) + +#define ZC_V_SIZE 8 +#define ZC_RHO_SIZE 32 +#define ZC_R_SIZE 48 +#define ZC_S_SIZE 0 +#define ZC_K_SIZE 32 +#define ZC_CM_SIZE 32 +#define ZC_COIN_SIZE (ZC_ADDR_PK_SIZE+ZC_V_SIZE+ZC_RHO_SIZE+ZC_R_SIZE+ZC_S_SIZE+ZC_CM_SIZE) +#define ZC_TX_MINT_SIZE (ZC_CM_SIZE+ZC_V_SIZE+ZC_K_SIZE+ZC_S_SIZE) + +#define ZC_ROOT_SIZE 32 +#define ZC_SN_SIZE 32 +#define ZC_PK_SIG_SIZE 66 +#define ZC_H_SIZE 32 +#define ZC_POUR_PROOF_SIZE 288 +#define ZC_C_SIZE 173 +#define ZC_SIGMA_SIZE 72 +#define ZC_TX_POUR_SIZE (ZC_ROOT_SIZE+(2*ZC_SN_SIZE)+(2*ZC_CM_SIZE)+ZC_V_SIZE+ZC_PK_SIG_SIZE+(2*ZC_H_SIZE)+ZC_POUR_PROOF_SIZE+(2*ZC_C_SIZE)+ZC_SIGMA_SIZE) + +#define SNARK + +typedef std::vector MerkleRootType; + +namespace libsnark { +}; + +namespace libzerocash { + using namespace libsnark; +}; + +#include "zerocash/utils/util.h" + +#endif /* ZEROCASH_H_ */ diff --git a/src/zerocash/ZerocashParams.cpp b/src/zerocash/ZerocashParams.cpp new file mode 100644 index 000000000..aac89ef9b --- /dev/null +++ b/src/zerocash/ZerocashParams.cpp @@ -0,0 +1,180 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the class ZerocashParams. + + See ZerocashParams.h . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +#include + +#include "Zerocash.h" +#include "ZerocashParams.h" + +static void throw_missing_param_file_exception(std::string paramtype, std::string path) { + /* paramtype should be either "proving" or "verifying". */ + const char* tmpl = ("Could not open %s key file: %s\n" + "Please refer to user documentation for installing this file."); + throw std::runtime_error((boost::format(tmpl) % paramtype % path).str()); +} + +namespace libzerocash { + +int ZerocashParams::getTreeDepth() +{ + return treeDepth; +} + +zerocash_pour_keypair ZerocashParams::GenerateNewKeyPair(const unsigned int tree_depth) +{ + libzerocash::ZerocashParams::zerocash_pp::init_public_params(); + libzerocash::zerocash_pour_keypair kp_v1 = + libzerocash::zerocash_pour_ppzksnark_generator( + libzerocash::ZerocashParams::numPourInputs, + libzerocash::ZerocashParams::numPourOutputs, + tree_depth + ); + return kp_v1; +} + +void ZerocashParams::SaveProvingKeyToFile(const zerocash_pour_proving_key* p_pk_1, std::string path) +{ + std::stringstream ssProving; + ssProving << p_pk_1->r1cs_pk; + std::ofstream pkFilePtr; + pkFilePtr.open(path, std::ios::binary); + ssProving.rdbuf()->pubseekpos(0, std::ios_base::out); + pkFilePtr << ssProving.rdbuf(); + pkFilePtr.flush(); + pkFilePtr.close(); +} + + +void ZerocashParams::SaveVerificationKeyToFile(const zerocash_pour_verification_key* p_vk_1, std::string path) +{ + std::stringstream ssVerification; + ssVerification << p_vk_1->r1cs_vk; + std::ofstream vkFilePtr; + vkFilePtr.open(path, std::ios::binary); + ssVerification.rdbuf()->pubseekpos(0, std::ios_base::out); + vkFilePtr << ssVerification.rdbuf(); + vkFilePtr.flush(); + vkFilePtr.close(); +} + +zerocash_pour_proving_key ZerocashParams::LoadProvingKeyFromFile(std::string path, const unsigned int tree_depth) +{ + std::stringstream ssProving; + std::ifstream fileProving(path, std::ios::binary); + + if(!fileProving.is_open()) { + throw_missing_param_file_exception("proving", path); + } + + ssProving << fileProving.rdbuf(); + fileProving.close(); + + ssProving.rdbuf()->pubseekpos(0, std::ios_base::in); + + r1cs_ppzksnark_proving_key pk_temp; + ssProving >> pk_temp; + + return zerocash_pour_proving_key( + libzerocash::ZerocashParams::numPourInputs, + libzerocash::ZerocashParams::numPourOutputs, + tree_depth, + std::move(pk_temp) + ); +} + +zerocash_pour_verification_key ZerocashParams::LoadVerificationKeyFromFile(std::string path, const unsigned int tree_depth) +{ + std::stringstream ssVerification; + std::ifstream fileVerification(path, std::ios::binary); + + if(!fileVerification.is_open()) { + throw_missing_param_file_exception("verification", path); + } + + ssVerification << fileVerification.rdbuf(); + fileVerification.close(); + + ssVerification.rdbuf()->pubseekpos(0, std::ios_base::in); + + r1cs_ppzksnark_verification_key vk_temp; + ssVerification >> vk_temp; + + return zerocash_pour_verification_key( + libzerocash::ZerocashParams::numPourInputs, + libzerocash::ZerocashParams::numPourOutputs, + std::move(vk_temp) + ); +} + +ZerocashParams::ZerocashParams( + const unsigned int tree_depth, + zerocash_pour_keypair *keypair +) : + treeDepth(tree_depth) +{ + params_pk_v1 = new zerocash_pour_proving_key(keypair->pk); + params_vk_v1 = new zerocash_pour_verification_key(keypair->vk); +} + +ZerocashParams::ZerocashParams( + const unsigned int tree_depth, + zerocash_pour_proving_key* p_pk_1, + zerocash_pour_verification_key* p_vk_1 +) : + treeDepth(tree_depth) +{ + assert(p_pk_1 != NULL || p_vk_1 != NULL); + + if (p_pk_1 == NULL) { + params_pk_v1 = NULL; + } else { + params_pk_v1 = new zerocash_pour_proving_key(*p_pk_1); + } + + if (p_vk_1 == NULL) { + params_vk_v1 = NULL; + } else { + params_vk_v1 = new zerocash_pour_verification_key(*p_vk_1); + } +} + +ZerocashParams::~ZerocashParams() +{ + if (params_pk_v1 != NULL) { + delete params_pk_v1; + } + if (params_vk_v1 != NULL) { + delete params_vk_v1; + } +} + +const zerocash_pour_proving_key& ZerocashParams::getProvingKey() +{ + if (params_pk_v1 != NULL) { + return *params_pk_v1; + } else { + throw std::runtime_error("Pour proving key not set."); + } +} + +const zerocash_pour_verification_key& ZerocashParams::getVerificationKey() +{ + if (params_vk_v1 != NULL) { + return *params_vk_v1; + } else { + throw std::runtime_error("Pour verification key not set."); + } +} + +} /* namespace libzerocash */ diff --git a/src/zerocash/ZerocashParams.h b/src/zerocash/ZerocashParams.h new file mode 100644 index 000000000..7b38226fa --- /dev/null +++ b/src/zerocash/ZerocashParams.h @@ -0,0 +1,60 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the class ZerocashParams. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef PARAMS_H_ +#define PARAMS_H_ + +#include "Zerocash.h" +#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" +#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" +#include "zerocash_pour_ppzksnark.hpp" + +namespace libzerocash { + +class ZerocashParams { + +public: + typedef default_r1cs_ppzksnark_pp zerocash_pp; + + ZerocashParams( + const unsigned int tree_depth, + zerocash_pour_keypair *keypair + ); + + ZerocashParams( + const unsigned int tree_depth, + zerocash_pour_proving_key* p_pk_1, + zerocash_pour_verification_key* p_vk_1 + ); + + const zerocash_pour_proving_key& getProvingKey(); + const zerocash_pour_verification_key& getVerificationKey(); + int getTreeDepth(); + ~ZerocashParams(); + + static const size_t numPourInputs = 2; + static const size_t numPourOutputs = 2; + + static zerocash_pour_keypair GenerateNewKeyPair(const unsigned int tree_depth); + + static void SaveProvingKeyToFile(const zerocash_pour_proving_key* p_pk_1, std::string path); + static void SaveVerificationKeyToFile(const zerocash_pour_verification_key* p_vk_1, std::string path); + static zerocash_pour_proving_key LoadProvingKeyFromFile(std::string path, const unsigned int tree_depth); + static zerocash_pour_verification_key LoadVerificationKeyFromFile(std::string path, const unsigned int tree_depth); +private: + int treeDepth; + zerocash_pour_proving_key* params_pk_v1; + zerocash_pour_verification_key* params_vk_v1; +}; + +} /* namespace libzerocash */ + +#endif /* PARAMS_H_ */ diff --git a/src/zerocash/profiling/profile_zerocash_pour_gadget.cpp b/src/zerocash/profiling/profile_zerocash_pour_gadget.cpp new file mode 100644 index 000000000..0aecd15af --- /dev/null +++ b/src/zerocash/profiling/profile_zerocash_pour_gadget.cpp @@ -0,0 +1,39 @@ +/** @file + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" +#include "libsnark/common/profiling.hpp" +#include "zerocash_pour_ppzksnark/zerocash_pour_gadget.hpp" + +using namespace libsnark; + +int main(int argc, const char* argv[]) +{ + start_profiling(); + default_r1cs_ppzksnark_pp::init_public_params(); +#ifndef DEBUG + printf("this program needs to be compiled with constraint annotations (make debug/make cppdebug)\n"); + return 2; +#else + if (argc != 4) + { + printf("usage: %s num_old_coins num_new_coins tree_depth\n", argv[0]); + return 1; + } + + + int num_old_coins = atoi(argv[1]); + int num_new_coins = atoi(argv[2]); + int tree_depth = atoi(argv[3]); + protoboard > pb; + libzerocash::zerocash_pour_gadget > g(pb, num_old_coins, num_new_coins, tree_depth, "pour"); + g.generate_r1cs_constraints(); + for (auto it : pb.get_constraint_system().constraint_annotations) + { + printf("%s\n", it.second.c_str()); + } +#endif +} diff --git a/src/zerocash/tests/full-test-suite.sh b/src/zerocash/tests/full-test-suite.sh new file mode 100755 index 000000000..e721a9c71 --- /dev/null +++ b/src/zerocash/tests/full-test-suite.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -eu + +SUITE_EXIT_STATUS=0 +REPOROOT="$(readlink -f "$(dirname "$0")"/../)" + +function run_test_phase +{ + echo "===== BEGIN: $*" + set +e + eval "$@" + if [ $? -eq 0 ] + then + echo "===== PASSED: $*" + else + echo "===== FAILED: $*" + SUITE_EXIT_STATUS=1 + fi + set -e +} + +cd "${REPOROOT}" + +# Test phases: +run_test_phase "${REPOROOT}/tests/utilTest" +run_test_phase "${REPOROOT}/tests/zerocashTest" +run_test_phase "${REPOROOT}/tests/merkleTest" +run_test_phase "${REPOROOT}/zerocash_pour_ppzksnark/tests/test_zerocash_pour_ppzksnark" + +exit $SUITE_EXIT_STATUS diff --git a/src/zerocash/tests/merkleTest.cpp b/src/zerocash/tests/merkleTest.cpp new file mode 100644 index 000000000..9be66cc9f --- /dev/null +++ b/src/zerocash/tests/merkleTest.cpp @@ -0,0 +1,199 @@ +/** @file + ***************************************************************************** + + Test for Merkle tree. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "zerocash/IncrementalMerkleTree.h" + +#include +#include + +#define BOOST_TEST_MODULE merkleTest +#include + +using namespace libzerocash; +using namespace std; + +void constructNonzeroTestVector(std::vector< std::vector > &values, uint32_t size) +{ + values.resize(0); + std::vector dummy; + dummy.resize(256); + dummy[0] = true; + + for (uint32_t i = 0; i < size; i++) + { + values.push_back(dummy); + } +} + +void constructZeroTestVector(std::vector< std::vector > &values, uint32_t size) +{ + values.resize(0); + std::vector dummy; + dummy.resize(256); + + for (uint32_t i = 0; i < size; i++) + { + values.push_back(dummy); + } +} + +BOOST_AUTO_TEST_CASE( testRootOfTreeOfZerosIsZero ) { + IncrementalMerkleTree incTree; + std::vector< std::vector > values; + std::vector actual_root; + + constructZeroTestVector(values, 2); + + // Create an IncrementalMerkleTree over the values. + if (incTree.insertVector(values) == false) { + BOOST_ERROR("Could not insert into the tree."); + } + incTree.prune(); + incTree.getRootValue(actual_root); + + std::vector expected_root(32*8, 0); + BOOST_CHECK( expected_root == actual_root ); +} + +void add_values_to_reference(IncrementalMerkleTree &tree, std::vector< std::vector > &values) { + IncrementalMerkleTree newtree(20); + + if (newtree.insertVector(values) == false) { + BOOST_ERROR("Could not insert into the tree."); + } + + tree.setTo(newtree); +} + +BOOST_AUTO_TEST_CASE( test_add_values_to_reference ) { + IncrementalMerkleTree incTree(20); + IncrementalMerkleTree incTree2(20); + + std::vector< std::vector > values; + constructNonzeroTestVector(values, 2); + + if (incTree.insertVector(values) == false) { + BOOST_ERROR("Could not insert into the tree."); + } + + add_values_to_reference(incTree2, values); + + { + std::vector root1, root2; + incTree.getRootValue(root1); + incTree2.getRootValue(root2); + + BOOST_CHECK(root1 == root2); + } +} + +BOOST_AUTO_TEST_CASE( testRootOfTreeOfNonZeroIsNonZero ) { + IncrementalMerkleTree incTree; + std::vector< std::vector > values; + std::vector actual_root; + + constructNonzeroTestVector(values, 2); + + // Create an IncrementalMerkleTree over the values. + if (incTree.insertVector(values) == false) { + BOOST_ERROR("Could not insert into the tree."); + } + incTree.prune(); + incTree.getRootValue(actual_root); + + std::vector expected_root(32*8, 0); + BOOST_CHECK( expected_root != actual_root ); +} + +BOOST_AUTO_TEST_CASE( testSerializationEdgeCase ) { + +} + +BOOST_AUTO_TEST_CASE( testCompactRepresentation ) { + for (uint32_t num_entries = 0; num_entries < 100; num_entries++) { + size_t test_depth = 64; + + if (num_entries == 2) { + // This is a particular failure I'm testing with weird + // padding caused by this depth. + test_depth = 20; + } + + std::vector< std::vector > values; + std::vector root1, root2; + IncrementalMerkleTree incTree(test_depth); + + constructNonzeroTestVector(values, num_entries); + + BOOST_REQUIRE( incTree.insertVector(values) ); + BOOST_REQUIRE( incTree.prune() ); + + IncrementalMerkleTreeCompact compact = incTree.getCompactRepresentation(); + + BOOST_REQUIRE( compact.getTreeHeight() == test_depth ); + + // Calculate what the path to the next-added element should be. + std::vector path_bytes(8); + std::vector path_bits; + libzerocash::convertIntToBytesVector(num_entries, path_bytes); + libzerocash::convertBytesVectorToVector(path_bytes, path_bits); + + if (test_depth == 64) { + // Make sure the paths match. + BOOST_REQUIRE( compact.getHashList() == path_bits ); + } + + // Make sure there's a hash for every '1' bit down the path. + BOOST_REQUIRE( compact.getHashVec().size() == libzerocash::countOnes(path_bits) ); + + /* Test serializing and deserializing. */ + std::vector serializedCompact = compact.serialize(); + IncrementalMerkleTreeCompact deserializedCompact = IncrementalMerkleTreeCompact::deserialize(serializedCompact); + BOOST_REQUIRE(compact.getTreeHeight() == deserializedCompact.getTreeHeight()); + BOOST_REQUIRE(compact.getHashList() == deserializedCompact.getHashList()); + BOOST_REQUIRE(compact.getHashVec() == deserializedCompact.getHashVec()); + + // Make sure 'restoring' the tree results in the same root. + IncrementalMerkleTree newTree(compact); + incTree.getRootValue(root1); + incTree.getRootValue(root2); + BOOST_REQUIRE( root1 == root2 ); + } +} + +BOOST_AUTO_TEST_CASE( testCompactDeserializationFailures ) { + IncrementalMerkleTree incTree(64); + std::vector< std::vector > values; + constructNonzeroTestVector(values, 5); + BOOST_REQUIRE( incTree.insertVector(values) ); + BOOST_REQUIRE( incTree.prune() ); + IncrementalMerkleTreeCompact compact = incTree.getCompactRepresentation(); + + /* Base the following tests off of this valid serialization. */ + std::vector serialized = compact.serialize(); + + /* Should fail if we truncate any number of bytes off the end. */ + for (size_t trunc_len = 0; trunc_len < serialized.size(); trunc_len++) { + std::vector truncated(serialized.begin(), serialized.begin() + trunc_len); + BOOST_CHECK_THROW( + IncrementalMerkleTreeCompact::deserialize(truncated), + std::out_of_range + ); + } + + /* Should fail if we append any number of extra bytes on the end. */ + std::vector extra_byte = serialized; + extra_byte.push_back(0x00); + BOOST_CHECK_THROW( + IncrementalMerkleTreeCompact::deserialize(extra_byte), + std::runtime_error + ); +} diff --git a/src/zerocash/tests/test_zerocash_pour_ppzksnark.cpp b/src/zerocash/tests/test_zerocash_pour_ppzksnark.cpp new file mode 100644 index 000000000..4c8ad2241 --- /dev/null +++ b/src/zerocash/tests/test_zerocash_pour_ppzksnark.cpp @@ -0,0 +1,325 @@ +/** @file + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +#include +#include +#include + +#include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" +#include "libsnark/common/data_structures/merkle_tree.hpp" +#include "libsnark/common/utils.hpp" +#include "libsnark/common/profiling.hpp" +#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" +#include "zerocash/zerocash_pour_gadget.hpp" +#include "zerocash/zerocash_pour_ppzksnark.hpp" + +using namespace libzerocash; + +bit_vector int_to_bit_vector(const size_t value, const size_t length) +{ + /* the returned result will have 0-th position equal to MSB of + `value`, when written as `length`-bit integer */ + + assert(log2(value) < length); + bit_vector result(length, false); + + for (size_t i = 0; i < length; ++i) + { + result[length-1-i] = ((value >> i) & 1) ? true : false; + } + + return result; +} + +bit_vector get_random_bit_vector(const size_t length) +{ + bit_vector result(length); + std::generate(result.begin(), result.end(), [] { return std::rand() % 2; }); + return result; +} + +std::vector sample_random_positions(const size_t num_positions, const size_t log2_universe_size) +{ + /* not asymptotically optimal, but likely not to be a problem in + practice, where num_positions is much smaller than + universe_size */ + assert(log2(num_positions) <= log2_universe_size); + assert(log2_universe_size <= 8 * sizeof(size_t)); + std::set positions; + std::vector result; + while (positions.size() != num_positions) + { + size_t new_pos = std::rand(); + if (log2_universe_size < 8 * sizeof(size_t)) + { + new_pos &= ((1ul << log2_universe_size) - 1); /* clear higher bits appropriately */ + } + if (positions.find(new_pos) == positions.end()) + { + positions.insert(new_pos); + result.emplace_back(new_pos); + } + } + + return result; +} + +template +bit_vector compute_coin_commitment(const bit_vector &address_public_key, + const bit_vector &coin_value, + const bit_vector &serial_number_nonce, + const bit_vector &address_commitment_nonce) +{ + /* commitment_to_address_public_key = H(address_public_key || serial_number_nonce) */ + bit_vector commitment_to_address_public_key_hash_input; + commitment_to_address_public_key_hash_input.insert(commitment_to_address_public_key_hash_input.end(), + address_public_key.begin(), + address_public_key.end()); + commitment_to_address_public_key_hash_input.insert(commitment_to_address_public_key_hash_input.end(), + serial_number_nonce.begin(), + serial_number_nonce.end()); + const bit_vector commitment_to_address_public_key = sha256_two_to_one_hash_gadget::get_hash(commitment_to_address_public_key_hash_input); + + /* coin_value_commitment_nonce = H(address_commitment_nonce || commitment_to_address_public_key[0..128]) */ + bit_vector coin_value_commitment_nonce_hash_input; + coin_value_commitment_nonce_hash_input.insert(coin_value_commitment_nonce_hash_input.end(), + address_commitment_nonce.begin(), + address_commitment_nonce.end()); + coin_value_commitment_nonce_hash_input.insert(coin_value_commitment_nonce_hash_input.end(), + commitment_to_address_public_key.begin(), + commitment_to_address_public_key.begin() + truncated_coin_commitment_length); + + const bit_vector coin_value_commitment_nonce = sha256_two_to_one_hash_gadget::get_hash(coin_value_commitment_nonce_hash_input); + + /* coin_commitment = H(coin_value_commitment_nonce || 0^{192} || coin_value) */ + bit_vector coin_commitment_hash_input; + coin_commitment_hash_input.insert(coin_commitment_hash_input.end(), + coin_value_commitment_nonce.begin(), + coin_value_commitment_nonce.end()); + coin_commitment_hash_input.resize(coin_commitment_hash_input.size() + coin_commitment_padding_length, false); + coin_commitment_hash_input.insert(coin_commitment_hash_input.end(), + coin_value.begin(), + coin_value.end()); + const bit_vector coin_commitment = sha256_two_to_one_hash_gadget::get_hash(coin_commitment_hash_input); + + return coin_commitment; +} + +std::vector randomly_split_up_value(const size_t value, const size_t num_parts) +{ + std::vector points(num_parts-1); + std::generate(points.begin(), points.end(), [value] { return std::rand() % value; }); + points.emplace_back(0); + points.emplace_back(value); + std::sort(points.begin(), points.end()); /* now points is a num_parts+1-length vector with endpoints of 0 and value */ + + std::vector result(num_parts); + for (size_t i = 0; i < num_parts; ++i) + { + result[i] = points[i+1] - points[i]; + } + /* the resulting sum telescopes to value-0 = value */ + + return result; +} + +template +void test_zerocash_pour_ppzksnark(const size_t num_old_coins, const size_t num_new_coins, const size_t tree_depth) +{ + typedef Fr FieldT; + + assert(log2(num_old_coins) <= tree_depth); + + /* information used by the prover in the witness map */ + std::vector old_coin_authentication_paths(num_old_coins); // + std::vector old_coin_merkle_tree_positions; // + bit_vector merkle_tree_root; // + std::vector new_address_public_keys(num_new_coins); // + std::vector old_address_secret_keys(num_old_coins); // + std::vector new_address_commitment_nonces(num_new_coins); // + std::vector old_address_commitment_nonces(num_old_coins); // + std::vector new_coin_serial_number_nonces(num_new_coins); // + std::vector old_coin_serial_number_nonces(num_old_coins); // + std::vector new_coin_values(num_new_coins); // + bit_vector public_in_value; // + bit_vector public_out_value; // + std::vector old_coin_values(num_old_coins); // + bit_vector signature_public_key_hash; // + + /* generate split for the money */ + std::vector old_coin_values_as_integers(num_old_coins); + std::generate(old_coin_values_as_integers.begin(), old_coin_values_as_integers.end(), [] { return std::rand() % 10000; }); + const size_t total_value_as_integer = std::accumulate(old_coin_values_as_integers.begin(), old_coin_values_as_integers.end(), 0); + std::vector all_new_values_as_integers = randomly_split_up_value(total_value_as_integer, num_new_coins + 1); + + std::transform(old_coin_values_as_integers.begin(), old_coin_values_as_integers.end(), + old_coin_values.begin(), + [] (const size_t value) { return int_to_bit_vector(value, coin_value_length); }); + public_in_value = int_to_bit_vector(0, coin_value_length); + public_out_value = int_to_bit_vector(all_new_values_as_integers[0], coin_value_length); + std::transform(all_new_values_as_integers.begin() + 1, all_new_values_as_integers.end(), + new_coin_values.begin(), + [] (const size_t value) { return int_to_bit_vector(value, coin_value_length); }); + + /* generate random private values for the prover */ + old_coin_merkle_tree_positions = sample_random_positions(num_old_coins, tree_depth); + for (size_t i = 0; i < num_old_coins; ++i) + { + old_address_secret_keys[i] = get_random_bit_vector(address_secret_key_length); + old_address_commitment_nonces[i] = get_random_bit_vector(address_commitment_nonce_length); + old_coin_serial_number_nonces[i] = get_random_bit_vector(serial_number_nonce_length); + } + + for (size_t i = 0; i < num_new_coins; ++i) + { + new_address_public_keys[i] = get_random_bit_vector(address_public_key_length); + new_address_commitment_nonces[i] = get_random_bit_vector(address_commitment_nonce_length); + new_coin_serial_number_nonces[i] = get_random_bit_vector(serial_number_nonce_length); + } + + merkle_tree > tree(tree_depth, coin_commitment_length); + for (size_t i = 0; i < num_old_coins; ++i) + { + /* calculate old coin and place it in the Merkle tree */ + + /* old_address_public_key = H(old_address_secret_key || 00...) */ + bit_vector old_address_public_key_hash_input = old_address_secret_keys[i]; + old_address_public_key_hash_input.resize(sha256_block_len); + const bit_vector old_address_public_key = sha256_two_to_one_hash_gadget::get_hash(old_address_public_key_hash_input); + + const bit_vector old_coin = compute_coin_commitment(old_address_public_key, + old_coin_values[i], + old_coin_serial_number_nonces[i], + old_address_commitment_nonces[i]); + tree.set_value(old_coin_merkle_tree_positions[i], old_coin); + } + + for (size_t i = 0; i < num_old_coins; ++i) + { + /* get the corresponding authentication paths */ + old_coin_authentication_paths[i] = tree.get_path(old_coin_merkle_tree_positions[i]); + } + + merkle_tree_root = tree.get_root(); + signature_public_key_hash = get_random_bit_vector(sha256_digest_len); + + /* calculate the values used by the verifier */ + std::vector old_coin_serial_numbers(num_old_coins); // + std::vector new_coin_commitments(num_new_coins); // + std::vector signature_public_key_hash_macs(num_old_coins); // + + for (size_t i = 0; i < num_old_coins; ++i) + { + /* serial_number = H(address_secret_key || 01 || serial_number_nonce[0..254]) */ + bit_vector old_coin_serial_number_hash_input; + old_coin_serial_number_hash_input.insert(old_coin_serial_number_hash_input.end(), + old_address_secret_keys[i].begin(), + old_address_secret_keys[i].end()); + old_coin_serial_number_hash_input.push_back(false); + old_coin_serial_number_hash_input.push_back(true); + old_coin_serial_number_hash_input.insert(old_coin_serial_number_hash_input.end(), + old_coin_serial_number_nonces[i].begin(), + old_coin_serial_number_nonces[i].begin() + truncated_serial_number_length); + + old_coin_serial_numbers[i] = sha256_two_to_one_hash_gadget::get_hash(old_coin_serial_number_hash_input); + + /* signature_public_key_hash_macs[i] = H(old_address_secret_keys[i] || 10 || i || signature_public_key_hash) */ + const size_t truncated_signature_public_key_hash_length = indexed_signature_public_key_hash_length - log2(num_old_coins); + + bit_vector signature_public_key_hash_macs_hash_input; + signature_public_key_hash_macs_hash_input.insert(signature_public_key_hash_macs_hash_input.end(), + old_address_secret_keys[i].begin(), + old_address_secret_keys[i].end()); + signature_public_key_hash_macs_hash_input.push_back(true); + signature_public_key_hash_macs_hash_input.push_back(false); + const bit_vector i_as_bits = int_to_bit_vector(i, log2(num_old_coins)); + signature_public_key_hash_macs_hash_input.insert(signature_public_key_hash_macs_hash_input.end(), + i_as_bits.begin(), + i_as_bits.end()); + signature_public_key_hash_macs_hash_input.insert(signature_public_key_hash_macs_hash_input.end(), + signature_public_key_hash.begin(), + signature_public_key_hash.begin() + truncated_signature_public_key_hash_length); + signature_public_key_hash_macs[i] = sha256_two_to_one_hash_gadget::get_hash(signature_public_key_hash_macs_hash_input); + } + + for (size_t i = 0; i < num_new_coins; ++i) + { + new_coin_commitments[i] = compute_coin_commitment(new_address_public_keys[i], + new_coin_values[i], + new_coin_serial_number_nonces[i], + new_address_commitment_nonces[i]); + } + + /* perform basic sanity checks */ + { + protoboard pb; + zerocash_pour_gadget pour(pb, num_old_coins, num_new_coins, tree_depth, "pour"); + pour.generate_r1cs_constraints(); + pour.generate_r1cs_witness(old_coin_authentication_paths, + old_coin_merkle_tree_positions, + merkle_tree_root, + new_address_public_keys, + old_address_secret_keys, + new_address_commitment_nonces, + old_address_commitment_nonces, + new_coin_serial_number_nonces, + old_coin_serial_number_nonces, + new_coin_values, + public_in_value, + public_out_value, + old_coin_values, + signature_public_key_hash); + assert(pb.is_satisfied()); + printf("gadget test OK for num_old_coins = %zu, num_new_coins = %zu, tree_depth = %zu\n", + num_old_coins, num_new_coins, tree_depth); + } + + /* do the end-to-end test */ + zerocash_pour_keypair keypair = zerocash_pour_ppzksnark_generator(num_old_coins, num_new_coins, tree_depth); + keypair = reserialize >(keypair); + + zerocash_pour_proof proof = zerocash_pour_ppzksnark_prover(keypair.pk, + old_coin_authentication_paths, + old_coin_merkle_tree_positions, + merkle_tree_root, + new_address_public_keys, + old_address_secret_keys, + new_address_commitment_nonces, + old_address_commitment_nonces, + new_coin_serial_number_nonces, + old_coin_serial_number_nonces, + new_coin_values, + public_in_value, + public_out_value, + old_coin_values, + signature_public_key_hash); + proof = reserialize >(proof); + + const bool verification_result = zerocash_pour_ppzksnark_verifier(keypair.vk, + merkle_tree_root, + old_coin_serial_numbers, + new_coin_commitments, + public_in_value, + public_out_value, + signature_public_key_hash, + signature_public_key_hash_macs, + proof); + printf("Verification result: %s\n", verification_result ? "pass" : "FAIL"); + assert(verification_result); +} + +int main(int argc, const char * argv[]) +{ + start_profiling(); + default_r1cs_ppzksnark_pp::init_public_params(); + test_zerocash_pour_ppzksnark(2, 2, 4); + test_zerocash_pour_ppzksnark(2, 3, 4); + test_zerocash_pour_ppzksnark(3, 2, 4); + test_zerocash_pour_ppzksnark(3, 3, 4); + test_zerocash_pour_ppzksnark(2, 2, 32); +} diff --git a/src/zerocash/tests/timer.cpp b/src/zerocash/tests/timer.cpp new file mode 100644 index 000000000..b5642f681 --- /dev/null +++ b/src/zerocash/tests/timer.cpp @@ -0,0 +1,48 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for a timer to profile executions. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +#include + +#include "zerocash/tests/timer.h" + +namespace libzerocash { + +struct timeval tv_start; +struct timeval tv_end; + +void timer_start() { + printf("%s\n", "Starting Timer"); + gettimeofday(&tv_start, 0); +} + +void timer_stop() { + float elapsed; + gettimeofday(&tv_end, 0); + + elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000); + printf("%s [%fs]\n\n", "Stopping Timer", elapsed); +} + +void timer_start(const std::string location) { + printf("%s %s\n", "(enter)", location.c_str()); + gettimeofday(&tv_start, 0); +} + +void timer_stop(const std::string location) { + float elapsed; + gettimeofday(&tv_end, 0); + + elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000); + printf("%s %s [%fs]\n\n", "(leave)", location.c_str(), elapsed); +} + +} /* namespace libzerocash */ diff --git a/src/zerocash/tests/timer.h b/src/zerocash/tests/timer.h new file mode 100644 index 000000000..ad9aef8d8 --- /dev/null +++ b/src/zerocash/tests/timer.h @@ -0,0 +1,28 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for a timer to profile executions. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef TIMER_H_ +#define TIMER_H_ + +#include + +namespace libzerocash { + +void timer_start(); +void timer_stop(); + +void timer_start(const std::string location); +void timer_stop(const std::string location); + +} /* namespace libzerocash */ + +#endif /* TIMER_H_ */ + diff --git a/src/zerocash/tests/utilTest.cpp b/src/zerocash/tests/utilTest.cpp new file mode 100644 index 000000000..6374b350a --- /dev/null +++ b/src/zerocash/tests/utilTest.cpp @@ -0,0 +1,534 @@ + +#define BOOST_TEST_MODULE utilTest +#include + +#include "zerocash/utils/util.h" +#include "zerocash/utils/sha256.h" + +#define SHA256_PREIMAGE_BYTES 3 +const unsigned char sha256_preimage[SHA256_PREIMAGE_BYTES] = { 'a', 'b', 'c' }; +/* This is the SHA256 hash of "abc" according to the modified implementation of + * SHA256 included in libzerocash. */ +const unsigned char sha256_hash[32] = { 0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae, + 0x85, 0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, + 0xf5, 0x3a, 0x51, 0x0e, 0x52, 0x7f, 0x9b, + 0x05, 0x68, 0x8c, 0x1f, 0x83, 0xd9, 0xab, + 0x5b, 0xe0, 0xcd, 0x19 }; + +BOOST_AUTO_TEST_CASE( testGetRandBytes ) { + unsigned char bytes1[32]; + unsigned char bytes2[32]; + + memset(bytes1, 0, 32); + memset(bytes2, 0, 32); + + libzerocash::getRandBytes(bytes1, 32); + libzerocash::getRandBytes(bytes2, 32); + + BOOST_CHECK( memcmp(bytes1, bytes2, 32) != 0 ); + BOOST_CHECK( memcmp(bytes1, bytes1+16, 16) != 0 ); +} + +BOOST_AUTO_TEST_CASE( testConvertVectorToInt ) { + BOOST_CHECK(libzerocash::convertVectorToInt({0}) == 0); + BOOST_CHECK(libzerocash::convertVectorToInt({1}) == 1); + BOOST_CHECK(libzerocash::convertVectorToInt({0,1}) == 1); + BOOST_CHECK(libzerocash::convertVectorToInt({1,0}) == 2); + BOOST_CHECK(libzerocash::convertVectorToInt({1,1}) == 3); + BOOST_CHECK(libzerocash::convertVectorToInt({1,0,0}) == 4); + BOOST_CHECK(libzerocash::convertVectorToInt({1,0,1}) == 5); + BOOST_CHECK(libzerocash::convertVectorToInt({1,1,0}) == 6); + + BOOST_CHECK_THROW(libzerocash::convertVectorToInt(std::vector(100)), std::length_error); + + { + std::vector v(63, 1); + BOOST_CHECK(libzerocash::convertVectorToInt(v) == 0x7fffffffffffffff); + } + + { + std::vector v(64, 1); + BOOST_CHECK(libzerocash::convertVectorToInt(v) == 0xffffffffffffffff); + } +} + +BOOST_AUTO_TEST_CASE( testConvertBytesToVector ) { + unsigned char bytes[5] = {0x00, 0x01, 0x03, 0x12, 0xFF}; + std::vector v1(5*8); + libzerocash::convertBytesToVector(bytes, v1); + + std::vector v2 = { + // 0x00 + 0, 0, 0, 0, 0, 0, 0, 0, + // 0x01 + 0, 0, 0, 0, 0, 0, 0, 1, + // 0x03 + 0, 0, 0, 0, 0, 0, 1, 1, + // 0x12 + 0, 0, 0, 1, 0, 0, 1, 0, + // 0xFF + 1, 1, 1, 1, 1, 1, 1, 1 + }; + + BOOST_CHECK(v1 == v2); + + std::vector unevensize(4); + unsigned char abyte[1] = { 0x55 }; + libzerocash::convertBytesToVector(abyte, unevensize); + + /* This may not be what we would expect, but this test will alert us if the + * behavior changes. */ + v2 = { 0, 0, 0, 0 }; + BOOST_CHECK(unevensize == v2); +} + +BOOST_AUTO_TEST_CASE( testConvertVectorToBytes) { + unsigned char bytes[5] = {0x00, 0x01, 0x03, 0x12, 0xFF}; + std::vector v = { + // 0x00 + 0, 0, 0, 0, 0, 0, 0, 0, + // 0x01 + 0, 0, 0, 0, 0, 0, 0, 1, + // 0x03 + 0, 0, 0, 0, 0, 0, 1, 1, + // 0x12 + 0, 0, 0, 1, 0, 0, 1, 0, + // 0xFF + 1, 1, 1, 1, 1, 1, 1, 1 + }; + unsigned char output[5]; + libzerocash::convertVectorToBytes(v, output); + BOOST_CHECK( memcmp(bytes, output, sizeof(bytes)) == 0 ); + + /* This is not necessarily the behavior one would expect, but this test will + * notify us if it changes. */ + unsigned char onebyte[1]; + std::vector unevensize { 1, 1, 1, 1, 1, 1, 1 }; + libzerocash::convertVectorToBytes(unevensize, onebyte); + BOOST_CHECK(onebyte[0] == 0); +} + +BOOST_AUTO_TEST_CASE( testConvertBytesToBytesVector ) { + unsigned char bytes[16]; + for (int i = 0; i < 16; i++) { + bytes[i] = i; + } + std::vector v(16); + libzerocash::convertBytesToBytesVector(bytes, v); + for (int i = 0; i < 16; i++) { + BOOST_CHECK(v.at(i) == bytes[i]); + } +} + +BOOST_AUTO_TEST_CASE( testConvertBytesVectorToBytes ) { + std::vectorv(16); + for (int i = 0; i < 16; i++) { + v[i] = i; + } + unsigned char bytes[16]; + memset(bytes, 0, 16); + libzerocash::convertBytesVectorToBytes(v, bytes); + for (int i = 0; i < 16; i++) { + BOOST_CHECK(bytes[i] == v.at(i)); + } +} + +BOOST_AUTO_TEST_CASE( testConvertBytesVectorToVector ) { + std::vector bytes = {0x00, 0x01, 0x03, 0x12, 0xFF}; + std::vector expected_bits = { + // 0x00 + 0, 0, 0, 0, 0, 0, 0, 0, + // 0x01 + 0, 0, 0, 0, 0, 0, 0, 1, + // 0x03 + 0, 0, 0, 0, 0, 0, 1, 1, + // 0x12 + 0, 0, 0, 1, 0, 0, 1, 0, + // 0xFF + 1, 1, 1, 1, 1, 1, 1, 1 + }; + std::vector actual_bits; + libzerocash::convertBytesVectorToVector(bytes, actual_bits); + BOOST_CHECK(actual_bits == expected_bits); +} + +BOOST_AUTO_TEST_CASE( testConvertVectorToBytesVector ) { + std::vector expected_bytes = {0x00, 0x01, 0x03, 0x12, 0xFF}; + std::vector bits = { + // 0x00 + 0, 0, 0, 0, 0, 0, 0, 0, + // 0x01 + 0, 0, 0, 0, 0, 0, 0, 1, + // 0x03 + 0, 0, 0, 0, 0, 0, 1, 1, + // 0x12 + 0, 0, 0, 1, 0, 0, 1, 0, + // 0xFF + 1, 1, 1, 1, 1, 1, 1, 1 + }; + // TODO: evaluate whether initializing with 5 should be necessary. + std::vector actual_bytes(5); + libzerocash::convertVectorToBytesVector(bits, actual_bytes); + BOOST_CHECK(actual_bytes == expected_bytes); +} + +BOOST_AUTO_TEST_CASE( testConvertIntToBytesVector ) { + uint64_t val; + std::vector expected; + std::vector bytes(8); + + val = 0ULL; + expected = { 0, 0, 0, 0, 0, 0, 0, 0 }; + libzerocash::convertIntToBytesVector(val, bytes); + BOOST_CHECK( expected == bytes ); + + val = 1ULL; + expected = { 0, 0, 0, 0, 0, 0, 0, 1 }; + libzerocash::convertIntToBytesVector(val, bytes); + BOOST_CHECK( expected == bytes ); + + val = 0xffffffffffffffffULL; + expected = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + libzerocash::convertIntToBytesVector(val, bytes); + BOOST_CHECK( expected == bytes ); + + val = 0x8000000080000001ULL; // sign extension + expected = { 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01 }; + libzerocash::convertIntToBytesVector(val, bytes); + BOOST_CHECK( expected == bytes ); + + // The following two tests aren't necessarily desired results. They merely + // document the behavior so that we'll be alerted if it changes in the + // future. + + val = 0xffffffffdeadbeefULL; // truncation + expected = { 0xde, 0xad, 0xbe, 0xef }; + std::vector small_bytes(4); + libzerocash::convertIntToBytesVector(val, small_bytes); + BOOST_CHECK( expected == small_bytes ); + + val = 0xf1f2f3f401020304ULL; // bytes buffer is too big + // The first byte is 4 because `>> 64` is undefined, and that's the result + // it has on my system (note that it's the same as the original LSB). + expected = { 0x04, 0xf1, 0xf2, 0xf3, 0xf4, 0x01, 0x02, 0x03, 0x04 }; + std::vector big_bytes(9); + libzerocash::convertIntToBytesVector(val, big_bytes); + BOOST_CHECK( expected == big_bytes); +} + +BOOST_AUTO_TEST_CASE( testConvertBytesVectorToInt ) { + uint64_t val; + uint64_t expected; + std::vector bytes; + + bytes = { 0, 0, 0, 0, 0, 0, 0, 0 }; + expected = 0ULL; + val = libzerocash::convertBytesVectorToInt(bytes); + BOOST_CHECK( expected == val ); + + bytes = { 0, 0, 0, 0, 0, 0, 0, 1 }; + expected = 1ULL; + val = libzerocash::convertBytesVectorToInt(bytes); + BOOST_CHECK( expected == val ); + + bytes = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + expected = 0xffffffffffffffffULL; + val = libzerocash::convertBytesVectorToInt(bytes); + BOOST_CHECK( expected == val ); + + bytes = { 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01 }; + expected = 0x8000000080000001ULL; + val = libzerocash::convertBytesVectorToInt(bytes); + BOOST_CHECK( expected == val ); + + bytes = { 0xde, 0xad, 0xbe, 0xef }; // opposite of truncation + expected = 0xdeadbeefULL; + val = libzerocash::convertBytesVectorToInt(bytes); + BOOST_CHECK( expected == val ); +} + +BOOST_AUTO_TEST_CASE( testConvertIntToVector ) { + uint64_t val; + std::vector expected; + std::vector vector; + + val = 0ULL; + expected = { 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 }; + libzerocash::convertIntToVector(val, vector); + BOOST_CHECK( expected == vector ); + + val = 1ULL; + expected = { 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, 1 }; + libzerocash::convertIntToVector(val, vector); + BOOST_CHECK( expected == vector ); + + val = 0xffffffffffffffffULL; + expected = { 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1 }; + libzerocash::convertIntToVector(val, vector); + BOOST_CHECK( expected == vector ); + + val = 0x8000000080000001ULL; // sign extension + expected = { 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, + 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, 1 }; + libzerocash::convertIntToVector(val, vector); + BOOST_CHECK( expected == vector ); + + std::vector too_big(100); + libzerocash::convertIntToVector(0, too_big); + BOOST_CHECK(too_big.size() == 64); + + std::vector too_small(10); + libzerocash::convertIntToVector(0, too_small); + BOOST_CHECK(too_big.size() == 64); +} + +BOOST_AUTO_TEST_CASE( testConcatenateTwoBoolVectors ) { + std::vector front = { 0, 1, 0 }; + std::vector back = { 1, 0, 1 }; + std::vector expected = { 0, 1, 0, 1, 0, 1 }; + std::vector actual; + libzerocash::concatenateVectors(front, back, actual); + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testConcatenateTwoByteVectors ) { + std::vector front = { 0, 1, 2 }; + std::vector back = { 3, 4, 5 }; + std::vector expected = { 0, 1, 2, 3, 4, 5 }; + std::vector actual; + libzerocash::concatenateVectors(front, back, actual); + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testConcatenateThreeBoolVectors ) { + std::vector front = { 0, 1, 0 }; + std::vector middle { 1, 1, 1 }; + std::vector back = { 1, 0, 1 }; + std::vector expected = { 0, 1, 0, 1, 1, 1, 1, 0, 1 }; + std::vector actual; + libzerocash::concatenateVectors(front, middle, back, actual); + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testConcatenateThreeByteVectors ) { + std::vector front = { 0, 1, 0 }; + std::vector middle { 1, 1, 1 }; + std::vector back = { 1, 0, 1 }; + std::vector expected = { 0, 1, 0, 1, 1, 1, 1, 0, 1 }; + std::vector actual; + libzerocash::concatenateVectors(front, middle, back, actual); + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testSHA256ModifiedTestVectors ) { + unsigned char actual_hash[32]; + libzerocash::sha256(sha256_preimage, actual_hash, 3); + BOOST_CHECK( memcmp(sha256_hash, actual_hash, 32) == 0 ); +} + +BOOST_AUTO_TEST_CASE( testSHA256ModifiedTestVectorsCTX ) { + unsigned char actual_hash[32]; + SHA256_CTX_mod ctx256; + libzerocash::sha256(&ctx256, sha256_preimage, actual_hash, 3); + BOOST_CHECK( memcmp(sha256_hash, actual_hash, 32) == 0 ); +} + +BOOST_AUTO_TEST_CASE( testSHA256TestVectors ) { + /* Tests an actual SHA256 test vector (with length padding) to make sure + * libzerocash's implementation is actually the same as SHA256 with the + * length padding removed. */ + unsigned char preimage[3] = { 'a', 'b', 'c' }; + unsigned char expected_hash[32] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, + 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, + 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, + 0x15, 0xad }; + unsigned char actual_hash[32]; + SHA256_CTX_mod ctx256; + sha256_init(&ctx256); + sha256_update(&ctx256, preimage, 3); + sha256_length_padding(&ctx256); + sha256_final_no_padding(&ctx256, actual_hash); + + BOOST_CHECK( memcmp(expected_hash, actual_hash, 32) == 0 ); +} + +BOOST_AUTO_TEST_CASE( testHashBoolVectorToBoolVectorCTX ) { + SHA256_CTX_mod ctx256; + + std::vector preimage(SHA256_PREIMAGE_BYTES * 8); + libzerocash::convertBytesToVector(sha256_preimage, preimage); + + std::vector expected(32*8); + libzerocash::convertBytesToVector(sha256_hash, expected); + + // TODO: evaluate whether this should be a necessary precondition. + std::vector actual(32*8); + libzerocash::hashVector(&ctx256, preimage, actual); + + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testHashByteVectorToByteVectorCTX ) { + SHA256_CTX_mod ctx256; + + std::vector preimage(SHA256_PREIMAGE_BYTES); + libzerocash::convertBytesToBytesVector(sha256_preimage, preimage); + + std::vector expected(32); + libzerocash::convertBytesToBytesVector(sha256_hash, expected); + + // TODO: evaluate whether this should be a necessary precondition. + std::vector actual(32); + libzerocash::hashVector(&ctx256, preimage, actual); + + BOOST_CHECK( expected == actual ); +} + + +BOOST_AUTO_TEST_CASE( testHashBoolVectorToBoolVector ) { + std::vector preimage(SHA256_PREIMAGE_BYTES * 8); + libzerocash::convertBytesToVector(sha256_preimage, preimage); + + std::vector expected(32*8); + libzerocash::convertBytesToVector(sha256_hash, expected); + + // TODO: evaluate whether this should be a necessary precondition. + std::vector actual(32*8); + libzerocash::hashVector(preimage, actual); + + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testHashByteVectorToByteVector ) { + std::vector preimage(SHA256_PREIMAGE_BYTES); + libzerocash::convertBytesToBytesVector(sha256_preimage, preimage); + + std::vector expected(32); + libzerocash::convertBytesToBytesVector(sha256_hash, expected); + + // TODO: evaluate whether this should be a necessary precondition. + std::vector actual(32); + libzerocash::hashVector(preimage, actual); + + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testHashBoolVectorsCTX ) { + SHA256_CTX_mod ctx256; + + std::vector preimage1(8); + libzerocash::convertBytesToVector(sha256_preimage, preimage1); + + std::vector preimage2((SHA256_PREIMAGE_BYTES - 1) * 8); + libzerocash::convertBytesToVector(sha256_preimage + 1, preimage2); + + std::vector expected(32*8); + libzerocash::convertBytesToVector(sha256_hash, expected); + + // TODO: evaluate whether this should be a necessary precondition. + std::vector actual(32 * 8); + libzerocash::hashVectors(&ctx256, preimage1, preimage2, actual); + + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testHashByteVectorsCTX ) { + SHA256_CTX_mod ctx256; + + std::vector preimage1(1); + libzerocash::convertBytesToBytesVector(sha256_preimage, preimage1); + + std::vector preimage2(SHA256_PREIMAGE_BYTES - 1); + libzerocash::convertBytesToBytesVector(sha256_preimage + 1, preimage2); + + std::vector expected(32); + libzerocash::convertBytesToBytesVector(sha256_hash, expected); + + // TODO: evaluate whether this should be a necessary precondition. + std::vector actual(32); + libzerocash::hashVectors(&ctx256, preimage1, preimage2, actual); + + BOOST_CHECK( expected == actual ); +} + + +BOOST_AUTO_TEST_CASE( testHashBoolVectors ) { + std::vector preimage1(8); + libzerocash::convertBytesToVector(sha256_preimage, preimage1); + + std::vector preimage2((SHA256_PREIMAGE_BYTES - 1) * 8); + libzerocash::convertBytesToVector(sha256_preimage + 1, preimage2); + + std::vector expected(32*8); + libzerocash::convertBytesToVector(sha256_hash, expected); + + // TODO: evaluate whether this should be a necessary precondition. + std::vector actual(32 * 8); + libzerocash::hashVectors(preimage1, preimage2, actual); + + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testHashByteVectors ) { + std::vector preimage1(1); + libzerocash::convertBytesToBytesVector(sha256_preimage, preimage1); + + std::vector preimage2(SHA256_PREIMAGE_BYTES - 1); + libzerocash::convertBytesToBytesVector(sha256_preimage + 1, preimage2); + + std::vector expected(32); + libzerocash::convertBytesToBytesVector(sha256_hash, expected); + + // TODO: evaluate whether this should be a necessary precondition. + std::vector actual(32); + libzerocash::hashVectors(preimage1, preimage2, actual); + + BOOST_CHECK( expected == actual ); +} + +BOOST_AUTO_TEST_CASE( testVectorIsZero ) { + std::vector bits; + BOOST_CHECK( libzerocash::VectorIsZero(bits) ); + + bits = { 0 }; + BOOST_CHECK( libzerocash::VectorIsZero(bits) ); + + bits = { 0, 0 }; + BOOST_CHECK( libzerocash::VectorIsZero(bits) ); + + bits = { 1 }; + BOOST_CHECK( !libzerocash::VectorIsZero(bits) ); + + bits = { 0, 1 }; + BOOST_CHECK( !libzerocash::VectorIsZero(bits) ); +} + diff --git a/src/zerocash/tests/zerocashTest.cpp b/src/zerocash/tests/zerocashTest.cpp new file mode 100644 index 000000000..00d763e55 --- /dev/null +++ b/src/zerocash/tests/zerocashTest.cpp @@ -0,0 +1,634 @@ +/** @file + ***************************************************************************** + + A test for Zerocash. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include +#include + +#define BOOST_TEST_MODULE zerocashTest +#include + +#include "timer.h" + +#include "zerocash/Zerocash.h" +#include "zerocash/ZerocashParams.h" +#include "zerocash/Address.h" +#include "zerocash/CoinCommitment.h" +#include "zerocash/Coin.h" +#include "zerocash/IncrementalMerkleTree.h" +#include "zerocash/MintTransaction.h" +#include "zerocash/PourTransaction.h" +#include "zerocash/PourInput.h" +#include "zerocash/PourOutput.h" +#include "zerocash/utils/util.h" + +using namespace std; +using namespace libsnark; + +#define TEST_TREE_DEPTH 4 + +BOOST_AUTO_TEST_CASE( SaveAndLoadKeysFromFiles ) { + cout << "\nSaveAndLoadKeysFromFiles TEST\n" << endl; + + cout << "Creating Params...\n" << endl; + + libzerocash::timer_start("Param Generation"); + auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(TEST_TREE_DEPTH); + libzerocash::ZerocashParams p( + TEST_TREE_DEPTH, + &keypair + ); + libzerocash::timer_stop("Param Generation"); + print_mem("after param generation"); + + cout << "Successfully created Params.\n" << endl; + + std::string vk_path = "./zerocashTest-verification-key"; + std::string pk_path = "./zerocashTest-proving-key"; + + libzerocash::timer_start("Saving Proving Key"); + + libzerocash::ZerocashParams::SaveProvingKeyToFile( + &p.getProvingKey(), + pk_path + ); + + libzerocash::timer_stop("Saving Proving Key"); + + libzerocash::timer_start("Saving Verification Key"); + + libzerocash::ZerocashParams::SaveVerificationKeyToFile( + &p.getVerificationKey(), + vk_path + ); + + libzerocash::timer_stop("Saving Verification Key"); + + libzerocash::timer_start("Loading Proving Key"); + auto pk_loaded = libzerocash::ZerocashParams::LoadProvingKeyFromFile(pk_path, TEST_TREE_DEPTH); + libzerocash::timer_stop("Loading Proving Key"); + + libzerocash::timer_start("Loading Verification Key"); + auto vk_loaded = libzerocash::ZerocashParams::LoadVerificationKeyFromFile(vk_path, TEST_TREE_DEPTH); + libzerocash::timer_stop("Loading Verification Key"); + + cout << "Comparing Proving and Verification key.\n" << endl; + + if ( !( p.getProvingKey() == pk_loaded && p.getVerificationKey() == vk_loaded) ) { + BOOST_ERROR("Proving and verification key are not equal."); + } + + vector coins; + vector addrs; + + cout << "Creating Addresses and Coins...\n" << endl; + for(size_t i = 0; i < 5; i++) { + addrs.push_back(libzerocash::Address::CreateNewRandomAddress()); + coins.push_back(libzerocash::Coin(addrs.at(i).getPublicAddress(), i)); + } + cout << "Successfully created address and coins.\n" << endl; + + cout << "Creating a Mint Transaction...\n" << endl; + libzerocash::MintTransaction minttx(coins.at(0)); + cout << "Successfully created a Mint Transaction.\n" << endl; + + vector> coinValues(5); + vector temp_comVal(ZC_CM_SIZE * 8); + for(size_t i = 0; i < coinValues.size(); i++) { + libzerocash::convertBytesVectorToVector(coins.at(i).getCoinCommitment().getCommitmentValue(), temp_comVal); + coinValues.at(i) = temp_comVal; + } + + cout << "Creating Merkle Tree...\n" << endl; + libzerocash::IncrementalMerkleTree merkleTree(coinValues, TEST_TREE_DEPTH); + cout << "Successfully created Merkle Tree.\n" << endl; + + std::vector index; + + cout << "Creating Witness 1...\n" << endl; + merkle_authentication_path witness_1(TEST_TREE_DEPTH); + libzerocash::convertIntToVector(1, index); + merkleTree.getWitness(index, witness_1); + cout << "Successfully created Witness 1.\n" << endl; + + cout << "Creating Witness 2...\n" << endl; + merkle_authentication_path witness_2(TEST_TREE_DEPTH); + libzerocash::convertIntToVector(3, index); + merkleTree.getWitness(index, witness_2); + cout << "Successfully created Witness 2.\n" << endl; + + cout << "Creating coins to spend...\n" << endl; + libzerocash::Address newAddress3 = libzerocash::Address::CreateNewRandomAddress(); + libzerocash::PublicAddress pubAddress3 = newAddress3.getPublicAddress(); + + libzerocash::Address newAddress4 = libzerocash::Address::CreateNewRandomAddress(); + libzerocash::PublicAddress pubAddress4 = newAddress4.getPublicAddress(); + + libzerocash::Coin c_1_new(pubAddress3, 2); + libzerocash::Coin c_2_new(pubAddress4, 2); + cout << "Successfully created coins to spend.\n" << endl; + + vector rt(ZC_ROOT_SIZE); + merkleTree.getRootValue(rt); + + // XXX: debugging + std::cout << "Root: " << rt.size() << endl; + std::cout << "wit1: " << witness_1.size() << endl; + std::cout << "wit2: " << witness_1.size() << endl; + + vector as(ZC_SIG_PK_SIZE, 'a'); + + cout << "Creating a pour transaction...\n" << endl; + libzerocash::PourTransaction pourtx(1, p, + rt, + coins.at(1), coins.at(3), + addrs.at(1), addrs.at(3), + 1, 3, + witness_1, witness_2, + pubAddress3, pubAddress4, + 0, + 0, + as, + c_1_new, c_2_new); + cout << "Successfully created a pour transaction.\n" << endl; + + std::vector pubkeyHash(ZC_SIG_PK_SIZE, 'a'); + + cout << "Verifying a pour transaction...\n" << endl; + bool pourtx_res = pourtx.verify(p, pubkeyHash, rt); + + BOOST_CHECK(pourtx_res); +} + +BOOST_AUTO_TEST_CASE( PourInputOutputTest ) { + // dummy input + { + libzerocash::PourInput input(TEST_TREE_DEPTH); + + BOOST_CHECK(input.old_coin.getValue() == 0); + BOOST_CHECK(input.old_address.getPublicAddress() == input.old_coin.getPublicAddress()); + } + + // dummy output + { + libzerocash::PourOutput output(0); + + BOOST_CHECK(output.new_coin.getValue() == 0); + BOOST_CHECK(output.to_address == output.new_coin.getPublicAddress()); + } +} + +// testing with general situational setup +void test_pour(libzerocash::ZerocashParams& p, + uint64_t vpub_in, + uint64_t vpub_out, + std::vector inputs, // values of the inputs (max 2) + std::vector outputs) // values of the outputs (max 2) +{ + using pour_input_state = std::tuple>; + + // Construct incremental merkle tree + libzerocash::IncrementalMerkleTree merkleTree(TEST_TREE_DEPTH); + + // Dummy sig_pk + vector as(ZC_SIG_PK_SIZE, 'a'); + + vector pour_inputs; + vector pour_outputs; + + vector input_state; + + for(std::vector::iterator it = inputs.begin(); it != inputs.end(); ++it) { + libzerocash::Address addr = libzerocash::Address::CreateNewRandomAddress(); + libzerocash::Coin coin(addr.getPublicAddress(), *it); + + // commitment from coin + std::vector commitment(ZC_CM_SIZE * 8); + libzerocash::convertBytesVectorToVector(coin.getCoinCommitment().getCommitmentValue(), commitment); + + // insert commitment into the merkle tree + std::vector index; + merkleTree.insertElement(commitment, index); + + // store the state temporarily + input_state.push_back(std::make_tuple(addr, coin, index)); + } + + // compute the merkle root we will be working with + vector rt(ZC_ROOT_SIZE); + { + vector root_bv(ZC_ROOT_SIZE * 8); + merkleTree.getRootValue(root_bv); + libzerocash::convertVectorToBytesVector(root_bv, rt); + } + + // get witnesses for all the input coins and construct the pours + for(vector::iterator it = input_state.begin(); it != input_state.end(); ++it) { + merkle_authentication_path path(TEST_TREE_DEPTH); + + auto index = std::get<2>(*it); + merkleTree.getWitness(index, path); + + pour_inputs.push_back(libzerocash::PourInput(std::get<1>(*it), std::get<0>(*it), libzerocash::convertVectorToInt(index), path)); + } + + // construct dummy outputs with the given values + for(vector::iterator it = outputs.begin(); it != outputs.end(); ++it) { + pour_outputs.push_back(libzerocash::PourOutput(*it)); + } + + libzerocash::PourTransaction pourtx(p, as, rt, pour_inputs, pour_outputs, vpub_in, vpub_out); + + BOOST_CHECK(pourtx.verify(p, as, rt)); +} + +BOOST_AUTO_TEST_CASE( PourVpubInTest ) { + auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(TEST_TREE_DEPTH); + libzerocash::ZerocashParams p( + TEST_TREE_DEPTH, + &keypair + ); + + // Things that should work.. + test_pour(p, 0, 0, {1}, {1}); + test_pour(p, 0, 0, {2}, {1, 1}); + test_pour(p, 0, 0, {2, 2}, {3, 1}); + test_pour(p, 0, 1, {1}, {}); + test_pour(p, 0, 1, {2}, {1}); + test_pour(p, 0, 1, {2, 2}, {2, 1}); + test_pour(p, 1, 0, {}, {1}); + test_pour(p, 1, 0, {1}, {1, 1}); + test_pour(p, 1, 0, {2, 2}, {2, 3}); + + // Things that should not work... + BOOST_CHECK_THROW(test_pour(p, 0, 1, {1}, {1}), std::invalid_argument); + BOOST_CHECK_THROW(test_pour(p, 0, 1, {2}, {1, 1}), std::invalid_argument); + BOOST_CHECK_THROW(test_pour(p, 0, 1, {2, 2}, {3, 1}), std::invalid_argument); + BOOST_CHECK_THROW(test_pour(p, 0, 2, {1}, {}), std::invalid_argument); + BOOST_CHECK_THROW(test_pour(p, 0, 2, {2}, {1}), std::invalid_argument); + BOOST_CHECK_THROW(test_pour(p, 0, 2, {2, 2}, {2, 1}), std::invalid_argument); + BOOST_CHECK_THROW(test_pour(p, 1, 1, {}, {1}), std::invalid_argument); + BOOST_CHECK_THROW(test_pour(p, 1, 1, {1}, {1, 1}), std::invalid_argument); + BOOST_CHECK_THROW(test_pour(p, 1, 1, {2, 2}, {2, 3}), std::invalid_argument); + + BOOST_CHECK_THROW(test_pour(p, 0, 0, {2, 2}, {2, 3}), std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE( CoinTest ) { + cout << "\nCOIN TEST\n" << endl; + + libzerocash::Address newAddress = libzerocash::Address::CreateNewRandomAddress(); + libzerocash::PublicAddress pubAddress = newAddress.getPublicAddress(); + + libzerocash::Coin coin(pubAddress, 0); + + cout << "Successfully created a coin.\n" << endl; + + /////////////////////////////////////////////////////////////////////////// + + libzerocash::timer_start("Coin"); + libzerocash::Coin coin2(pubAddress, 0); + libzerocash::timer_stop("Coin"); + + cout << "Successfully created a coin.\n" << endl; +} + +BOOST_AUTO_TEST_CASE( MintTxTest ) { + cout << "\nMINT TRANSACTION TEST\n" << endl; + + libzerocash::Address newAddress = libzerocash::Address::CreateNewRandomAddress(); + libzerocash::PublicAddress pubAddress = newAddress.getPublicAddress(); + + vector value(ZC_V_SIZE, 0); + + libzerocash::timer_start("Coin"); + const libzerocash::Coin coin(pubAddress, 0); + libzerocash::timer_stop("Coin"); + + libzerocash::timer_start("Mint Transaction"); + libzerocash::MintTransaction minttx(coin); + libzerocash::timer_stop("Mint Transaction"); + + cout << "Successfully created a mint transaction.\n" << endl; + + libzerocash::timer_start("Mint Transaction Verify"); + bool minttx_res = minttx.verify(); + libzerocash::timer_stop("Mint Transaction Verify"); + + BOOST_CHECK(minttx_res); +} + +BOOST_AUTO_TEST_CASE( PourTxTest ) { + cout << "\nPOUR TRANSACTION TEST\n" << endl; + + cout << "Creating Params...\n" << endl; + + libzerocash::timer_start("Param Generation"); + auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(TEST_TREE_DEPTH); + libzerocash::ZerocashParams p( + TEST_TREE_DEPTH, + &keypair + ); + libzerocash::timer_stop("Param Generation"); + print_mem("after param generation"); + + cout << "Successfully created Params.\n" << endl; + + vector coins; + vector addrs; + + for(size_t i = 0; i < 5; i++) { + addrs.push_back(libzerocash::Address::CreateNewRandomAddress()); + coins.push_back(libzerocash::Coin(addrs.at(i).getPublicAddress(), i)); + } + + cout << "Successfully created coins.\n" << endl; + + vector> coinValues(5); + + vector temp_comVal(ZC_CM_SIZE * 8); + for(size_t i = 0; i < coinValues.size(); i++) { + libzerocash::convertBytesVectorToVector(coins.at(i).getCoinCommitment().getCommitmentValue(), temp_comVal); + coinValues.at(i) = temp_comVal; + libzerocash::printVectorAsHex("Coin => ", coinValues.at(i)); + } + + cout << "Creating Merkle Tree...\n" << endl; + + libzerocash::timer_start("Merkle Tree"); + libzerocash::IncrementalMerkleTree merkleTree(coinValues, TEST_TREE_DEPTH); + libzerocash::timer_stop("Merkle Tree"); + + cout << "Successfully created Merkle Tree.\n" << endl; + + merkle_authentication_path witness_1(TEST_TREE_DEPTH); + + libzerocash::timer_start("Witness"); + std::vector index; + libzerocash::convertIntToVector(1, index); + if (merkleTree.getWitness(index, witness_1) == false) { + BOOST_ERROR("Could not get witness"); + } + libzerocash::timer_stop("Witness"); + + cout << "Witness 1: " << endl; + for(size_t i = 0; i < witness_1.size(); i++) { + libzerocash::printVectorAsHex(witness_1.at(i)); + } + cout << "\n" << endl; + + merkle_authentication_path witness_2(TEST_TREE_DEPTH); + libzerocash::convertIntToVector(3, index); + if (merkleTree.getWitness(index, witness_2) == false) { + cout << "Could not get witness" << endl; + } + + cout << "Witness 2: " << endl; + for(size_t i = 0; i < witness_2.size(); i++) { + libzerocash::printVectorAsHex(witness_2.at(i)); + } + cout << "\n" << endl; + + libzerocash::Address newAddress3 = libzerocash::Address::CreateNewRandomAddress(); + libzerocash::PublicAddress pubAddress3 = newAddress3.getPublicAddress(); + + libzerocash::Address newAddress4 = libzerocash::Address::CreateNewRandomAddress(); + libzerocash::PublicAddress pubAddress4 = newAddress4.getPublicAddress(); + + libzerocash::Coin c_1_new(pubAddress3, 2); + libzerocash::Coin c_2_new(pubAddress4, 2); + + vector root_bv(ZC_ROOT_SIZE * 8); + merkleTree.getRootValue(root_bv); + vector rt(ZC_ROOT_SIZE); + libzerocash::convertVectorToBytesVector(root_bv, rt); + + vector ones(ZC_V_SIZE, 1); + vector twos(ZC_V_SIZE, 2); + vector as(ZC_SIG_PK_SIZE, 'a'); + + cout << "Creating a pour transaction...\n" << endl; + + libzerocash::timer_start("Pour Transaction"); + libzerocash::PourTransaction pourtx(1, p, rt, coins.at(1), coins.at(3), addrs.at(1), addrs.at(3), 1, 3, witness_1, witness_2, pubAddress3, pubAddress4, 0, 0, as, c_1_new, c_2_new); + libzerocash::timer_stop("Pour Transaction"); + print_mem("after pour transaction"); + + cout << "Successfully created a pour transaction.\n" << endl; + + std::vector pubkeyHash(ZC_SIG_PK_SIZE, 'a'); + + libzerocash::timer_start("Pour Transaction Verify"); + bool pourtx_res = pourtx.verify(p, pubkeyHash, rt); + libzerocash::timer_stop("Pour Transaction Verify"); + + BOOST_CHECK(pourtx_res); +} + +BOOST_AUTO_TEST_CASE( MerkleTreeSimpleTest ) { + cout << "\nMERKLE TREE SIMPLE TEST\n" << endl; + + vector coins; + vector addrs; + + cout << "Creating coins...\n" << endl; + + for(size_t i = 0; i < 5; i++) { + addrs.push_back(libzerocash::Address::CreateNewRandomAddress()); + coins.push_back(libzerocash::Coin(addrs.at(i).getPublicAddress(), i)); + } + + cout << "Successfully created coins.\n" << endl; + + vector> coinValues(coins.size()); + + vector temp_comVal(ZC_CM_SIZE * 8); + for(size_t i = 0; i < coinValues.size(); i++) { + libzerocash::convertBytesVectorToVector(coins.at(i).getCoinCommitment().getCommitmentValue(), temp_comVal); + coinValues.at(i) = temp_comVal; + libzerocash::printVectorAsHex(coinValues.at(i)); + } + + cout << "Creating Merkle Tree...\n" << endl; + + libzerocash::IncrementalMerkleTree merkleTree(64); + vector root; + merkleTree.getRootValue(root); + cout << "Root: "; + libzerocash::printVectorAsHex(root); + cout << endl; + + cout << "Successfully created Merkle Tree.\n" << endl; + + cout << "Copying and pruning Merkle Tree...\n" << endl; + libzerocash::IncrementalMerkleTree copyTree = merkleTree; + copyTree.prune(); + + cout << "Obtaining compact representation and reconstituting tree...\n" << endl; + libzerocash::IncrementalMerkleTreeCompact compactTree = merkleTree.getCompactRepresentation(); + + cout << "Compact representation vector: "; + libzerocash::printVector(compactTree.getHashList()); + + libzerocash::IncrementalMerkleTree reconstitutedTree(compactTree); + reconstitutedTree.getRootValue(root); + cout << "New root: "; + libzerocash::printVectorAsHex(root); + cout << endl; + + reconstitutedTree.insertVector(coinValues); + merkleTree.insertVector(coinValues); + + reconstitutedTree.getRootValue(root); + cout << "New root (added a bunch more): "; + libzerocash::printVectorAsHex(root); + cout << endl; + + merkleTree.getRootValue(root); + cout << "Old root (added a bunch more): "; + libzerocash::printVectorAsHex(root); + cout << endl; + + merkle_authentication_path witness(16); + std::vector index; + libzerocash::convertIntToVector(3, index); + if (merkleTree.getWitness(index, witness) == false) { + BOOST_ERROR("Witness generation failed."); + } + + cout << "Successfully created witness.\n" << endl; + + cout << "Witness: " << endl; + for(size_t i = 0; i < witness.size(); i++) { + libzerocash::printVectorAsHex(witness.at(i)); + } + cout << "\n" << endl; + + vector wit1(SHA256_BLOCK_SIZE * 8); + vector wit2(SHA256_BLOCK_SIZE * 8); + vector wit3(SHA256_BLOCK_SIZE * 8); + vector inter_1(SHA256_BLOCK_SIZE * 8); + vector inter_2(SHA256_BLOCK_SIZE * 8); + std::vector zeros(SHA256_BLOCK_SIZE * 8, 0); + + wit1 = coinValues.at(2); + libzerocash::hashVectors(coinValues.at(0), coinValues.at(1), wit2); + libzerocash::hashVectors(coinValues.at(4), zeros, inter_1); + inter_2 = zeros; + libzerocash::hashVectors(inter_1, inter_2, wit3); + + BOOST_CHECK(witness.size() == 64); + for (size_t i = 0; i < 61; i++) { + BOOST_CHECK(witness.at(i) == zeros); + } + BOOST_CHECK( + (witness.at(61) == wit3) && + (witness.at(62) == wit2) && + (witness.at(63) == wit1) + ); +} + +BOOST_AUTO_TEST_CASE( SimpleTxTest ) { + cout << "\nSIMPLE TRANSACTION TEST\n" << endl; + + libzerocash::timer_start("Param Generation"); + auto keypair = libzerocash::ZerocashParams::GenerateNewKeyPair(TEST_TREE_DEPTH); + libzerocash::ZerocashParams p( + TEST_TREE_DEPTH, + &keypair + ); + libzerocash::timer_stop("Param Generation"); + + vector coins; + vector addrs; + + cout << "Creating Addresses and Coins...\n" << endl; + for(size_t i = 0; i < 5; i++) { + addrs.push_back(libzerocash::Address::CreateNewRandomAddress()); + coins.push_back(libzerocash::Coin(addrs.at(i).getPublicAddress(), i)); + } + cout << "Successfully created address and coins.\n" << endl; + + cout << "Creating a Mint Transaction...\n" << endl; + libzerocash::MintTransaction minttx(coins.at(0)); + cout << "Successfully created a Mint Transaction.\n" << endl; + + cout << "Verifying a Mint Transaction...\n" << endl; + bool minttx_res = minttx.verify(); + + vector> coinValues(5); + vector temp_comVal(ZC_CM_SIZE * 8); + for(size_t i = 0; i < coinValues.size(); i++) { + libzerocash::convertBytesVectorToVector(coins.at(i).getCoinCommitment().getCommitmentValue(), temp_comVal); + coinValues.at(i) = temp_comVal; + } + + cout << "Creating Merkle Tree...\n" << endl; + libzerocash::IncrementalMerkleTree merkleTree(coinValues, TEST_TREE_DEPTH); + cout << "Successfully created Merkle Tree.\n" << endl; + + std::vector index; + + cout << "Creating Witness 1...\n" << endl; + merkle_authentication_path witness_1(TEST_TREE_DEPTH); + libzerocash::convertIntToVector(1, index); + if (merkleTree.getWitness(index, witness_1) == false) { + BOOST_ERROR("Could not get witness"); + } + cout << "Successfully created Witness 1.\n" << endl; + + cout << "Creating Witness 2...\n" << endl; + merkle_authentication_path witness_2(TEST_TREE_DEPTH); + libzerocash::convertIntToVector(3, index); + if (merkleTree.getWitness(index, witness_2) == false) { + cout << "Could not get witness" << endl; + } + cout << "Successfully created Witness 2.\n" << endl; + + cout << "Creating coins to spend...\n" << endl; + libzerocash::Address newAddress3 = libzerocash::Address::CreateNewRandomAddress(); + libzerocash::PublicAddress pubAddress3 = newAddress3.getPublicAddress(); + + libzerocash::Address newAddress4 = libzerocash::Address::CreateNewRandomAddress(); + libzerocash::PublicAddress pubAddress4 = newAddress4.getPublicAddress(); + + libzerocash::Coin c_1_new(pubAddress3, 2); + libzerocash::Coin c_2_new(pubAddress4, 2); + cout << "Successfully created coins to spend.\n" << endl; + + vector root_bv(ZC_ROOT_SIZE * 8); + merkleTree.getRootValue(root_bv); + vector rt(ZC_ROOT_SIZE); + libzerocash::convertVectorToBytesVector(root_bv, rt); + + + vector as(ZC_SIG_PK_SIZE, 'a'); + + cout << "Creating a pour transaction...\n" << endl; + libzerocash::PourTransaction pourtx(1, p, + rt, + coins.at(1), coins.at(3), + addrs.at(1), addrs.at(3), + 1, 3, + witness_1, witness_2, + pubAddress3, pubAddress4, + 0, + 0, + as, + c_1_new, c_2_new); + cout << "Successfully created a pour transaction.\n" << endl; + + std::vector pubkeyHash(ZC_SIG_PK_SIZE, 'a'); + + cout << "Verifying a pour transaction...\n" << endl; + bool pourtx_res = pourtx.verify(p, pubkeyHash, rt); + + BOOST_CHECK(minttx_res && pourtx_res); +} diff --git a/src/zerocash/utils/sha256.cpp b/src/zerocash/utils/sha256.cpp new file mode 100644 index 000000000..25512e497 --- /dev/null +++ b/src/zerocash/utils/sha256.cpp @@ -0,0 +1,163 @@ +/********************************************************************* +* Filename: sha256.c +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Implementation of the SHA-256 hashing algorithm. + SHA-256 is one of the three algorithms in the SHA2 + specification. The others, SHA-384 and SHA-512, are not + offered in this implementation. + Algorithm specification can be found here: + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + This implementation uses little endian byte order. +*********************************************************************/ + +/*************************** HEADER FILES ***************************/ +#include +#include +#include "sha256.h" + +/****************************** MACROS ******************************/ +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +/**************************** VARIABLES *****************************/ +static const uint32_t k[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void sha256_transform(SHA256_CTX_mod *ctx, const uint8_t data[]) +{ + uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void sha256_init(SHA256_CTX_mod *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(SHA256_CTX_mod *ctx, const uint8_t data[], size_t len) +{ + uint32_t i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +/* Applies the length padding, which libzerocash does not use. Call before + * sha256_final_no_padding() to get the test-vector compliant SHA256 hash. */ +void sha256_length_padding(SHA256_CTX_mod *ctx) +{ + uint32_t i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = ctx->bitlen; + ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; + ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; + ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; + ctx->data[56] = ctx->bitlen >> 56; + sha256_transform(ctx, ctx->data); +} + +void sha256_final_no_padding(SHA256_CTX_mod *ctx, uint8_t hash[]) +{ + uint32_t i; + + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +} diff --git a/src/zerocash/utils/sha256.h b/src/zerocash/utils/sha256.h new file mode 100644 index 000000000..fbcc1176c --- /dev/null +++ b/src/zerocash/utils/sha256.h @@ -0,0 +1,32 @@ +/********************************************************************* +* Filename: sha256.h +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Defines the API for the corresponding SHA1 implementation. +*********************************************************************/ + +#ifndef SHA256H_H +#define SHA256H_H + +/*************************** HEADER FILES ***************************/ +#include +#include + +/****************************** MACROS ******************************/ +#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest + +typedef struct { + uint8_t data[64]; + uint32_t datalen; + unsigned long long bitlen; + uint32_t state[8]; +} SHA256_CTX_mod; + +/*********************** FUNCTION DECLARATIONS **********************/ +void sha256_init(SHA256_CTX_mod *ctx); +void sha256_update(SHA256_CTX_mod *ctx, const uint8_t data[], size_t len); +void sha256_length_padding(SHA256_CTX_mod *ctx); +void sha256_final_no_padding(SHA256_CTX_mod *ctx, uint8_t hash[]); + +#endif // SHA256H_H diff --git a/src/zerocash/utils/util.cpp b/src/zerocash/utils/util.cpp new file mode 100644 index 000000000..d784db02c --- /dev/null +++ b/src/zerocash/utils/util.cpp @@ -0,0 +1,346 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +namespace libzerocash { + +void printChar(const unsigned char c) { + for(int j = 8; j >= 0; j--) { + std::cout << ((c >> j) & 1); + } + std::cout << std::endl; +} + +void printVector(const std::vector& v) { + std::cout << v.size() << " MSB "; + for(size_t i = 0; i < v.size(); i++) { + std::cout << v.at(i); + } + std::cout << " LSB" << std::endl; +} + +void printVector(const std::string str, const std::vector& v) { + std::cout << str << " " << v.size() << " MSB "; + for(size_t i = 0; i < v.size(); i++) { + std::cout << v.at(i); + } + std::cout << " LSB" << std::endl; +} + +void printVectorAsHex(const std::vector& v) { + unsigned char bytes[int(v.size() / 8)]; + convertVectorToBytes(v, bytes); + + for(int i = 0; i < int(v.size() / 8); i++) { + std::cout << std::setw(2) << std::setfill('0') << std::hex << (int) bytes[i]; + } + std::cout << std::dec << std::endl; +} + +void printVectorAsHex(const std::string str, const std::vector& v) { + unsigned char bytes[int(v.size() / 8)]; + convertVectorToBytes(v, bytes); + + std::cout << str << " "; + for(int i = 0; i < int(v.size() / 8); i++) { + std::cout << std::setw(2) << std::setfill('0') << std::hex << (int) bytes[i]; + } + std::cout << std::dec << std::endl; +} + +void printBytesVector(const std::vector& v) { + std::vector boolVec(v.size() * 8); + convertBytesVectorToVector(v, boolVec); + printVector(boolVec); +} + +void printBytesVector(const std::string str, const std::vector& v) { + std::vector boolVec(v.size() * 8); + convertBytesVectorToVector(v, boolVec); + printVector(str, boolVec); +} + +void printBytesVectorAsHex(const std::vector& v) { + std::vector boolVec(v.size() * 8); + convertBytesVectorToVector(v, boolVec); + printVectorAsHex(boolVec); +} + +void printBytesVectorAsHex(const std::string str, const std::vector& v) { + std::vector boolVec(v.size() * 8); + convertBytesVectorToVector(v, boolVec); + printVectorAsHex(str, boolVec); +} + +void getRandBytes(unsigned char* bytes, int num) { + int ret = RAND_bytes(bytes, num); + if(ret != 1) + std::cout << "rand_bytes error!" << ERR_get_error() << std::endl; +} + +void convertBytesToVector(const unsigned char* bytes, std::vector& v) { + int numBytes = v.size() / 8; + unsigned char c; + for(int i = 0; i < numBytes; i++) { + c = bytes[i]; + + for(int j = 0; j < 8; j++) { + v.at((i*8)+j) = ((c >> (7-j)) & 1); + } + } +} + +void convertVectorToBytes(const std::vector& v, unsigned char* bytes) { + int numBytes = v.size() / 8; + unsigned char c = '\0'; + + for(int i = 0; i < numBytes; i++) { + c = '\0'; + for(int j = 0; j < 8; j++) { + if(j == 7) + c = ((c | v.at((i*8)+j))); + else + c = ((c | v.at((i*8)+j)) << 1); + } + bytes[i] = c; + } +} + +void convertBytesToBytesVector(const unsigned char* bytes, std::vector& v) { + for(size_t i = 0; i < v.size(); i++) { + v.at(i) = bytes[i]; + } +} + +void convertBytesVectorToBytes(const std::vector& v, unsigned char* bytes) { + for(size_t i = 0; i < v.size(); i++) { + bytes[i] = v.at(i); + } +} + +void convertBytesVectorToVector(const std::vector& bytes, std::vector& v) { + v.resize(bytes.size() * 8); + unsigned char bytesArr[bytes.size()]; + convertBytesVectorToBytes(bytes, bytesArr); + convertBytesToVector(bytesArr, v); +} + +void convertVectorToBytesVector(const std::vector& v, std::vector& bytes) { + unsigned char bytesArr[int(ceil(v.size() / 8.))]; + convertVectorToBytes(v, bytesArr); + convertBytesToBytesVector(bytesArr, bytes); +} + +void convertIntToBytesVector(const uint64_t val_int, std::vector& bytes) { + for(size_t i = 0; i < bytes.size(); i++) { + bytes[bytes.size()-1-i] = (val_int >> (i * 8)); + } +} + +void convertIntToVector(uint64_t val, std::vector& v) +{ + v.resize(64); + for(unsigned int i = 0; i < 64; ++i, val >>= 1) { + v.at(63 - i) = val & 0x01; + } +} + +uint64_t convertVectorToInt(const std::vector& v) { + if (v.size() > 64) { + throw std::length_error ("boolean vector can't be larger than 64 bits"); + } + + uint64_t result = 0; + for (size_t i=0; i& bytes) { + uint64_t val_int = 0; + + for(size_t i = 0; i < bytes.size(); i++) { + val_int = val_int + (((uint64_t)bytes[i]) << ((bytes.size()-1-i) * 8)); + } + + return val_int; +} + +void concatenateVectors(const std::vector& A, const std::vector& B, std::vector& result) { + result.reserve(A.size() + B.size()); + result.insert(result.end(), A.begin(), A.end()); + result.insert(result.end(), B.begin(), B.end()); +} + +void concatenateVectors(const std::vector& A, const std::vector& B, std::vector& result) { + result.reserve(A.size() + B.size()); + result.insert(result.end(), A.begin(), A.end()); + result.insert(result.end(), B.begin(), B.end()); +} + +void concatenateVectors(const std::vector& A, const std::vector& B, const std::vector& C, std::vector& result) { + result.reserve(A.size() + B.size() + C.size()); + result.insert(result.end(), A.begin(), A.end()); + result.insert(result.end(), B.begin(), B.end()); + result.insert(result.end(), C.begin(), C.end()); +} + +void concatenateVectors(const std::vector& A, const std::vector& B, const std::vector& C, std::vector& result) { + result.reserve(A.size() + B.size() + C.size()); + result.insert(result.end(), A.begin(), A.end()); + result.insert(result.end(), B.begin(), B.end()); + result.insert(result.end(), C.begin(), C.end()); +} + +void sha256(const unsigned char* input, unsigned char* hash, int len) { + SHA256_CTX_mod ctx256; + + sha256_init(&ctx256); + sha256_update(&ctx256, input, len); + sha256_final_no_padding(&ctx256, hash); +} + +void sha256(SHA256_CTX_mod* ctx256, const unsigned char* input, unsigned char* hash, int len) { + sha256_init(ctx256); + sha256_update(ctx256, input, len); + sha256_final_no_padding(ctx256, hash); +} + +void hashVector(SHA256_CTX_mod* ctx256, const std::vector input, std::vector& output) { + int size = int(input.size() / 8); + unsigned char bytes[size]; + convertVectorToBytes(input, bytes); + + unsigned char hash[SHA256_BLOCK_SIZE]; + sha256(ctx256, bytes, hash, (int)size); + + convertBytesToVector(hash, output); +} + +void hashVector(SHA256_CTX_mod* ctx256, const std::vector input, std::vector& output) { + int size = int(input.size()); + unsigned char bytes[size]; + convertBytesVectorToBytes(input, bytes); + + unsigned char hash[SHA256_BLOCK_SIZE]; + sha256(ctx256, bytes, hash, (int)size); + + convertBytesToBytesVector(hash, output); +} + +void hashVector(const std::vector input, std::vector& output) { + SHA256_CTX_mod ctx256; + + int size = int(input.size() / 8); + unsigned char bytes[size]; + convertVectorToBytes(input, bytes); + + unsigned char hash[SHA256_BLOCK_SIZE]; + sha256(&ctx256, bytes, hash, (int)size); + + convertBytesToVector(hash, output); +} + +void hashVector(const std::vector input, std::vector& output) { + SHA256_CTX_mod ctx256; + + int size = int(input.size()); + unsigned char bytes[size]; + convertBytesVectorToBytes(input, bytes); + + unsigned char hash[SHA256_BLOCK_SIZE]; + sha256(&ctx256, bytes, hash, (int)size); + + convertBytesToBytesVector(hash, output); +} + +void hashVectors(SHA256_CTX_mod* ctx256, const std::vector left, const std::vector right, std::vector& output) { + std::vector concat; + concatenateVectors(left, right, concat); + + int size = int(concat.size() / 8); + unsigned char bytes[size]; + convertVectorToBytes(concat, bytes); + + unsigned char hash[SHA256_BLOCK_SIZE]; + sha256(ctx256, bytes, hash, (int)size); + + convertBytesToVector(hash, output); +} + +void hashVectors(SHA256_CTX_mod* ctx256, const std::vector left, const std::vector right, std::vector& output) { + std::vector concat; + concatenateVectors(left, right, concat); + + int size = int(concat.size()); + unsigned char bytes[size]; + convertBytesVectorToBytes(concat, bytes); + + unsigned char hash[SHA256_BLOCK_SIZE]; + sha256(ctx256, bytes, hash, (int)size); + + convertBytesToBytesVector(hash, output); +} + +void hashVectors(const std::vector left, const std::vector right, std::vector& output) { + std::cout << std::endl; + + std::vector concat; + concatenateVectors(left, right, concat); + + int size = int(concat.size() / 8); + unsigned char bytes[size]; + convertVectorToBytes(concat, bytes); + + unsigned char hash[SHA256_BLOCK_SIZE]; + sha256(bytes, hash, (int)size); + + convertBytesToVector(hash, output); +} + +void hashVectors(const std::vector left, const std::vector right, std::vector& output) { + std::vector concat; + concatenateVectors(left, right, concat); + + int size = int(concat.size()); + unsigned char bytes[size]; + convertBytesVectorToBytes(concat, bytes); + + unsigned char hash[SHA256_BLOCK_SIZE]; + sha256(bytes, hash, (int)size); + + convertBytesToBytesVector(hash, output); +} + +bool VectorIsZero(const std::vector test) { + // XXX: not time safe + return (test.end() == std::find(test.begin(), test.end(), true)); +} + +size_t countOnes(const std::vector& vec) { + return count(vec.begin(), vec.end(), true); +} + +std::vector vectorSlice(const std::vector& vec, size_t start, size_t length) { + std::vector slice(length); + for (size_t i = 0; i < length; i++) { + slice.at(i) = vec.at(start + i); + } + return slice; +} + +} /* namespace libzerocash */ + diff --git a/src/zerocash/utils/util.h b/src/zerocash/utils/util.h new file mode 100644 index 000000000..1029fffab --- /dev/null +++ b/src/zerocash/utils/util.h @@ -0,0 +1,90 @@ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include +#include +#include +#include + +#include "sha256.h" + +namespace libzerocash { + +void printChar(const unsigned char c); + +void printVector(const std::vector& v); + +void printVector(const std::string str, const std::vector& v); + +void printVectorAsHex(const std::vector& v); + +void printVectorAsHex(const std::string str, const std::vector& v); + +void printBytesVector(const std::vector& v); + +void printBytesVector(const std::string str, const std::vector& v); + +void printBytesVectorAsHex(const std::vector& v); + +void printBytesVectorAsHex(const std::string str, const std::vector& v); + +void getRandBytes(unsigned char* bytes, int num); + +void convertBytesToVector(const unsigned char* bytes, std::vector& v); + +void convertVectorToBytes(const std::vector& v, unsigned char* bytes); + +void convertBytesToBytesVector(const unsigned char* bytes, std::vector& v); + +void convertBytesVectorToBytes(const std::vector& v, unsigned char* bytes); + +void convertBytesVectorToVector(const std::vector& bytes, std::vector& v); + +void convertVectorToBytesVector(const std::vector& v, std::vector& bytes); + +void convertIntToBytesVector(const uint64_t val_int, std::vector& bytes); + +void convertIntToVector(uint64_t val, std::vector& v); + +uint64_t convertVectorToInt(const std::vector& v); + +uint64_t convertBytesVectorToInt(const std::vector& bytes); + +void concatenateVectors(const std::vector& A, const std::vector& B, std::vector& result); + +void concatenateVectors(const std::vector& A, const std::vector& B, std::vector& result); + +void concatenateVectors(const std::vector& A, const std::vector& B, const std::vector& C, std::vector& result); + +void concatenateVectors(const std::vector& A, const std::vector& B, const std::vector& C, std::vector& result); + +void sha256(const unsigned char* input, unsigned char* hash, int len); + +void sha256(SHA256_CTX_mod* ctx256, const unsigned char* input, unsigned char* hash, int len); + +void hashVector(SHA256_CTX_mod* ctx256, const std::vector input, std::vector& output); + +void hashVector(SHA256_CTX_mod* ctx256, const std::vector input, std::vector& output); + +void hashVector(const std::vector input, std::vector& output); + +void hashVector(const std::vector input, std::vector& output); + +void hashVectors(SHA256_CTX_mod* ctx256, const std::vector left, const std::vector right, std::vector& output); + +void hashVectors(SHA256_CTX_mod* ctx256, const std::vector left, const std::vector right, std::vector& output); + +void hashVectors(const std::vector left, const std::vector right, std::vector& output); + +void hashVectors(const std::vector left, const std::vector right, std::vector& output); + +bool VectorIsZero(const std::vector test); + +size_t countOnes(const std::vector& vec); + +std::vector vectorSlice(const std::vector& vec, size_t start, size_t length); + +} /* namespace libzerocash */ +#endif /* UTIL_H_ */ + + diff --git a/src/zerocash/zerocash_pour_gadget.hpp b/src/zerocash/zerocash_pour_gadget.hpp new file mode 100644 index 000000000..a08f06a89 --- /dev/null +++ b/src/zerocash/zerocash_pour_gadget.hpp @@ -0,0 +1,188 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the Zerocash Pour gadget. + + The Pour gadget implements the NP statement "Zerocash Pour" described in \[BCGGMTV14]. + + + References: + + \[BCGGMTV14]: + "Zerocash: Decentralized Anonymous Payments from Bitcoin", + Eli Ben-Sasson, Alessandro Chiesa, Christina Garman, Matthew Green, Ian Miers, Eran Tromer, Madars Virza, + S&P 2014, + + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ZEROCASH_POUR_GADGET_HPP_ +#define ZEROCASH_POUR_GADGET_HPP_ + +#include "zerocash_pour_params.hpp" +#include "libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp" +#include "libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp" + +namespace libzerocash { + +using namespace libsnark; + +/** + * Gadget for the NP statement Zerocash Pour. + * + * More precisely, this gadgets checks the following NP statement. + * (See Section 4.2 and Section 5.1 of the Zerocash paper for more details.) + * + * (A) old_coin_commitment_variables[i] appears on path old_coin_authentication_paths[i] to merkle_tree_root_variable + * + * (B) old_address_public_keys[i] + * = PRF_{old_address_secret_key_variables[i]}^{addr}(z) + * = H(old_address_secret_key_variables[i] || 00 || z) + * where z = 0...0 + * + * (C) old_coin_serial_number_variables[i] + * = PRF_{old_address_secret_key_variables[i]}^{sn}(old_coin_serial_number_nonce_variables[0..254]) + * = H(old_address_secret_key_variables[i] || 01 || old_coin_serial_number_nonce_variables[0..254]) + * + * Properties (D0) and (D1) jointly ensure that + * + * old_coin_value_commitment_nonces[i] + * = COMM_{old_address_commitment_nonce_variables[i]}(old_address_public_key_variables[i] || old_coin_serial_number_nonce_variables[i]) + * + * as follows: + * + * (D0) commitments_to_old_address_public_keys[i] + * = H(old_address_public_key_variables[i] || old_coin_serial_number_nonce_variables[i]) + * + * (D1) old_coin_value_commitment_nonces[i] + * = H(old_address_commitment_nonce_variables[i] || commitments_to_old_address_public_keys[i] [0..128]) + * + * Given this, (D2) computes old_coin_commitments: + * + * (D2) old_coin_commitment_variables[i] + * = COMM_s(old_coin_value_variables[i] || old_coin_value_commitment_nonces[i]) + * = H(old_coin_value_commitment_nonces[i] || 0^{192} || old_coin_value_variables[i]) + * + * Here we ignore commitment randomness s, because + * k = old_coin_value_commitment_nonces[i] + * is an output of a statistically-hiding commitment scheme. + * + * While (D0) and (D1) check that old coin commitments are well formed, + * (E0) and (E1) check the same properties for new coins. + * + * (F) mac_of_signature_public_key_hash_variables[i] + * = PRF_{old_address_secret_key_variables[i]}^{pk}(i || signature_public_key_hash_variable) + * = H(old_address_secret_key_variables[i] || 10 || i || signature_public_key_hash_variable) + * + * Here signature_public_key_hash is truncated so that the entire argument fits inside SHA256 block. + * Furthermore, the representation of i is MSB to LSB and is exactly log2(num_old_coins) bits long. + */ +template +class zerocash_pour_gadget : public gadget { +public: + size_t tree_depth; + + pb_variable_array input_as_field_elements; /* R1CS input */ + pb_variable_array input_as_bits; /* unpacked R1CS input */ + std::shared_ptr > unpack_inputs; + + /* individual components of the unpacked R1CS input */ + std::shared_ptr > merkle_tree_root_variable; + std::vector > > old_coin_serial_number_variables; + pb_variable_array old_coin_enforce_commitment; + std::vector > > new_coin_commitment_variables; + pb_variable_array public_old_value_variable; + pb_variable_array public_new_value_variable; + std::shared_ptr > signature_public_key_hash_variable; + std::vector > > mac_of_signature_public_key_hash_variables; + + /* TODO */ + pb_variable zero; + + std::vector > new_address_public_key_variables; + std::vector > old_address_secret_key_variables; + std::vector > new_address_commitment_nonce_variables; + std::vector > old_address_commitment_nonce_variables; + std::vector > new_coin_serial_number_nonce_variables; + std::vector > old_coin_serial_number_nonce_variables; + std::vector > new_coin_value_variables; + std::vector > old_coin_value_variables; + + std::vector > > prf_for_old_coin_serial_number_input_variables; + std::vector > > prfs_for_old_coin_serial_numbers; // (C) + + std::vector > > old_address_public_key_variables; + std::vector > > prf_for_old_address_public_key_input_variables; + std::vector > > prfs_for_old_address_public_keys; // (B) + + std::vector > > commitments_to_old_address_public_keys; + std::vector > > commit_to_old_address_public_key_input_variables; + std::vector > > commit_to_old_address_public_keys; // (D0) + + std::vector > > old_coin_value_commitment_nonces; + std::vector > > commit_to_old_coin_value_commitment_nonce_input_variables; + std::vector > > commit_to_old_coin_value_commitment_nonces; // (D1) + + std::vector > > old_coin_commitment_variables; + std::vector > > compute_old_coin_commitment_input_variables; + std::vector > > compute_old_coin_commitments; // (D2) + + std::vector > > commitments_to_new_address_public_keys; + std::vector > > commit_to_new_address_public_key_input_variables; + std::vector > > commit_to_new_address_public_keys; // (E0) + + std::vector > > new_coin_value_commitment_nonces; + std::vector > > commit_to_new_coin_value_commitment_nonce_input_variables; + std::vector > > commit_to_new_coin_value_commitment_nonces; // (E1) + + std::vector > > compute_new_coin_commitment_input_variables; + std::vector > > compute_new_coin_commitments; // (E2) + + std::vector > > prf_for_macs_of_signature_public_key_hash_input_variables; + std::vector > > prfs_for_macs_of_signature_public_key_hash; // (F) + + std::vector > old_coin_merkle_tree_position_variables; + std::vector > > > old_coin_authentication_path_variables; + std::vector > > > old_coin_commitments_in_tree; // (A) + + size_t num_old_coins; + size_t num_new_coins; + + zerocash_pour_gadget(protoboard &pb, const size_t num_old_coins, const size_t num_new_coins, const size_t tree_depth, const std::string &annotation_prefix); + void generate_r1cs_constraints(); + void generate_r1cs_witness(const std::vector &old_coin_authentication_paths, + const std::vector &old_coin_merkle_tree_positions, + const bit_vector &merkle_tree_root, + const std::vector &new_address_public_keys, + const std::vector &old_address_secret_keys, + const std::vector &new_address_commitment_nonces, + const std::vector &old_address_commitment_nonces, + const std::vector &new_coin_serial_number_nonces, + const std::vector &old_coin_serial_number_nonces, + const std::vector &new_coin_values, + const bit_vector &public_old_value, + const bit_vector &public_new_value, + const std::vector &old_coin_values, + const bit_vector &signature_public_key_hash); +}; + +template +r1cs_primary_input zerocash_pour_input_map(const size_t num_old_coins, + const size_t num_new_coins, + const bit_vector &merkle_tree_root, + const std::vector &old_coin_serial_numbers, + const std::vector &new_coin_commitments, + const bit_vector &public_old_value, + const bit_vector &public_new_value, + const bit_vector &signature_public_key_hash, + const std::vector &signature_public_key_hash_macs); + +} // libzerocash + +#include "zerocash_pour_gadget.tcc" + +#endif // ZEROCASH_POUR_GADGET_HPP_ diff --git a/src/zerocash/zerocash_pour_gadget.tcc b/src/zerocash/zerocash_pour_gadget.tcc new file mode 100644 index 000000000..43dcd27af --- /dev/null +++ b/src/zerocash/zerocash_pour_gadget.tcc @@ -0,0 +1,503 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the Zerocash Pour gadget. + + See zerocash_pour_gadget.hpp . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "algebra/fields/field_utils.hpp" + +namespace libzerocash { + +template +zerocash_pour_gadget::zerocash_pour_gadget(protoboard &pb, + const size_t num_old_coins, + const size_t num_new_coins, + const size_t tree_depth, + const std::string &annotation_prefix) : + gadget(pb, FMT(annotation_prefix, " zerocash_pour_gadget")), + tree_depth(tree_depth), + num_old_coins(num_old_coins), + num_new_coins(num_new_coins) +{ + /* allocate packed inputs */ + const size_t input_size_in_bits = sha256_digest_len + num_old_coins*sha256_digest_len + num_new_coins*sha256_digest_len + (coin_value_length * 2) + (num_old_coins + 1) * sha256_digest_len; + const size_t input_size_in_field_elements = div_ceil(input_size_in_bits, FieldT::capacity()); + input_as_field_elements.allocate(pb, input_size_in_field_elements, FMT(annotation_prefix, " input_as_field_elements")); + this->pb.set_input_sizes(input_size_in_field_elements); + + /* allocate inputs */ + merkle_tree_root_variable.reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " merkle_tree_root_variable"))); + + old_coin_enforce_commitment.allocate(pb, num_old_coins, FMT(annotation_prefix, " old_coin_enforce_commitment")); + old_coin_serial_number_variables.resize(num_old_coins); + for (size_t i = 0; i < num_old_coins; ++i) + { + old_coin_serial_number_variables[i].reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " old_coin_serial_number_variables_%zu", i))); + } + + new_coin_commitment_variables.resize(num_new_coins); + for (size_t i = 0; i < num_new_coins; ++i) + { + new_coin_commitment_variables[i].reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " new_coin_commitment_variables_%zu", i))); + } + + public_old_value_variable.allocate(pb, coin_value_length, FMT(annotation_prefix, " public_old_value_variable")); + public_new_value_variable.allocate(pb, coin_value_length, FMT(annotation_prefix, " public_new_value_variable")); + signature_public_key_hash_variable.reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " signature_public_key_hash"))); + + mac_of_signature_public_key_hash_variables.resize(num_old_coins); + for (size_t i = 0; i < num_old_coins; ++i) + { + mac_of_signature_public_key_hash_variables[i].reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " mac_of_signature_public_key_hash_variables_%zu", i))); + } + + /* do the multipacking */ + input_as_bits.insert(input_as_bits.end(), merkle_tree_root_variable->bits.begin(), merkle_tree_root_variable->bits.end()); + for (size_t i = 0; i < num_old_coins; ++i) + { + input_as_bits.insert(input_as_bits.end(), old_coin_serial_number_variables[i]->bits.begin(), old_coin_serial_number_variables[i]->bits.end()); + } + for (size_t i = 0; i < num_new_coins; ++i) + { + input_as_bits.insert(input_as_bits.end(), new_coin_commitment_variables[i]->bits.begin(), new_coin_commitment_variables[i]->bits.end()); + } + input_as_bits.insert(input_as_bits.end(), public_old_value_variable.begin(), public_old_value_variable.end()); + input_as_bits.insert(input_as_bits.end(), public_new_value_variable.begin(), public_new_value_variable.end()); + input_as_bits.insert(input_as_bits.end(), signature_public_key_hash_variable->bits.begin(), signature_public_key_hash_variable->bits.end()); + for (size_t i = 0; i < num_old_coins; ++i) + { + input_as_bits.insert(input_as_bits.end(), mac_of_signature_public_key_hash_variables[i]->bits.begin(), mac_of_signature_public_key_hash_variables[i]->bits.end()); + } + assert(input_as_bits.size() == input_size_in_bits); + unpack_inputs.reset(new multipacking_gadget(this->pb, input_as_bits, input_as_field_elements, FieldT::capacity(), FMT(this->annotation_prefix, " unpack_inputs"))); + + pb_linear_combination_array IV = SHA256_default_IV(pb); + zero.allocate(this->pb, FMT(this->annotation_prefix, " zero")); /* TODO */ + + /* allocate witness */ + new_address_public_key_variables.resize(num_new_coins); + new_address_commitment_nonce_variables.resize(num_new_coins); + new_coin_serial_number_nonce_variables.resize(num_new_coins); + new_coin_value_variables.resize(num_new_coins); + for (size_t i = 0; i < num_new_coins; ++i) + { + new_address_public_key_variables[i].allocate(pb, address_public_key_length, FMT(annotation_prefix, " new_address_public_key_variables_%zu", i)); + new_address_commitment_nonce_variables[i].allocate(pb, address_commitment_nonce_length, FMT(annotation_prefix, " new_address_commitment_nonce_variables_%zu", i)); + new_coin_serial_number_nonce_variables[i].allocate(pb, serial_number_nonce_length, FMT(annotation_prefix, " new_coin_serial_number_nonce_variables_%zu", i)); + new_coin_value_variables[i].allocate(pb, coin_value_length, FMT(annotation_prefix, " new_coin_value_variables_%zu", i)); + } + + old_address_secret_key_variables.resize(num_old_coins); + old_address_commitment_nonce_variables.resize(num_old_coins); + old_coin_serial_number_nonce_variables.resize(num_old_coins); + old_coin_value_variables.resize(num_old_coins); + for (size_t i = 0; i < num_old_coins; ++i) + { + old_address_secret_key_variables[i].allocate(pb, address_secret_key_length, FMT(annotation_prefix, " old_address_secret_key_variables_%zu", i)); + old_address_commitment_nonce_variables[i].allocate(pb, address_commitment_nonce_length, FMT(annotation_prefix, " old_address_commitment_nonce_variables_%zu", i)); + old_coin_serial_number_nonce_variables[i].allocate(pb, serial_number_nonce_length, FMT(annotation_prefix, " old_coin_serial_number_nonce_variables_%zu", i)); + old_coin_value_variables[i].allocate(pb, coin_value_length, FMT(annotation_prefix, " old_coin_value_variables_%zu", i)); + } + + /* do the actual hashing */ + pb_variable_array zero_one; + zero_one.emplace_back(zero); + zero_one.emplace_back(ONE); + + prf_for_old_coin_serial_number_input_variables.resize(num_old_coins); + prfs_for_old_coin_serial_numbers.resize(num_old_coins); + for (size_t i = 0; i < num_old_coins; ++i) + { + /* (C) old_coin_serial_number_variables[i] = PRF_{old_address_secret_key_variables[i]}^{sn} + (old_coin_serial_number_nonce_variables[0..254]) = + H(old_address_secret_key_variables[i] || 01 || old_coin_serial_number_nonce_variables[0..254]) */ + prf_for_old_coin_serial_number_input_variables[i].reset(new block_variable(pb, { + old_address_secret_key_variables[i], + zero_one, + pb_variable_array(old_coin_serial_number_nonce_variables[i].begin(), + old_coin_serial_number_nonce_variables[i].begin() + truncated_serial_number_length) }, + FMT(annotation_prefix, " prf_for_old_coin_serial_number_input_variables_%zu", i))); + prfs_for_old_coin_serial_numbers[i].reset(new sha256_compression_function_gadget(pb, IV, prf_for_old_coin_serial_number_input_variables[i]->bits, *old_coin_serial_number_variables[i], FMT(annotation_prefix, " prfs_for_old_coin_serial_numbers_%zu", i))); + } + + old_address_public_key_variables.resize(num_old_coins); + prf_for_old_address_public_key_input_variables.resize(num_old_coins); + prfs_for_old_address_public_keys.resize(num_old_coins); + + for (size_t i = 0; i < num_old_coins; ++i) + { + old_address_public_key_variables[i].reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " old_address_public_key_variables_%zu", i))); + + /* (B) old_address_public_keys[i] = PRF_{old_address_secret_key_variables[i]}^{addr}(z) = + H(old_address_secret_key_variables[i] || 00 || z), where z = 0...0 */ + pb_variable_array addr_pk_pad(address_public_key_padding_length, zero); + prf_for_old_address_public_key_input_variables[i].reset(new block_variable(pb, + { old_address_secret_key_variables[i], addr_pk_pad }, + FMT(annotation_prefix, " prf_for_old_address_public_key_input_variables_%zu", i))); + prfs_for_old_address_public_keys[i].reset(new sha256_compression_function_gadget(pb, + IV, + prf_for_old_address_public_key_input_variables[i]->bits, + *old_address_public_key_variables[i], + FMT(annotation_prefix, " prfs_for_old_address_public_keys_%zu", i))); + } + + commitments_to_old_address_public_keys.resize(num_old_coins); + commit_to_old_address_public_key_input_variables.resize(num_old_coins); + commit_to_old_address_public_keys.resize(num_old_coins); + + for (size_t i = 0; i < num_old_coins; ++i) + { + /* (D0) commitments_to_old_address_public_keys[i] = H(old_address_public_key_variables[i] || old_coin_serial_number_nonce_variables[i]) */ + commitments_to_old_address_public_keys[i].reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " commitments_to_old_address_public_keys_%zu", i))); + commit_to_old_address_public_key_input_variables[i].reset(new block_variable(pb, { old_address_public_key_variables[i]->bits, old_coin_serial_number_nonce_variables[i] }, FMT(annotation_prefix, " commit_to_old_address_public_key_input_variables_%zu", i))); + commit_to_old_address_public_keys[i].reset(new sha256_compression_function_gadget(pb, IV, commit_to_old_address_public_key_input_variables[i]->bits, *commitments_to_old_address_public_keys[i], FMT(annotation_prefix, " commit_to_old_address_public_keys_%zu", i))); + } + + old_coin_value_commitment_nonces.resize(num_old_coins); + commit_to_old_coin_value_commitment_nonce_input_variables.resize(num_old_coins); + commit_to_old_coin_value_commitment_nonces.resize(num_old_coins); + + for (size_t i = 0; i < num_old_coins; ++i) + { + /* (D1) old_coin_value_commitment_nonces[i] = + H(old_address_commitment_nonce_variables[i] || commitments_to_old_address_public_keys[i] [0..128]) */ + old_coin_value_commitment_nonces[i].reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " old_coin_value_commitment_nonces_%zu", i))); + commit_to_old_coin_value_commitment_nonce_input_variables[i].reset(new block_variable(pb, { old_address_commitment_nonce_variables[i], pb_variable_array(commitments_to_old_address_public_keys[i]->bits.begin(), commitments_to_old_address_public_keys[i]->bits.begin()+ truncated_coin_commitment_length) }, FMT(annotation_prefix, " commit_to_old_coin_value_commitment_nonce_input_variables_%zu", i))); + commit_to_old_coin_value_commitment_nonces[i].reset(new sha256_compression_function_gadget(pb, IV, commit_to_old_coin_value_commitment_nonce_input_variables[i]->bits, *old_coin_value_commitment_nonces[i], FMT(annotation_prefix, " commit_to_old_coin_value_commitment_nonces_%zu", i))); + } + + pb_variable_array coincomm_pad(coin_commitment_padding_length, zero); + old_coin_commitment_variables.resize(num_old_coins); + compute_old_coin_commitment_input_variables.resize(num_old_coins); + compute_old_coin_commitments.resize(num_old_coins); + + for (size_t i = 0; i < num_old_coins; ++i) + { + /* (D2) old_coin_commitment_variables[i] = COMM_s(old_coin_value_variables[i] || old_coin_value_commitment_nonces[i]) + H(old_coin_value_commitment_nonces[i] || 0^{192} || old_coin_value_variables[i]) + + Here we ignore commitment randomness s, as k = old_coin_value_commitment_nonces[i] is an output of a + statistically hiding commitment scheme. */ + old_coin_commitment_variables[i].reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " old_coin_commitment_variables_%zu", i))); + compute_old_coin_commitment_input_variables[i].reset(new block_variable(pb, { old_coin_value_commitment_nonces[i]->bits, coincomm_pad, old_coin_value_variables[i] }, FMT(annotation_prefix, " compute_old_coin_commitment_input_variables_%zu", i))); + compute_old_coin_commitments[i].reset(new sha256_compression_function_gadget(pb, IV, compute_old_coin_commitment_input_variables[i]->bits, *old_coin_commitment_variables[i], FMT(annotation_prefix, " compute_old_coin_commitment_%zu", i))); + } + + commitments_to_new_address_public_keys.resize(num_new_coins); + commit_to_new_address_public_key_input_variables.resize(num_new_coins); + commit_to_new_address_public_keys.resize(num_new_coins); + + for (size_t i = 0; i < num_new_coins; ++i) + { + /* (E0) commitments_to_new_address_public_keys[i] = H(new_address_public_key_variables[i] || new_coin_serial_number_nonce_variables[i]) */ + commitments_to_new_address_public_keys[i].reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " commitments_to_new_address_public_keys_%zu", i))); + commit_to_new_address_public_key_input_variables[i].reset(new block_variable(pb, { new_address_public_key_variables[i], new_coin_serial_number_nonce_variables[i] }, FMT(annotation_prefix, " commit_to_new_address_public_key_input_variables_%zu", i))); + commit_to_new_address_public_keys[i].reset(new sha256_compression_function_gadget(pb, IV, commit_to_new_address_public_key_input_variables[i]->bits, *commitments_to_new_address_public_keys[i], FMT(annotation_prefix, " commit_to_new_address_public_keys_%zu", i))); + } + + new_coin_value_commitment_nonces.resize(num_new_coins); + commit_to_new_coin_value_commitment_nonce_input_variables.resize(num_new_coins); + commit_to_new_coin_value_commitment_nonces.resize(num_new_coins); + for (size_t i = 0; i < num_new_coins; ++i) + { + /* (E1) new_coin_value_commitment_nonces[i] = + H(new_address_commitment_nonce_variables[i] || commitments_to_new_address_public_keys[i] [0..128]) */ + new_coin_value_commitment_nonces[i].reset(new digest_variable(pb, sha256_digest_len, FMT(annotation_prefix, " new_coin_value_commitment_nonces_%zu", i))); + commit_to_new_coin_value_commitment_nonce_input_variables[i].reset(new block_variable(pb, { new_address_commitment_nonce_variables[i], pb_variable_array(commitments_to_new_address_public_keys[i]->bits.begin(), commitments_to_new_address_public_keys[i]->bits.begin()+ truncated_coin_commitment_length) }, FMT(annotation_prefix, " commit_to_new_coin_value_commitment_nonce_input_variables_%zu", i))); + commit_to_new_coin_value_commitment_nonces[i].reset(new sha256_compression_function_gadget(pb, IV, commit_to_new_coin_value_commitment_nonce_input_variables[i]->bits, *new_coin_value_commitment_nonces[i], FMT(annotation_prefix, " commit_to_new_coin_value_commitment_nonces_%zu", i))); + } + + compute_new_coin_commitment_input_variables.resize(num_new_coins); + compute_new_coin_commitments.resize(num_new_coins); + + for (size_t i = 0; i < num_new_coins; ++i) + { + /* (E2) new_coin_commitment_variables[i] = COMM_s(new_coin_value_variables[i] || new_coin_value_commitment_nonces[i]) + H(new_coin_value_commitment_nonces[i] || 0^{192} || new_coin_value_variables[i]) */ + compute_new_coin_commitment_input_variables[i].reset(new block_variable(pb, { new_coin_value_commitment_nonces[i]->bits, coincomm_pad, new_coin_value_variables[i] }, FMT(annotation_prefix, " compute_new_coin_commitment_input_variables_%zu", i))); + compute_new_coin_commitments[i].reset(new sha256_compression_function_gadget(pb, IV, compute_new_coin_commitment_input_variables[i]->bits, *new_coin_commitment_variables[i], FMT(annotation_prefix, " compute_new_coin_commitment_%zu", i))); + } + + /* compute signature public key macs */ + prf_for_macs_of_signature_public_key_hash_input_variables.resize(num_old_coins); + prfs_for_macs_of_signature_public_key_hash.resize(num_old_coins); + const size_t truncated_signature_public_key_hash_length = indexed_signature_public_key_hash_length - log2(num_old_coins); + + for (size_t i = 0; i < num_old_coins; ++i) + { + /* (F) mac_of_signature_public_key_hash_variables[i] = PRF_{old_address_secret_key_variables[i]}^{pk} + (i || signature_public_key_hash_variable) = + H(old_address_secret_key_variables[i] || 10 || i || signature_public_key_hash_variable) + + Here signature_public_key_hash is truncated so that the entire argument fits inside SHA256 block. + Furthermore, the representation of i is MSB to LSB and is exactly log2(num_old_coins) bits long. */ + pb_variable_array prf_padding; + prf_padding.emplace_back(ONE); + prf_padding.emplace_back(zero); + + for (size_t j = 0; j < log2(num_old_coins); ++j) + { + prf_padding.emplace_back((i >> (log2(num_old_coins) - j - 1)) & 1 ? ONE : zero); + } + + prf_for_macs_of_signature_public_key_hash_input_variables[i].reset(new block_variable(pb, { old_address_secret_key_variables[i], prf_padding, pb_variable_array(signature_public_key_hash_variable->bits.begin(), signature_public_key_hash_variable->bits.begin()+truncated_signature_public_key_hash_length) }, FMT(annotation_prefix, " prf_for_macs_of_signature_public_key_hash_input_variables_%zu", i))); + prfs_for_macs_of_signature_public_key_hash[i].reset(new sha256_compression_function_gadget(pb, IV, prf_for_macs_of_signature_public_key_hash_input_variables[i]->bits, *mac_of_signature_public_key_hash_variables[i], FMT(annotation_prefix, " prfs_for_macs_of_signature_public_key_hash_%zu", i))); + } + + /* prove membership in the Merkle tree*/ + old_coin_merkle_tree_position_variables.resize(num_old_coins); + old_coin_authentication_path_variables.resize(num_old_coins); + old_coin_commitments_in_tree.resize(num_old_coins); + for (size_t i = 0; i < num_old_coins; ++i) + { + /* (A) old_coin_commitment_variables[i] appears on path old_coin_authentication_paths[i] + to merkle_tree_root_variable */ + old_coin_merkle_tree_position_variables[i].allocate(pb, tree_depth, FMT(annotation_prefix, " old_coin_merkle_tree_position_variables_%zu", i)); + old_coin_authentication_path_variables[i].reset(new merkle_authentication_path_variable >(pb, tree_depth, FMT(annotation_prefix, " old_coin_authentication_path_variables_%zu", i))); + old_coin_commitments_in_tree[i].reset(new merkle_tree_check_read_gadget >( + pb, tree_depth, old_coin_merkle_tree_position_variables[i], *old_coin_commitment_variables[i], *merkle_tree_root_variable, + *old_coin_authentication_path_variables[i], old_coin_enforce_commitment[i], FMT(annotation_prefix, " old_coin_commitments_in_tree_%zu", i))); + } +} + +template +void zerocash_pour_gadget::generate_r1cs_constraints() +{ + generate_r1cs_equals_const_constraint(this->pb, zero, FieldT::zero(), FMT(this->annotation_prefix, " zero")); + + for (size_t i = 0; i < num_old_coins; ++i) + { + prfs_for_old_coin_serial_numbers[i]->generate_r1cs_constraints(); + prfs_for_old_address_public_keys[i]->generate_r1cs_constraints(); + commit_to_old_address_public_keys[i]->generate_r1cs_constraints(); + commit_to_old_coin_value_commitment_nonces[i]->generate_r1cs_constraints(); + compute_old_coin_commitments[i]->generate_r1cs_constraints(); + old_coin_commitments_in_tree[i]->generate_r1cs_constraints(); + prfs_for_macs_of_signature_public_key_hash[i]->generate_r1cs_constraints(); + + for (size_t j = 0; j < tree_depth; ++j) + { + generate_boolean_r1cs_constraint(this->pb, old_coin_merkle_tree_position_variables[i][j], FMT(this->annotation_prefix, " old_coin_merkle_tree_position_variables_%zu_%zu", i, j)); + } + } + + for (size_t i = 0; i < num_new_coins; ++i) + { + commit_to_new_address_public_keys[i]->generate_r1cs_constraints(); + commit_to_new_coin_value_commitment_nonces[i]->generate_r1cs_constraints(); + compute_new_coin_commitments[i]->generate_r1cs_constraints(); + } + + unpack_inputs->generate_r1cs_constraints(true); + + /* ensure bitness of all values */ + for (size_t j = 0; j < coin_value_length; ++j) + { + for (size_t i = 0; i < num_old_coins; ++i) + { + generate_boolean_r1cs_constraint(this->pb, old_coin_value_variables[i][j], FMT(this->annotation_prefix, " old_coin_value_variables_%zu_%zu", i, j)); + } + for (size_t i = 0; i < num_new_coins; ++i) + { + generate_boolean_r1cs_constraint(this->pb, new_coin_value_variables[i][j], FMT(this->annotation_prefix, " new_coin_value_variables_%zu_%zu", i, j)); + } + } + + for (size_t i = 0; i < num_old_coins; ++i) + { + generate_boolean_r1cs_constraint(this->pb, old_coin_enforce_commitment[i], FMT(this->annotation_prefix, " old_coin_enforce_commitment_%zu", i)); + this->pb.add_r1cs_constraint(r1cs_constraint( + pb_packing_sum(pb_variable_array(old_coin_value_variables[i].rbegin(), old_coin_value_variables[i].rend())), + 1 - old_coin_enforce_commitment[i], + 0), FMT(this->annotation_prefix, " enforce_%zu", i)); + } + + /* check the balance equation */ + linear_combination old_packed_value; + for (size_t i = 0; i < num_old_coins; ++i) + { + old_packed_value = old_packed_value + pb_packing_sum(pb_variable_array(old_coin_value_variables[i].rbegin(), old_coin_value_variables[i].rend())); + } + old_packed_value = old_packed_value + pb_packing_sum(pb_variable_array(public_old_value_variable.rbegin(), public_old_value_variable.rend())); + + linear_combination new_packed_value; + for (size_t i = 0; i < num_new_coins; ++i) + { + new_packed_value = new_packed_value + pb_packing_sum(pb_variable_array(new_coin_value_variables[i].rbegin(), new_coin_value_variables[i].rend())); + } + new_packed_value = new_packed_value + pb_packing_sum(pb_variable_array(public_new_value_variable.rbegin(), public_new_value_variable.rend())); + + this->pb.add_r1cs_constraint(r1cs_constraint(1, old_packed_value, new_packed_value), FMT(this->annotation_prefix, " balance")); +} + +template +void zerocash_pour_gadget::generate_r1cs_witness(const std::vector &old_coin_authentication_paths, + const std::vector &old_coin_merkle_tree_positions, + const bit_vector &merkle_tree_root, + const std::vector &new_address_public_keys, + const std::vector &old_address_secret_keys, + const std::vector &new_address_commitment_nonces, + const std::vector &old_address_commitment_nonces, + const std::vector &new_coin_serial_number_nonces, + const std::vector &old_coin_serial_number_nonces, + const std::vector &new_coin_values, + const bit_vector &public_old_value, + const bit_vector &public_new_value, + const std::vector &old_coin_values, + const bit_vector &signature_public_key_hash) +{ + /* fill in the auxiliary variables */ + this->pb.val(zero) = FieldT::zero(); + + /* fill in the witness */ + for (size_t i = 0; i < num_new_coins; ++i) + { + new_address_public_key_variables[i].fill_with_bits(this->pb, new_address_public_keys[i]); + new_address_commitment_nonce_variables[i].fill_with_bits(this->pb, new_address_commitment_nonces[i]); + } + + for (size_t i = 0; i < num_old_coins; ++i) + { + old_address_secret_key_variables[i].fill_with_bits(this->pb, old_address_secret_keys[i]); + old_address_commitment_nonce_variables[i].fill_with_bits(this->pb, old_address_commitment_nonces[i]); + } + + for (size_t i = 0; i < num_new_coins; ++i) + { + new_coin_serial_number_nonce_variables[i].fill_with_bits(this->pb, new_coin_serial_number_nonces[i]); + new_coin_value_variables[i].fill_with_bits(this->pb, new_coin_values[i]); + } + + for (size_t i = 0; i < num_old_coins; ++i) + { + this->pb.val(old_coin_enforce_commitment[i]) = FieldT::zero(); + old_coin_serial_number_nonce_variables[i].fill_with_bits(this->pb, old_coin_serial_number_nonces[i]); + old_coin_value_variables[i].fill_with_bits(this->pb, old_coin_values[i]); + + for (size_t j = 0; j < coin_value_length; ++j) + { + if (old_coin_values[i][j]) { + // If any bit in the value is nonzero, the value is nonzero. + // Thus, the old coin must be committed in the tree. + this->pb.val(old_coin_enforce_commitment[i]) = FieldT::one(); + break; + } + } + } + + public_old_value_variable.fill_with_bits(this->pb, public_old_value); + public_new_value_variable.fill_with_bits(this->pb, public_new_value); + signature_public_key_hash_variable->generate_r1cs_witness(signature_public_key_hash); + + /* do the hashing */ + for (size_t i = 0; i < num_old_coins; ++i) + { + prfs_for_old_coin_serial_numbers[i]->generate_r1cs_witness(); + prfs_for_old_address_public_keys[i]->generate_r1cs_witness(); + commit_to_old_address_public_keys[i]->generate_r1cs_witness(); + commit_to_old_coin_value_commitment_nonces[i]->generate_r1cs_witness(); + compute_old_coin_commitments[i]->generate_r1cs_witness(); + prfs_for_macs_of_signature_public_key_hash[i]->generate_r1cs_witness(); + } + + for (size_t i = 0; i < num_new_coins; ++i) + { + commit_to_new_address_public_keys[i]->generate_r1cs_witness(); + commit_to_new_coin_value_commitment_nonces[i]->generate_r1cs_witness(); + compute_new_coin_commitments[i]->generate_r1cs_witness(); + } + + /* prove the membership in the Merkle tree */ + for (size_t i = 0; i < num_old_coins; ++i) + { + /* (A) old_coin_commitment_variables[i] appears on path old_coin_authentication_paths[i] + to merkle_tree_root_variable */ + old_coin_merkle_tree_position_variables[i].fill_with_bits_of_ulong(this->pb, old_coin_merkle_tree_positions[i]); + old_coin_authentication_path_variables[i]->generate_r1cs_witness(old_coin_merkle_tree_positions[i], old_coin_authentication_paths[i]); + old_coin_commitments_in_tree[i]->generate_r1cs_witness(); + } + + /* pack the input */ + unpack_inputs->generate_r1cs_witness_from_bits(); + +#ifdef DEBUG + printf("input_as_field_elements according to witness map:\n"); + for (size_t i = 0; i < input_as_field_elements.size(); ++i) + { + this->pb.val(input_as_field_elements[i]).print(); + } +#endif +} + +template +r1cs_primary_input zerocash_pour_input_map(const size_t num_old_coins, + const size_t num_new_coins, + const bit_vector &merkle_tree_root, + const std::vector &old_coin_serial_numbers, + const std::vector &new_coin_commitments, + const bit_vector &public_old_value, + const bit_vector &public_new_value, + const bit_vector &signature_public_key_hash, + const std::vector &signature_public_key_hash_macs) +{ + enter_block("Call to zerocash_pour_input_map"); + assert(merkle_tree_root.size() == sha256_digest_len); + assert(old_coin_serial_numbers.size() == num_old_coins); + for (auto &old_coin_serial_number : old_coin_serial_numbers) + { + assert(old_coin_serial_number.size() == serial_number_length); + } + assert(new_coin_commitments.size() == num_new_coins); + for (auto &new_coin_commitment : new_coin_commitments) + { + assert(new_coin_commitment.size() == coin_commitment_length); + } + assert(public_old_value.size() == coin_value_length); + assert(public_new_value.size() == coin_value_length); + assert(signature_public_key_hash.size() == sha256_digest_len); + assert(signature_public_key_hash_macs.size() == num_old_coins); + for (auto &signature_public_key_hash_mac : signature_public_key_hash_macs) + { + assert(signature_public_key_hash_mac.size() == sha256_digest_len); + } + + bit_vector input_as_bits; + + input_as_bits.insert(input_as_bits.end(), merkle_tree_root.begin(), merkle_tree_root.end()); + for (auto &old_coin_serial_number : old_coin_serial_numbers) + { + input_as_bits.insert(input_as_bits.end(), old_coin_serial_number.begin(), old_coin_serial_number.end()); + } + for (auto &new_coin_commitment : new_coin_commitments) + { + input_as_bits.insert(input_as_bits.end(), new_coin_commitment.begin(), new_coin_commitment.end()); + } + input_as_bits.insert(input_as_bits.end(), public_old_value.begin(), public_old_value.end()); + input_as_bits.insert(input_as_bits.end(), public_new_value.begin(), public_new_value.end()); + input_as_bits.insert(input_as_bits.end(), signature_public_key_hash.begin(), signature_public_key_hash.end()); + for (auto &signature_public_key_hash_mac : signature_public_key_hash_macs) + { + input_as_bits.insert(input_as_bits.end(), signature_public_key_hash_mac.begin(), signature_public_key_hash_mac.end()); + } + std::vector input_as_field_elements = pack_bit_vector_into_field_element_vector(input_as_bits); + +#ifdef DEBUG + printf("input_as_field_elements from zerocash_pour_input_map:\n"); + for (size_t i = 0; i < input_as_field_elements.size(); ++i) + { + input_as_field_elements[i].print(); + } +#endif + leave_block("Call to zerocash_pour_input_map"); + + return input_as_field_elements; +} + +} // libzerocash diff --git a/src/zerocash/zerocash_pour_params.hpp b/src/zerocash/zerocash_pour_params.hpp new file mode 100644 index 000000000..ba6853981 --- /dev/null +++ b/src/zerocash/zerocash_pour_params.hpp @@ -0,0 +1,34 @@ +/** @file + ***************************************************************************** + + Declaration of various parameters used by the Pour gadget and Pour ppzkSNARK. + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ZEROCASH_POUR_PARAMS_HPP_ +#define ZEROCASH_POUR_PARAMS_HPP_ + +namespace libzerocash { + +const size_t sha256_block_len = 512; +const size_t sha256_digest_len = 256; +const size_t address_public_key_length = sha256_digest_len; +const size_t address_public_key_padding_length = 256; +const size_t address_secret_key_length = 256; +const size_t coin_commitment_length = sha256_digest_len; +const size_t coin_commitment_padding_length = 192; +const size_t truncated_coin_commitment_length = 128; +const size_t truncated_serial_number_length = 254; +const size_t serial_number_length = sha256_digest_len; +const size_t address_commitment_nonce_length = 384; +const size_t serial_number_nonce_length = 256; +const size_t coin_value_length = 64; +const size_t indexed_signature_public_key_hash_length = 254; + +} // libzerocash + +#endif // ZEROCASH_POUR_PARAMS_HPP_ diff --git a/src/zerocash/zerocash_pour_ppzksnark.hpp b/src/zerocash/zerocash_pour_ppzksnark.hpp new file mode 100644 index 000000000..0eeb77db9 --- /dev/null +++ b/src/zerocash/zerocash_pour_ppzksnark.hpp @@ -0,0 +1,232 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for a ppzkSNARK for the NP statement "Pour". + + This includes: + - class for proving key + - class for verification key + - class for key pair (proving key & verification key) + - class for proof + - generator algorithm + - prover algorithm + - verifier algorithm + + The ppzkSNARK is obtained by using an R1CS ppzkSNARK relative to an R1CS + realization of the NP statement "Pour". The implementation follows, extends, + and optimizes the approach described in \[BCGGMTV14]. + + + Acronyms: + + - R1CS = "Rank-1 Constraint Systems" + - ppzkSNARK = "PreProcessing Zero-Knowledge Succinct Non-interactive ARgument of Knowledge" + + References: + + \[BCGGMTV14]: + "Zerocash: Decentralized Anonymous Payments from Bitcoin", + Eli Ben-Sasson, Alessandro Chiesa, Christina Garman, Matthew Green, Ian Miers, Eran Tromer, Madars Virza, + S&P 2014, + + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ZEROCASH_POUR_PPZKSNARK_HPP_ +#define ZEROCASH_POUR_PPZKSNARK_HPP_ + +#include "libsnark/common/data_structures/merkle_tree.hpp" +#include "libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp" +#include + +namespace libzerocash { + +/******************************** Proving key ********************************/ + +/** + * A proving key for the Pour ppzkSNARK. + */ +template +class zerocash_pour_proving_key; + +template +std::ostream& operator<<(std::ostream &out, const zerocash_pour_proving_key &pk); + +template +std::istream& operator>>(std::istream &in, zerocash_pour_proving_key &pk); + +template +class zerocash_pour_proving_key { +public: + size_t num_old_coins; + size_t num_new_coins; + size_t tree_depth; + r1cs_ppzksnark_proving_key r1cs_pk; + + zerocash_pour_proving_key() = default; + zerocash_pour_proving_key(const zerocash_pour_proving_key &other) = default; + zerocash_pour_proving_key(zerocash_pour_proving_key &&other) = default; + zerocash_pour_proving_key(const size_t num_old_coins, + const size_t num_new_coins, + const size_t tree_depth, + const r1cs_ppzksnark_proving_key &r1cs_pk) : + num_old_coins(num_old_coins), num_new_coins(num_new_coins), + tree_depth(tree_depth), r1cs_pk(r1cs_pk) {} + zerocash_pour_proving_key(const size_t num_old_coins, + const size_t num_new_coins, + const size_t tree_depth, + r1cs_ppzksnark_proving_key &&r1cs_pk) : + num_old_coins(num_old_coins), num_new_coins(num_new_coins), + tree_depth(tree_depth), r1cs_pk(std::move(r1cs_pk)) {} + zerocash_pour_proving_key& operator=(const zerocash_pour_proving_key &other) = default; + + bool operator==(const zerocash_pour_proving_key &other) const; + friend std::ostream& operator<< (std::ostream &out, const zerocash_pour_proving_key &pk); + friend std::istream& operator>> (std::istream &in, zerocash_pour_proving_key &pk); +}; + +/******************************* Verification key ****************************/ + +/** + * A verification key for the Pour ppzkSNARK. + */ +template +class zerocash_pour_verification_key; + +template +std::ostream& operator<<(std::ostream &out, const zerocash_pour_verification_key &pk); + +template +std::istream& operator>>(std::istream &in, zerocash_pour_verification_key &pk); + +template +class zerocash_pour_verification_key { +public: + size_t num_old_coins; + size_t num_new_coins; + r1cs_ppzksnark_verification_key r1cs_vk; + + zerocash_pour_verification_key() = default; + zerocash_pour_verification_key(const zerocash_pour_verification_key &other) = default; + zerocash_pour_verification_key(zerocash_pour_verification_key &&other) = default; + zerocash_pour_verification_key(const size_t num_old_coins, + const size_t num_new_coins, + const r1cs_ppzksnark_verification_key &r1cs_vk) : + num_old_coins(num_old_coins), num_new_coins(num_new_coins), + r1cs_vk(r1cs_vk) {} + zerocash_pour_verification_key(const size_t num_old_coins, + const size_t num_new_coins, + r1cs_ppzksnark_verification_key &&r1cs_vk) : + num_old_coins(num_old_coins), num_new_coins(num_new_coins), + r1cs_vk(std::move(r1cs_vk)) {} + zerocash_pour_verification_key& operator=(const zerocash_pour_verification_key &other) = default; + + bool operator==(const zerocash_pour_verification_key &other) const; + friend std::ostream& operator<< (std::ostream &out, const zerocash_pour_verification_key &pk); + friend std::istream& operator>> (std::istream &in, zerocash_pour_verification_key &pk); +}; + +/********************************** Key pair *********************************/ + +/** + * A key pair for the Pour ppzkSNARK, which consists of a proving key and a verification key. + */ +template +class zerocash_pour_keypair; + +template +std::ostream& operator<<(std::ostream &out, const zerocash_pour_keypair &pk); + +template +std::istream& operator>>(std::istream &in, zerocash_pour_keypair &pk); + +template +class zerocash_pour_keypair { +public: + zerocash_pour_proving_key pk; + zerocash_pour_verification_key vk; + + zerocash_pour_keypair() = default; + zerocash_pour_keypair(const zerocash_pour_keypair &other) = default; + zerocash_pour_keypair(zerocash_pour_keypair &&other) = default; + zerocash_pour_keypair(const zerocash_pour_proving_key &pk, + const zerocash_pour_verification_key &vk) : + pk(pk), vk(vk) {} + zerocash_pour_keypair(zerocash_pour_proving_key &&pk, + zerocash_pour_verification_key &&vk) : + pk(std::move(pk)), + vk(std::move(vk)) {} + zerocash_pour_keypair& operator=(const zerocash_pour_keypair &other) = default; + + bool operator==(const zerocash_pour_keypair &other) const; + friend std::ostream& operator<< (std::ostream &out, const zerocash_pour_keypair &pk); + friend std::istream& operator>> (std::istream &in, zerocash_pour_keypair &pk); +}; + +/*********************************** Proof ***********************************/ + +/** + * A proof for the Pour ppzkSNARK. + */ +template +using zerocash_pour_proof = r1cs_ppzksnark_proof; + + +/***************************** Main algorithms *******************************/ + +/** + * A generator algorithm for the Pour ppzkSNARK. + * + * Given a tree depth d, this algorithm produces proving and verification keys + * for Pour relative to a Merkle tree of depth d. + */ +template +zerocash_pour_keypair zerocash_pour_ppzksnark_generator(const size_t num_old_coins, + const size_t num_new_coins, + const size_t tree_depth); + +/** + * A prover algorithm for the Pour ppzkSNARK. + * + * TODO: add description + */ +template +zerocash_pour_proof zerocash_pour_ppzksnark_prover(const zerocash_pour_proving_key &pk, + const std::vector &old_coin_authentication_paths, + const std::vector &old_coin_merkle_tree_positions, + const bit_vector &merkle_tree_root, + const std::vector &new_address_public_keys, + const std::vector &old_address_secret_keys, + const std::vector &new_address_commitment_nonces, + const std::vector &old_address_commitment_nonces, + const std::vector &new_coin_serial_number_nonces, + const std::vector &old_coin_serial_number_nonces, + const std::vector &new_coin_values, + const bit_vector &public_old_value, + const bit_vector &public_new_value, + const std::vector &old_coin_values, + const bit_vector &signature_public_key_hash); + +/** + * A verifier algorithm for the Pour ppzkSNARK. + */ +template +bool zerocash_pour_ppzksnark_verifier(const zerocash_pour_verification_key &vk, + const bit_vector &merkle_tree_root, + const std::vector &old_coin_serial_numbers, + const std::vector &new_coin_commitments, + const bit_vector &public_old_value, + const bit_vector &public_new_value, + const bit_vector &signature_public_key_hash, + const std::vector &signature_public_key_hash_macs, + const zerocash_pour_proof &proof); + +} // libzerocash + +#include "zerocash_pour_ppzksnark.tcc" + +#endif // ZEROCASH_POUR_PPZKSNARK_HPP_ diff --git a/src/zerocash/zerocash_pour_ppzksnark.tcc b/src/zerocash/zerocash_pour_ppzksnark.tcc new file mode 100644 index 000000000..c0d2b322a --- /dev/null +++ b/src/zerocash/zerocash_pour_ppzksnark.tcc @@ -0,0 +1,212 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for a ppzkSNARK for the NP statement "Pour". + + See zerocash_pour_ppzksnark.hpp . + + ***************************************************************************** + * @author This file is part of libzerocash, developed by the Zerocash + * project and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef ZEROCASH_POUR_PPZKSNARK_TCC_ +#define ZEROCASH_POUR_PPZKSNARK_TCC_ + +#include "zerocash_pour_gadget.hpp" +#include "common/profiling.hpp" + +namespace libzerocash { + +template +bool zerocash_pour_proving_key::operator==(const zerocash_pour_proving_key &other) const +{ + return (this->num_old_coins == other.num_old_coins && + this->num_new_coins == other.num_new_coins && + this->tree_depth == other.tree_depth && + this->r1cs_pk == other.r1cs_pk); +} + +template +std::ostream& operator<<(std::ostream &out, const zerocash_pour_proving_key &pk) +{ + out << pk.num_old_coins << "\n"; + out << pk.num_new_coins << "\n"; + out << pk.tree_depth << "\n"; + out << pk.r1cs_pk; + + return out; +} + +template +std::istream& operator>>(std::istream &in, zerocash_pour_proving_key &pk) +{ + in >> pk.num_old_coins; + consume_newline(in); + in >> pk.num_new_coins; + consume_newline(in); + in >> pk.tree_depth; + consume_newline(in); + in >> pk.r1cs_pk; + + return in; +} + +template +bool zerocash_pour_verification_key::operator==(const zerocash_pour_verification_key &other) const +{ + return (this->num_old_coins == other.num_old_coins && + this->num_new_coins == other.num_new_coins && + this->r1cs_vk == other.r1cs_vk); +} + +template +std::ostream& operator<<(std::ostream &out, const zerocash_pour_verification_key &vk) +{ + out << vk.num_old_coins << "\n"; + out << vk.num_new_coins << "\n"; + out << vk.r1cs_vk; + + return out; +} + +template +std::istream& operator>>(std::istream &in, zerocash_pour_verification_key &vk) +{ + in >> vk.num_old_coins; + consume_newline(in); + in >> vk.num_new_coins; + consume_newline(in); + in >> vk.r1cs_vk; + + return in; +} + +template +bool zerocash_pour_keypair::operator==(const zerocash_pour_keypair &other) const +{ + return (this->pk == other.pk && + this->vk == other.vk); +} + +template +std::ostream& operator<<(std::ostream &out, const zerocash_pour_keypair &kp) +{ + out << kp.pk; + out << kp.vk; + + return out; +} + +template +std::istream& operator>>(std::istream &in, zerocash_pour_keypair &kp) +{ + in >> kp.pk; + in >> kp.vk; + + return in; +} + +template +zerocash_pour_keypair zerocash_pour_ppzksnark_generator(const size_t num_old_coins, + const size_t num_new_coins, + const size_t tree_depth) +{ + typedef Fr FieldT; + enter_block("Call to zerocash_pour_ppzksnark_generator"); + + protoboard pb; + zerocash_pour_gadget g(pb, num_old_coins, num_new_coins, tree_depth, "zerocash_pour"); + g.generate_r1cs_constraints(); + const r1cs_constraint_system constraint_system = pb.get_constraint_system(); + r1cs_ppzksnark_keypair ppzksnark_keypair = r1cs_ppzksnark_generator(constraint_system); + leave_block("Call to zerocash_pour_ppzksnark_generator"); + + zerocash_pour_proving_key zerocash_pour_pk(num_old_coins, num_new_coins, tree_depth, std::move(ppzksnark_keypair.pk)); + zerocash_pour_verification_key zerocash_pour_vk(num_old_coins, num_new_coins, std::move(ppzksnark_keypair.vk)); + return zerocash_pour_keypair(std::move(zerocash_pour_pk), std::move(zerocash_pour_vk)); +} + +template +zerocash_pour_proof zerocash_pour_ppzksnark_prover(const zerocash_pour_proving_key &pk, + const std::vector &old_coin_authentication_paths, + const std::vector &old_coin_merkle_tree_positions, + const bit_vector &merkle_tree_root, + const std::vector &new_address_public_keys, + const std::vector &old_address_secret_keys, + const std::vector &new_address_commitment_nonces, + const std::vector &old_address_commitment_nonces, + const std::vector &new_coin_serial_number_nonces, + const std::vector &old_coin_serial_number_nonces, + const std::vector &new_coin_values, + const bit_vector &public_old_value, + const bit_vector &public_new_value, + const std::vector &old_coin_values, + const bit_vector &signature_public_key_hash) +{ + typedef Fr FieldT; + + enter_block("Call to zerocash_pour_ppzksnark_prover"); + + protoboard pb; + zerocash_pour_gadget g(pb, pk.num_old_coins, pk.num_new_coins, pk.tree_depth, "zerocash_pour"); + g.generate_r1cs_constraints(); + g.generate_r1cs_witness(old_coin_authentication_paths, + old_coin_merkle_tree_positions, + merkle_tree_root, + new_address_public_keys, + old_address_secret_keys, + new_address_commitment_nonces, + old_address_commitment_nonces, + new_coin_serial_number_nonces, + old_coin_serial_number_nonces, + new_coin_values, + public_old_value, + public_new_value, + old_coin_values, + signature_public_key_hash); + if (!pb.is_satisfied()) { + leave_block("Call to zerocash_pour_ppzksnark_prover"); + throw std::invalid_argument("Constraints not satisfied by inputs"); + } + + zerocash_pour_proof proof = r1cs_ppzksnark_prover(pk.r1cs_pk, pb.primary_input(), pb.auxiliary_input()); + + leave_block("Call to zerocash_pour_ppzksnark_prover"); + + return proof; +} + +template +bool zerocash_pour_ppzksnark_verifier(const zerocash_pour_verification_key &vk, + const bit_vector &merkle_tree_root, + const std::vector &old_coin_serial_numbers, + const std::vector &new_coin_commitments, + const bit_vector &public_old_value, + const bit_vector &public_new_value, + const bit_vector &signature_public_key_hash, + const std::vector &signature_public_key_hash_macs, + const zerocash_pour_proof &proof) +{ + typedef Fr FieldT; + + enter_block("Call to zerocash_pour_ppzksnark_verifier"); + const r1cs_primary_input input = zerocash_pour_input_map(vk.num_old_coins, + vk.num_new_coins, + merkle_tree_root, + old_coin_serial_numbers, + new_coin_commitments, + public_old_value, + public_new_value, + signature_public_key_hash, + signature_public_key_hash_macs); + const bool ans = r1cs_ppzksnark_verifier_strong_IC(vk.r1cs_vk, input, proof); + leave_block("Call to zerocash_pour_ppzksnark_verifier"); + + return ans; +} + +} // libzerocash + +#endif // ZEROCASH_POUR_PPZKSNARK_TCC_