Move libzerocash (dd5db5815be70f0e4895784cc905df6f1c73cb17) into the src tree.

This commit is contained in:
Sean Bowe 2016-02-11 13:44:25 -07:00
parent dba05929e7
commit 523bc77f64
46 changed files with 6641 additions and 0 deletions

16
src/zerocash/.gitignore vendored Normal file
View File

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

35
src/zerocash/.travis.yml Normal file
View File

@ -0,0 +1,35 @@
os: linux
env:
global:
- ZCTRAVIS_BOOST_OPTS="threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1 address-model=64"
- ZCTRAVIS_BOOST_LIBRARIES="chrono,filesystem,program_options,system,thread,test"
- ZCTRAVIS_SNARK_FLAGS="-j2 CURVE=ALT_BN128 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1"
- ZCTRAVIS_LIBZEROCASH_CXXFLAGS="-w -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"
- ZCTRAVIS_LIBZEROCASH_FLAGS="-j2 NO_PROCPS=1 MINDEPS=1 LINK_RT=1"
before_install:
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y # For g++-4.9
- sudo apt-get update -qq
- sudo apt-get install -qq g++-4.9
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 90
# Install dependencies
- sudo apt-get install cmake libgmp-dev libssl-dev
# We still need boost, let's get it.
- wget http://downloads.sourceforge.net/project/boost/boost/1.57.0/boost_1_57_0.tar.bz2
- echo "910c8c022a33ccec7f088bd65d4f14b466588dda94ba2124e78b8c57db264967 boost_1_57_0.tar.bz2" | sha256sum -c
- tar -xjf boost_*.tar.bz2
- cd boost_1_57_0
- ./bootstrap.sh --without-icu --with-libraries="$ZCTRAVIS_BOOST_LIBRARIES"
- ./b2 -d2 -j2 -d1 $ZCTRAVIS_BOOST_OPTS
- sudo ./b2 -d0 -j2 $ZCTRAVIS_BOOST_OPTS install
- cd ..
# We still need crypto++, let's get it.
- ./get-cryptopp
script:
- LIBSNARK_FLAGS="$ZCTRAVIS_SNARK_FLAGS" ./get-libsnark
- CXXFLAGS="$ZCTRAVIS_LIBZEROCASH_CXXFLAGS" make $ZCTRAVIS_LIBZEROCASH_FLAGS
# - ./zerocash_pour_ppzksnark/tests/test_zerocash_pour_ppzksnark
# - ./tests/merkleTest
# - ./tests/zerocashTest

7
src/zerocash/AUTHORS Normal file
View File

@ -0,0 +1,7 @@
Eli Ben-Sasson
Alessandro Chiesa
Christina Garman
Matthew Green
Ian Miers
Eran Tromer
Madars Virza

19
src/zerocash/LICENSE Normal file
View File

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

161
src/zerocash/Makefile Normal file
View File

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

27
src/zerocash/get-cryptopp Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
# To pass parameters to the cryptopp makefile, use `CRYPTOPP_FLAGS=... ./get-cryptopp`
set -e
DEPSRC=./depsrc
DEPINST=./depinst
DUMMIES="cryptopp-dummy.exe"
set -x
mkdir -p $DEPINST
DEPINST=`pwd -P`/$DEPINST # remember absolute path
mkdir -p $DEPSRC
cd $DEPSRC
mkdir -p cryptopp562 && cd cryptopp562
if [ ! -f hmac.cpp ]; then
wget http://www.cryptopp.com/cryptopp562.zip
echo "5cbfd2fcb4a6b3aab35902e2e0f3b59d9171fee12b3fc2b363e1801dfec53574 cryptopp562.zip" | sha256sum -c
unzip cryptopp562.zip
fi
make static dynamic CXXFLAGS='-DNDEBUG -g -O2 -fPIC' $CRYPTOPP_FLAGS
touch $DUMMIES # kludge: cryptopp's "make install" breaks if there are no such files to install
make install PREFIX=$DEPINST/ $CRYPTOPP_FLAGS
rm -f $DUMMIES

39
src/zerocash/get-libsnark Executable file
View File

@ -0,0 +1,39 @@
#!/bin/bash
# To pass options options to libsnark Makefile, put them in env var LIBSNARK_FLAGS.
# To clone libsnark from an alternate location, set env var LIBSNARK_SRC. For example:
# LIBSNARK_SRC="$HOME/libsnark.git --branch master" ./get-libsnark
# To use curve ALT_BN128 instead of BN128 (which is x64-only), use:
# CURVE=ALT_BN128 ./get-libsnark
set -e
LIBSNARK_SRC=${LIBSNARK_SRC:-https://github.com/scipr-lab/libsnark}
CURVE=${CURVE:-BN128}
LIBSNARK_FLAGS="$LIBSNARK_FLAGS NO_SUPERCOP=1 NO_GTEST=1 NO_DOCS=1 CURVE=$CURVE"
if [[ `uname -s` == "Darwin" ]]; then
LIBSNARK_FLAGS="$LIBSNARK_FLAGS NO_PROCPS=1"
fi
set -x
DEPSRC=./depsrc
DEPINST=./depinst
mkdir -p $DEPINST
DEPINST=`pwd -P`/$DEPINST # remember absolute path
mkdir -p $DEPSRC
cd $DEPSRC
[ ! -d libsnark ] && git clone $LIBSNARK_SRC libsnark
cd libsnark
git pull
if [ "$CURVE" == "BN128" ]; then
# TODO: submit -fPIC patch to ate-pairing
INC_DIR=-fPIC ./prepare-depends.sh
fi
make clean
CXXFLAGS="-DNO_PT_COMPRESSION=1" make lib $LIBSNARK_FLAGS
make install PREFIX=$DEPINST $LIBSNARK_FLAGS

View File

@ -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 <cryptopp/osrng.h>
using CryptoPP::AutoSeededRandomPool;
#include <cryptopp/eccrypto.h>
using CryptoPP::ECP;
using CryptoPP::ECIES;
#include <cryptopp/oids.h>
namespace ASN1 = CryptoPP::ASN1;
#include <cryptopp/filters.h>
using CryptoPP::StringSink;
using CryptoPP::StringStore;
#include "Zerocash.h"
#include "Address.h"
namespace libzerocash {
PrivateAddress::PrivateAddress(const std::vector<unsigned char> 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<unsigned char>& PrivateAddress::getAddressSecret() const {
return this->a_sk;
}
PublicAddress::PublicAddress(): a_pk(ZC_A_PK_SIZE) {
this->pk_enc = "";
}
PublicAddress::PublicAddress(const std::vector<unsigned char>& 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<bool> a_sk_bool(ZC_A_SK_SIZE * 8);
convertBytesVectorToVector(addr_sk.getAddressSecret(), a_sk_bool);
std::vector<bool> zeros_256(256, 0);
std::vector<bool> a_pk_internal;
concatenateVectors(a_sk_bool, zeros_256, a_pk_internal);
std::vector<bool> a_pk_bool(ZC_A_PK_SIZE * 8);
hashVector(a_pk_internal, a_pk_bool);
convertVectorToBytesVector(a_pk_bool, this->a_pk);
ECIES<ECP>::PublicKey publicKey;
ECIES<ECP>::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<unsigned char>& 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<unsigned char> 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<ECP>::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 */

View File

@ -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 <vector>
#include <string>
namespace libzerocash {
/***************************** Private address ********************************/
class PrivateAddress {
public:
/* This constructor is to be used ONLY for deserialization. */
PrivateAddress();
PrivateAddress(const std::vector<unsigned char> a_sk, const std::string sk_enc);
bool operator==(const PrivateAddress& rhs) const;
bool operator!=(const PrivateAddress& rhs) const;
const std::vector<unsigned char>& getAddressSecret() const;
const std::string getEncryptionSecretKey() const;
private:
std::vector<unsigned char> 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<unsigned char>& 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<unsigned char>& getPublicAddressSecret() const;
const std::string getEncryptionPublicKey() const;
private:
std::vector<unsigned char> 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_ */

View File

@ -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 <cryptopp/osrng.h>
using CryptoPP::AutoSeededRandomPool;
#include <cryptopp/eccrypto.h>
using CryptoPP::ECP;
using CryptoPP::ECIES;
#include <cryptopp/oids.h>
namespace ASN1 = CryptoPP::ASN1;
#include <cryptopp/filters.h>
using CryptoPP::StringSink;
using CryptoPP::StringStore;
#include <stdexcept>
#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<ECP>::PrivateKey decodedPrivateKey;
decodedPrivateKey.Load(StringStore(addr.getPrivateAddress().getEncryptionSecretKey()).Ref());
// Create the decryption session
AutoSeededRandomPool prng;
ECIES<ECP>::Decryptor decrypt(decodedPrivateKey);
// Convert the input string into a vector of bytes
std::vector<byte> 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<unsigned char> 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<unsigned char> value_v(plaintext.begin(),
plaintext.begin() + ZC_V_SIZE);
std::vector<unsigned char> r_v(plaintext.begin() + ZC_V_SIZE,
plaintext.begin() + ZC_V_SIZE + ZC_R_SIZE);
std::vector<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char>& rho, const std::vector<unsigned char>& r): addr_pk(addr), rho(rho), r(r), k(ZC_K_SIZE), coinValue(ZC_V_SIZE)
{
convertIntToBytesVector(value, this->coinValue);
std::vector<unsigned char> a_pk = addr.getPublicAddressSecret();
this->computeCommitments(a_pk);
}
void
Coin::computeCommitments(std::vector<unsigned char>& a_pk)
{
std::vector<unsigned char> k_internal;
std::vector<unsigned char> k_internalhash_trunc(16);
std::vector<unsigned char> k_internalhash_internal;
concatenateVectors(a_pk, this->rho, k_internalhash_internal);
std::vector<unsigned char> 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<unsigned char>& Coin::getInternalCommitment() const {
return this->k;
}
const std::vector<unsigned char>& Coin::getRho() const {
return this->rho;
}
const std::vector<unsigned char>& Coin::getR() const {
return this->r;
}
uint64_t Coin::getValue() const {
return convertBytesVectorToInt(this->coinValue);
}
} /* namespace libzerocash */

View File

@ -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 <vector>
#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<unsigned char>& rho,
const std::vector<unsigned char>& 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<unsigned char>& getRho() const;
const std::vector<unsigned char>& getR() const;
private:
PublicAddress addr_pk;
CoinCommitment cm;
std::vector<unsigned char> rho;
std::vector<unsigned char> r;
std::vector<unsigned char> k;
std::vector<unsigned char> coinValue;
const std::vector<unsigned char>& getInternalCommitment() const;
void computeCommitments(std::vector<unsigned char>& a_pk);
};
} /* namespace libzerocash */
#endif /* COIN_H_ */

View File

@ -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 <stdexcept>
#include <stdint.h>
#include "Zerocash.h"
#include "CoinCommitment.h"
namespace libzerocash {
CoinCommitment::CoinCommitment() : commitmentValue(ZC_CM_SIZE)
{ }
CoinCommitment::CoinCommitment(const std::vector<unsigned char>& val,
const std::vector<unsigned char>& k) : commitmentValue(ZC_CM_SIZE)
{
std::vector<bool> zeros_192(192, 0);
std::vector<bool> cm_internal;
std::vector<bool> value_bool(ZC_V_SIZE * 8, 0);
std::vector<bool> 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<bool> 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<unsigned char>& CoinCommitment::getCommitmentValue() const {
return this->commitmentValue;
}
} /* namespace libzerocash */

View File

@ -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 <vector>
namespace libzerocash {
/****************************** Coin commitment ******************************/
class CoinCommitment {
friend class PourTransaction;
friend class PourProver;
public:
CoinCommitment();
CoinCommitment(const std::vector<unsigned char>& val,
const std::vector<unsigned char>& k);
const std::vector<unsigned char>& getCommitmentValue() const;
bool operator==(const CoinCommitment& rhs) const;
bool operator!=(const CoinCommitment& rhs) const;
private:
std::vector<unsigned char> commitmentValue;
};
} /* namespace libzerocash */
#endif /* COINCOMMITMENT_H_ */

View File

@ -0,0 +1,44 @@
/** @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 <fstream>
#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"
#include "zerocash_pour_ppzksnark/zerocash_pour_gadget.hpp"
#include "zerocash_pour_ppzksnark/zerocash_pour_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;
}

View File

@ -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 <cmath>
#include <iostream>
#include <vector>
namespace libzerocash {
/////////////////////////////////////////////
// IncrementalMerkleTreeCompact class
/////////////////////////////////////////////
std::vector<unsigned char> 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<unsigned char> serialized;
/* treeHeight (4 bytes, big endian) */
std::vector<unsigned char> 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<bool> 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<unsigned char> 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<unsigned char>& serialized) {
IncrementalMerkleTreeCompact deserialized;
size_t currentPos = 0;
/* treeHeight */
std::vector<unsigned char> treeHeightBytes = vectorSlice(serialized, 0, 4);
currentPos += 4;
deserialized.treeHeight = convertBytesVectorToInt(treeHeightBytes);
/* hashList */
uint32_t hashListBytesLength = ceil(deserialized.treeHeight / 8.0);
std::vector<unsigned char> 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<unsigned char> 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<bool> > &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<bool> &hashV, std::vector<bool> &index) {
// Resize the index vector
index.resize(this->treeHeight);
// Insert the element
return this->root.insertElement(hashV, index);
}
bool
IncrementalMerkleTree::insertElement(const std::vector<unsigned char> &hashV, std::vector<unsigned char> &index) {
// Create a temporary vector to hold hashV
std::vector<bool> hashVBool(hashV.size() * 8);
convertBytesVectorToVector(hashV, hashVBool);
// Create a temporary vector to hold the index
std::vector<bool> 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<bool> &index, merkle_authentication_path &witness) {
// Resize the witness if necessary
if (witness.size() < this->treeHeight) {
witness.resize(treeHeight);
}
std::vector<bool> 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<bool> > &valueVector)
{
std::vector<bool> index;
for (std::vector< std::vector<bool> >::iterator iter = valueVector.begin();
iter != valueVector.end(); ++iter) {
if (this->insertElement(*iter, index) == false) {
return false;
}
}
return true;
}
bool
IncrementalMerkleTree::getRootValue(std::vector<bool>& r) const {
// Query the root for its hash
this->root.getValue(r);
return true;
}
bool
IncrementalMerkleTree::getRootValue(std::vector<unsigned char>& r) const {
// Create a temporary byte vector
std::vector<bool> 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<unsigned char>
IncrementalMerkleTree::getRoot(){
std::vector<unsigned char> 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<bool> &hashV, std::vector<bool> &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<bool> &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<bool> hash(SHA256_BLOCK_SIZE * 8);
std::vector<bool> 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<unsigned char> 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<bool> 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 */

View File

@ -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 <vector>
#include <iostream>
#include <map>
#include <cstring>
#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<unsigned char> > const& getHashVec() { return hashVec; }
std::vector< bool > const& getHashList() { return hashList; }
std::vector<unsigned char> serialize() const;
static IncrementalMerkleTreeCompact deserialize(const std::vector<unsigned char>& serialized);
private:
IncrementalMerkleTreeCompact() : treeHeight(0) {}
uint32_t treeHeight;
std::vector< std::vector<unsigned char> > hashVec;
std::vector< bool > hashList;
};
/********************* Incremental Merkle tree node **************************/
class IncrementalMerkleNode {
public:
SHA256_CTX_mod ctx256;
IncrementalMerkleNode* left;
IncrementalMerkleNode* right;
std::vector<bool> 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<bool> &hashV, std::vector<bool> &index);
bool getWitness(const std::vector<bool> &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<bool> &r) const { r = value; }
const std::vector<bool>& 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<bool> > &valueVector, uint32_t height);
IncrementalMerkleTree(IncrementalMerkleTreeCompact &compact);
void setTo(const IncrementalMerkleTree &other) {
auto compact = other.getCompactRepresentation();
fromCompactRepresentation(compact);
}
bool insertElement(const std::vector<bool> &hashV, std::vector<bool> &index);
bool insertElement(const std::vector<unsigned char> &hashV, std::vector<unsigned char> &index);
bool insertVector(std::vector< std::vector<bool> > &valueVector);
bool getWitness(const std::vector<bool> &index, merkle_authentication_path &witness);
bool getRootValue(std::vector<bool>& r) const;
bool getRootValue(std::vector<unsigned char>& r) const;
std::vector<unsigned char>getRoot();
bool prune();
IncrementalMerkleTreeCompact getCompactRepresentation() const;
std::vector<unsigned char> serialize() const {
auto compact = getCompactRepresentation();
return compact.serialize();
}
static IncrementalMerkleTree deserialize(const std::vector<unsigned char>& serialized) {
auto deserialized = IncrementalMerkleTreeCompact::deserialize(serialized);
return IncrementalMerkleTree(deserialized);
}
bool fromCompactRepresentation(IncrementalMerkleTreeCompact &rep);
};
} /* namespace libzerocash */
#endif /* INCREMENTALMERKLETREE_H_ */

View File

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

View File

@ -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<unsigned char> 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<unsigned char> coinValue; // coin value
std::vector<unsigned char> internalCommitment; // "k" in paper notation
CoinCommitment externalCommitment; // "cm" in paper notation
const CoinCommitment& getCoinCommitment() const { return this->externalCommitment; }
};
} /* namespace libzerocash */
#endif /* MINTTRANSACTION_H_ */

View File

@ -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<bool> commitment(ZC_CM_SIZE * 8);
convertBytesVectorToVector(this->old_coin.getCoinCommitment().getCommitmentValue(), commitment);
// insert commitment into the merkle tree
std::vector<bool> 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 */

View File

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

View File

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

View File

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

View File

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

View File

@ -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<unsigned char>& pubkeyHash,
const std::vector<unsigned char>& rt,
const uint64_t vpub_old,
const uint64_t vpub_new,
const boost::array<std::vector<unsigned char>, 2> serials,
const boost::array<std::vector<unsigned char>, 2> commitments,
const boost::array<std::vector<unsigned char>, 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_ */

View File

@ -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 <cryptopp/osrng.h>
using CryptoPP::AutoSeededRandomPool;
#include <cryptopp/eccrypto.h>
using CryptoPP::ECP;
using CryptoPP::ECIES;
#include <cryptopp/filters.h>
using CryptoPP::StringSource;
using CryptoPP::StringStore;
using CryptoPP::StringSink;
using CryptoPP::PK_EncryptorFilter;
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include <openssl/bn.h>
#include <openssl/sha.h>
#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_ppzksnark/zerocash_pour_gadget.hpp"
#include "zerocash_pour_ppzksnark/zerocash_pour_ppzksnark.hpp"
namespace libzerocash {
PourTransaction::PourTransaction(): cm_1(), cm_2() {
}
PourTransaction::PourTransaction(ZerocashParams& params,
const std::vector<unsigned char>& pubkeyHash,
const MerkleRootType& rt,
std::vector<PourInput> inputs,
std::vector<PourOutput> 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<unsigned char>& 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<unsigned char>& 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<bool> root_bv(ZC_ROOT_SIZE * 8);
std::vector<bool> addr_pk_new_1_bv(ZC_A_PK_SIZE * 8);
std::vector<bool> addr_pk_new_2_bv(ZC_A_PK_SIZE * 8);
std::vector<bool> addr_sk_old_1_bv(ZC_A_SK_SIZE * 8);
std::vector<bool> addr_sk_old_2_bv(ZC_A_SK_SIZE * 8);
std::vector<bool> rand_new_1_bv(ZC_R_SIZE * 8);
std::vector<bool> rand_new_2_bv(ZC_R_SIZE * 8);
std::vector<bool> rand_old_1_bv(ZC_R_SIZE * 8);
std::vector<bool> rand_old_2_bv(ZC_R_SIZE * 8);
std::vector<bool> nonce_new_1_bv(ZC_RHO_SIZE * 8);
std::vector<bool> nonce_new_2_bv(ZC_RHO_SIZE * 8);
std::vector<bool> nonce_old_1_bv(ZC_RHO_SIZE * 8);
std::vector<bool> nonce_old_2_bv(ZC_RHO_SIZE * 8);
std::vector<bool> val_new_1_bv(ZC_V_SIZE * 8);
std::vector<bool> val_new_2_bv(ZC_V_SIZE * 8);
std::vector<bool> val_old_pub_bv(ZC_V_SIZE * 8);
std::vector<bool> val_new_pub_bv(ZC_V_SIZE * 8);
std::vector<bool> val_old_1_bv(ZC_V_SIZE * 8);
std::vector<bool> val_old_2_bv(ZC_V_SIZE * 8);
std::vector<bool> cm_new_1_bv(ZC_CM_SIZE * 8);
std::vector<bool> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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<bool> 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<bool> sn_internal_1;
concatenateVectors(addr_sk_old_1_bv, nonce_old_1, sn_internal_1);
std::vector<bool> 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<bool> 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<bool> sn_internal_2;
concatenateVectors(addr_sk_old_2_bv, nonce_old_2, sn_internal_2);
std::vector<bool> 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<bool> h_S_bv(ZC_H_SIZE * 8);
convertBytesToVector(h_S_bytes, h_S_bv);
std::vector<bool> 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<bool> 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<bool> MAC_1_internal;
concatenateVectors(addr_sk_old_1_bv, h_S_internal1, MAC_1_internal);
std::vector<bool> MAC_1_bv(ZC_H_SIZE * 8);
hashVector(MAC_1_internal, MAC_1_bv);
convertVectorToBytesVector(MAC_1_bv, this->MAC_1);
std::vector<bool> MAC_2_internal;
concatenateVectors(addr_sk_old_2_bv, h_S_internal2, MAC_2_internal);
std::vector<bool> 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<ZerocashParams::zerocash_pp>(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<ECP>::PublicKey publicKey_1;
publicKey_1.Load(StringStore(addr_1_new.getEncryptionPublicKey()).Ref());
ECIES<ECP>::Encryptor encryptor_1(publicKey_1);
std::vector<unsigned char> 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<ECP>::PublicKey publicKey_2;
publicKey_2.Load(StringStore(addr_2_new.getEncryptionPublicKey()).Ref());
ECIES<ECP>::Encryptor encryptor_2(publicKey_2);
std::vector<unsigned char> 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<unsigned char> &pubkeyHash,
const MerkleRootType &merkleRoot) const
{
if(this->version == 0){
return true;
}
zerocash_pour_proof<ZerocashParams::zerocash_pp> 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<bool> root_bv(ZC_ROOT_SIZE * 8);
std::vector<bool> sn_old_1_bv(ZC_SN_SIZE * 8);
std::vector<bool> sn_old_2_bv(ZC_SN_SIZE * 8);
std::vector<bool> cm_new_1_bv(ZC_CM_SIZE * 8);
std::vector<bool> cm_new_2_bv(ZC_CM_SIZE * 8);
std::vector<bool> val_old_pub_bv(ZC_V_SIZE * 8);
std::vector<bool> val_new_pub_bv(ZC_V_SIZE * 8);
std::vector<bool> MAC_1_bv(ZC_H_SIZE * 8);
std::vector<bool> 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<bool> 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<bool> h_S_bv(ZC_H_SIZE * 8);
convertBytesToVector(h_S_bytes, h_S_bv);
bool snark_result = zerocash_pour_ppzksnark_verifier<ZerocashParams::zerocash_pp>(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<unsigned char>& PourTransaction::getSpentSerial1() const{
return this->serialNumber_1;
}
const std::vector<unsigned char>& 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 */

View File

@ -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 <stdexcept>
#include <bitset>
#include <boost/array.hpp>
typedef std::vector<unsigned char> CoinCommitmentValue;
namespace libzerocash {
/***************************** Pour transaction ******************************/
class PourTransaction {
friend class PourProver;
public:
PourTransaction();
PourTransaction(ZerocashParams& params,
const std::vector<unsigned char>& pubkeyHash,
const MerkleRootType& rt,
const std::vector<PourInput> inputs,
const std::vector<PourOutput> 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<unsigned char>& 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<unsigned char>& 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<unsigned char> &pubkeyHash,
const MerkleRootType &merkleRoot) const;
const std::vector<unsigned char>& getSpentSerial1() const;
const std::vector<unsigned char>& 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<std::vector<unsigned char>, 2>& serials,
boost::array<std::vector<unsigned char>, 2>& commitments,
boost::array<std::vector<unsigned char>, 2>& macs,
boost::array<std::string, 2>& 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<std::string> 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<unsigned char> publicOldValue; // public input value of the Pour transaction
std::vector<unsigned char> publicNewValue; // public output value of the Pour transaction
std::vector<unsigned char> serialNumber_1; // serial number of input (old) coin #1
std::vector<unsigned char> 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<unsigned char> MAC_1; // first MAC (h_1 in paper notation)
std::vector<unsigned char> 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_ */

View File

@ -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 <stdexcept>
#include <vector>
/* 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<unsigned char> MerkleRootType;
namespace libsnark {
};
namespace libzerocash {
using namespace libsnark;
};
#include "libzerocash/utils/util.h"
#endif /* ZEROCASH_H_ */

View File

@ -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 <fstream>
#include <boost/format.hpp>
#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::zerocash_pp> ZerocashParams::GenerateNewKeyPair(const unsigned int tree_depth)
{
libzerocash::ZerocashParams::zerocash_pp::init_public_params();
libzerocash::zerocash_pour_keypair<libzerocash::ZerocashParams::zerocash_pp> kp_v1 =
libzerocash::zerocash_pour_ppzksnark_generator<libzerocash::ZerocashParams::zerocash_pp>(
libzerocash::ZerocashParams::numPourInputs,
libzerocash::ZerocashParams::numPourOutputs,
tree_depth
);
return kp_v1;
}
void ZerocashParams::SaveProvingKeyToFile(const zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* 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<ZerocashParams::zerocash_pp>* 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::zerocash_pp> 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<ZerocashParams::zerocash_pp> pk_temp;
ssProving >> pk_temp;
return zerocash_pour_proving_key<ZerocashParams::zerocash_pp>(
libzerocash::ZerocashParams::numPourInputs,
libzerocash::ZerocashParams::numPourOutputs,
tree_depth,
std::move(pk_temp)
);
}
zerocash_pour_verification_key<ZerocashParams::zerocash_pp> 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<ZerocashParams::zerocash_pp> vk_temp;
ssVerification >> vk_temp;
return zerocash_pour_verification_key<ZerocashParams::zerocash_pp>(
libzerocash::ZerocashParams::numPourInputs,
libzerocash::ZerocashParams::numPourOutputs,
std::move(vk_temp)
);
}
ZerocashParams::ZerocashParams(
const unsigned int tree_depth,
zerocash_pour_keypair<ZerocashParams::zerocash_pp> *keypair
) :
treeDepth(tree_depth)
{
params_pk_v1 = new zerocash_pour_proving_key<ZerocashParams::zerocash_pp>(keypair->pk);
params_vk_v1 = new zerocash_pour_verification_key<ZerocashParams::zerocash_pp>(keypair->vk);
}
ZerocashParams::ZerocashParams(
const unsigned int tree_depth,
zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* p_pk_1,
zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* 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<ZerocashParams::zerocash_pp>(*p_pk_1);
}
if (p_vk_1 == NULL) {
params_vk_v1 = NULL;
} else {
params_vk_v1 = new zerocash_pour_verification_key<ZerocashParams::zerocash_pp>(*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::zerocash_pp>& 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::zerocash_pp>& ZerocashParams::getVerificationKey()
{
if (params_vk_v1 != NULL) {
return *params_vk_v1;
} else {
throw std::runtime_error("Pour verification key not set.");
}
}
} /* namespace libzerocash */

View File

@ -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/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<ZerocashParams::zerocash_pp> *keypair
);
ZerocashParams(
const unsigned int tree_depth,
zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* p_pk_1,
zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* p_vk_1
);
const zerocash_pour_proving_key<zerocash_pp>& getProvingKey();
const zerocash_pour_verification_key<zerocash_pp>& getVerificationKey();
int getTreeDepth();
~ZerocashParams();
static const size_t numPourInputs = 2;
static const size_t numPourOutputs = 2;
static zerocash_pour_keypair<ZerocashParams::zerocash_pp> GenerateNewKeyPair(const unsigned int tree_depth);
static void SaveProvingKeyToFile(const zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* p_pk_1, std::string path);
static void SaveVerificationKeyToFile(const zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* p_vk_1, std::string path);
static zerocash_pour_proving_key<ZerocashParams::zerocash_pp> LoadProvingKeyFromFile(std::string path, const unsigned int tree_depth);
static zerocash_pour_verification_key<ZerocashParams::zerocash_pp> LoadVerificationKeyFromFile(std::string path, const unsigned int tree_depth);
private:
int treeDepth;
zerocash_pour_proving_key<ZerocashParams::zerocash_pp>* params_pk_v1;
zerocash_pour_verification_key<ZerocashParams::zerocash_pp>* params_vk_v1;
};
} /* namespace libzerocash */
#endif /* PARAMS_H_ */

View File

@ -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 <stdlib.h>
#include <memory.h>
#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;
}
}

View File

@ -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 <stddef.h>
#include <stdint.h>
/****************************** 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

View File

@ -0,0 +1,346 @@
#include <openssl/rand.h>
#include <openssl/err.h>
#include <string>
#include <iostream>
#include <fstream>
#include <exception>
#include <cstring>
#include <iomanip>
#include <algorithm>
#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<bool>& 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<bool>& 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<bool>& 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<bool>& 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<unsigned char>& v) {
std::vector<bool> boolVec(v.size() * 8);
convertBytesVectorToVector(v, boolVec);
printVector(boolVec);
}
void printBytesVector(const std::string str, const std::vector<unsigned char>& v) {
std::vector<bool> boolVec(v.size() * 8);
convertBytesVectorToVector(v, boolVec);
printVector(str, boolVec);
}
void printBytesVectorAsHex(const std::vector<unsigned char>& v) {
std::vector<bool> boolVec(v.size() * 8);
convertBytesVectorToVector(v, boolVec);
printVectorAsHex(boolVec);
}
void printBytesVectorAsHex(const std::string str, const std::vector<unsigned char>& v) {
std::vector<bool> 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<bool>& 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<bool>& 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<unsigned char>& v) {
for(size_t i = 0; i < v.size(); i++) {
v.at(i) = bytes[i];
}
}
void convertBytesVectorToBytes(const std::vector<unsigned char>& v, unsigned char* bytes) {
for(size_t i = 0; i < v.size(); i++) {
bytes[i] = v.at(i);
}
}
void convertBytesVectorToVector(const std::vector<unsigned char>& bytes, std::vector<bool>& v) {
v.resize(bytes.size() * 8);
unsigned char bytesArr[bytes.size()];
convertBytesVectorToBytes(bytes, bytesArr);
convertBytesToVector(bytesArr, v);
}
void convertVectorToBytesVector(const std::vector<bool>& v, std::vector<unsigned char>& bytes) {
unsigned char bytesArr[int(ceil(v.size() / 8.))];
convertVectorToBytes(v, bytesArr);
convertBytesToBytesVector(bytesArr, bytes);
}
void convertIntToBytesVector(const uint64_t val_int, std::vector<unsigned char>& 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<bool>& 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<bool>& 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<v.size();i++) {
if (v.at(i)) {
result |= (uint64_t)1 << ((v.size() - 1) - i);
}
}
return result;
}
uint64_t convertBytesVectorToInt(const std::vector<unsigned char>& 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<bool>& A, const std::vector<bool>& B, std::vector<bool>& 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<unsigned char>& A, const std::vector<unsigned char>& B, std::vector<unsigned char>& 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<bool>& A, const std::vector<bool>& B, const std::vector<bool>& C, std::vector<bool>& 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<unsigned char>& A, const std::vector<unsigned char>& B, const std::vector<unsigned char>& C, std::vector<unsigned char>& 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<bool> input, std::vector<bool>& 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<unsigned char> input, std::vector<unsigned char>& 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<bool> input, std::vector<bool>& 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<unsigned char> input, std::vector<unsigned char>& 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<bool> left, const std::vector<bool> right, std::vector<bool>& output) {
std::vector<bool> 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<unsigned char> left, const std::vector<unsigned char> right, std::vector<unsigned char>& output) {
std::vector<unsigned char> 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<bool> left, const std::vector<bool> right, std::vector<bool>& output) {
std::cout << std::endl;
std::vector<bool> 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<unsigned char> left, const std::vector<unsigned char> right, std::vector<unsigned char>& output) {
std::vector<unsigned char> 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<bool> test) {
// XXX: not time safe
return (test.end() == std::find(test.begin(), test.end(), true));
}
size_t countOnes(const std::vector<bool>& vec) {
return count(vec.begin(), vec.end(), true);
}
std::vector<unsigned char> vectorSlice(const std::vector<unsigned char>& vec, size_t start, size_t length) {
std::vector<unsigned char> slice(length);
for (size_t i = 0; i < length; i++) {
slice.at(i) = vec.at(start + i);
}
return slice;
}
} /* namespace libzerocash */

View File

@ -0,0 +1,90 @@
#ifndef UTIL_H_
#define UTIL_H_
#include <string>
#include <stdexcept>
#include <vector>
#include <cstdint>
#include "sha256.h"
namespace libzerocash {
void printChar(const unsigned char c);
void printVector(const std::vector<bool>& v);
void printVector(const std::string str, const std::vector<bool>& v);
void printVectorAsHex(const std::vector<bool>& v);
void printVectorAsHex(const std::string str, const std::vector<bool>& v);
void printBytesVector(const std::vector<unsigned char>& v);
void printBytesVector(const std::string str, const std::vector<unsigned char>& v);
void printBytesVectorAsHex(const std::vector<unsigned char>& v);
void printBytesVectorAsHex(const std::string str, const std::vector<unsigned char>& v);
void getRandBytes(unsigned char* bytes, int num);
void convertBytesToVector(const unsigned char* bytes, std::vector<bool>& v);
void convertVectorToBytes(const std::vector<bool>& v, unsigned char* bytes);
void convertBytesToBytesVector(const unsigned char* bytes, std::vector<unsigned char>& v);
void convertBytesVectorToBytes(const std::vector<unsigned char>& v, unsigned char* bytes);
void convertBytesVectorToVector(const std::vector<unsigned char>& bytes, std::vector<bool>& v);
void convertVectorToBytesVector(const std::vector<bool>& v, std::vector<unsigned char>& bytes);
void convertIntToBytesVector(const uint64_t val_int, std::vector<unsigned char>& bytes);
void convertIntToVector(uint64_t val, std::vector<bool>& v);
uint64_t convertVectorToInt(const std::vector<bool>& v);
uint64_t convertBytesVectorToInt(const std::vector<unsigned char>& bytes);
void concatenateVectors(const std::vector<bool>& A, const std::vector<bool>& B, std::vector<bool>& result);
void concatenateVectors(const std::vector<unsigned char>& A, const std::vector<unsigned char>& B, std::vector<unsigned char>& result);
void concatenateVectors(const std::vector<bool>& A, const std::vector<bool>& B, const std::vector<bool>& C, std::vector<bool>& result);
void concatenateVectors(const std::vector<unsigned char>& A, const std::vector<unsigned char>& B, const std::vector<unsigned char>& C, std::vector<unsigned char>& 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<bool> input, std::vector<bool>& output);
void hashVector(SHA256_CTX_mod* ctx256, const std::vector<unsigned char> input, std::vector<unsigned char>& output);
void hashVector(const std::vector<bool> input, std::vector<bool>& output);
void hashVector(const std::vector<unsigned char> input, std::vector<unsigned char>& output);
void hashVectors(SHA256_CTX_mod* ctx256, const std::vector<bool> left, const std::vector<bool> right, std::vector<bool>& output);
void hashVectors(SHA256_CTX_mod* ctx256, const std::vector<unsigned char> left, const std::vector<unsigned char> right, std::vector<unsigned char>& output);
void hashVectors(const std::vector<bool> left, const std::vector<bool> right, std::vector<bool>& output);
void hashVectors(const std::vector<unsigned char> left, const std::vector<unsigned char> right, std::vector<unsigned char>& output);
bool VectorIsZero(const std::vector<bool> test);
size_t countOnes(const std::vector<bool>& vec);
std::vector<unsigned char> vectorSlice(const std::vector<unsigned char>& vec, size_t start, size_t length);
} /* namespace libzerocash */
#endif /* UTIL_H_ */

View File

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

View File

@ -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 "libzerocash/IncrementalMerkleTree.h"
#include <iostream>
#include <vector>
#define BOOST_TEST_MODULE merkleTest
#include <boost/test/included/unit_test.hpp>
using namespace libzerocash;
using namespace std;
void constructNonzeroTestVector(std::vector< std::vector<bool> > &values, uint32_t size)
{
values.resize(0);
std::vector<bool> 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<bool> > &values, uint32_t size)
{
values.resize(0);
std::vector<bool> 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<bool> > values;
std::vector<bool> 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<bool> expected_root(32*8, 0);
BOOST_CHECK( expected_root == actual_root );
}
void add_values_to_reference(IncrementalMerkleTree &tree, std::vector< std::vector<bool> > &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<bool> > 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<bool> root1, root2;
incTree.getRootValue(root1);
incTree2.getRootValue(root2);
BOOST_CHECK(root1 == root2);
}
}
BOOST_AUTO_TEST_CASE( testRootOfTreeOfNonZeroIsNonZero ) {
IncrementalMerkleTree incTree;
std::vector< std::vector<bool> > values;
std::vector<bool> 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<bool> 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<bool> > values;
std::vector<bool> 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<unsigned char> path_bytes(8);
std::vector<bool> 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<unsigned char> 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<bool> > 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<unsigned char> 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<unsigned char> 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<unsigned char> extra_byte = serialized;
extra_byte.push_back(0x00);
BOOST_CHECK_THROW(
IncrementalMerkleTreeCompact::deserialize(extra_byte),
std::runtime_error
);
}

View File

@ -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 <stdio.h>
#include <sys/time.h>
#include "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 */

View File

@ -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 <string>
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_ */

View File

@ -0,0 +1,534 @@
#define BOOST_TEST_MODULE utilTest
#include <boost/test/included/unit_test.hpp>
#include "libzerocash/utils/util.h"
#include "libzerocash/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<bool>(100)), std::length_error);
{
std::vector<bool> v(63, 1);
BOOST_CHECK(libzerocash::convertVectorToInt(v) == 0x7fffffffffffffff);
}
{
std::vector<bool> 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<bool> v1(5*8);
libzerocash::convertBytesToVector(bytes, v1);
std::vector<bool> 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<bool> 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<bool> 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<bool> 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<unsigned char> 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::vector<unsigned char>v(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<unsigned char> bytes = {0x00, 0x01, 0x03, 0x12, 0xFF};
std::vector<bool> 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<bool> actual_bits;
libzerocash::convertBytesVectorToVector(bytes, actual_bits);
BOOST_CHECK(actual_bits == expected_bits);
}
BOOST_AUTO_TEST_CASE( testConvertVectorToBytesVector ) {
std::vector<unsigned char> expected_bytes = {0x00, 0x01, 0x03, 0x12, 0xFF};
std::vector<bool> 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<unsigned char> actual_bytes(5);
libzerocash::convertVectorToBytesVector(bits, actual_bytes);
BOOST_CHECK(actual_bytes == expected_bytes);
}
BOOST_AUTO_TEST_CASE( testConvertIntToBytesVector ) {
uint64_t val;
std::vector<unsigned char> expected;
std::vector<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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<bool> expected;
std::vector<bool> 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<bool> too_big(100);
libzerocash::convertIntToVector(0, too_big);
BOOST_CHECK(too_big.size() == 64);
std::vector<bool> too_small(10);
libzerocash::convertIntToVector(0, too_small);
BOOST_CHECK(too_big.size() == 64);
}
BOOST_AUTO_TEST_CASE( testConcatenateTwoBoolVectors ) {
std::vector<bool> front = { 0, 1, 0 };
std::vector<bool> back = { 1, 0, 1 };
std::vector<bool> expected = { 0, 1, 0, 1, 0, 1 };
std::vector<bool> actual;
libzerocash::concatenateVectors(front, back, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testConcatenateTwoByteVectors ) {
std::vector<unsigned char> front = { 0, 1, 2 };
std::vector<unsigned char> back = { 3, 4, 5 };
std::vector<unsigned char> expected = { 0, 1, 2, 3, 4, 5 };
std::vector<unsigned char> actual;
libzerocash::concatenateVectors(front, back, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testConcatenateThreeBoolVectors ) {
std::vector<bool> front = { 0, 1, 0 };
std::vector<bool> middle { 1, 1, 1 };
std::vector<bool> back = { 1, 0, 1 };
std::vector<bool> expected = { 0, 1, 0, 1, 1, 1, 1, 0, 1 };
std::vector<bool> actual;
libzerocash::concatenateVectors(front, middle, back, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testConcatenateThreeByteVectors ) {
std::vector<unsigned char> front = { 0, 1, 0 };
std::vector<unsigned char> middle { 1, 1, 1 };
std::vector<unsigned char> back = { 1, 0, 1 };
std::vector<unsigned char> expected = { 0, 1, 0, 1, 1, 1, 1, 0, 1 };
std::vector<unsigned char> 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<bool> preimage(SHA256_PREIMAGE_BYTES * 8);
libzerocash::convertBytesToVector(sha256_preimage, preimage);
std::vector<bool> expected(32*8);
libzerocash::convertBytesToVector(sha256_hash, expected);
// TODO: evaluate whether this should be a necessary precondition.
std::vector<bool> actual(32*8);
libzerocash::hashVector(&ctx256, preimage, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testHashByteVectorToByteVectorCTX ) {
SHA256_CTX_mod ctx256;
std::vector<unsigned char> preimage(SHA256_PREIMAGE_BYTES);
libzerocash::convertBytesToBytesVector(sha256_preimage, preimage);
std::vector<unsigned char> expected(32);
libzerocash::convertBytesToBytesVector(sha256_hash, expected);
// TODO: evaluate whether this should be a necessary precondition.
std::vector<unsigned char> actual(32);
libzerocash::hashVector(&ctx256, preimage, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testHashBoolVectorToBoolVector ) {
std::vector<bool> preimage(SHA256_PREIMAGE_BYTES * 8);
libzerocash::convertBytesToVector(sha256_preimage, preimage);
std::vector<bool> expected(32*8);
libzerocash::convertBytesToVector(sha256_hash, expected);
// TODO: evaluate whether this should be a necessary precondition.
std::vector<bool> actual(32*8);
libzerocash::hashVector(preimage, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testHashByteVectorToByteVector ) {
std::vector<unsigned char> preimage(SHA256_PREIMAGE_BYTES);
libzerocash::convertBytesToBytesVector(sha256_preimage, preimage);
std::vector<unsigned char> expected(32);
libzerocash::convertBytesToBytesVector(sha256_hash, expected);
// TODO: evaluate whether this should be a necessary precondition.
std::vector<unsigned char> actual(32);
libzerocash::hashVector(preimage, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testHashBoolVectorsCTX ) {
SHA256_CTX_mod ctx256;
std::vector<bool> preimage1(8);
libzerocash::convertBytesToVector(sha256_preimage, preimage1);
std::vector<bool> preimage2((SHA256_PREIMAGE_BYTES - 1) * 8);
libzerocash::convertBytesToVector(sha256_preimage + 1, preimage2);
std::vector<bool> expected(32*8);
libzerocash::convertBytesToVector(sha256_hash, expected);
// TODO: evaluate whether this should be a necessary precondition.
std::vector<bool> actual(32 * 8);
libzerocash::hashVectors(&ctx256, preimage1, preimage2, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testHashByteVectorsCTX ) {
SHA256_CTX_mod ctx256;
std::vector<unsigned char> preimage1(1);
libzerocash::convertBytesToBytesVector(sha256_preimage, preimage1);
std::vector<unsigned char> preimage2(SHA256_PREIMAGE_BYTES - 1);
libzerocash::convertBytesToBytesVector(sha256_preimage + 1, preimage2);
std::vector<unsigned char> expected(32);
libzerocash::convertBytesToBytesVector(sha256_hash, expected);
// TODO: evaluate whether this should be a necessary precondition.
std::vector<unsigned char> actual(32);
libzerocash::hashVectors(&ctx256, preimage1, preimage2, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testHashBoolVectors ) {
std::vector<bool> preimage1(8);
libzerocash::convertBytesToVector(sha256_preimage, preimage1);
std::vector<bool> preimage2((SHA256_PREIMAGE_BYTES - 1) * 8);
libzerocash::convertBytesToVector(sha256_preimage + 1, preimage2);
std::vector<bool> expected(32*8);
libzerocash::convertBytesToVector(sha256_hash, expected);
// TODO: evaluate whether this should be a necessary precondition.
std::vector<bool> actual(32 * 8);
libzerocash::hashVectors(preimage1, preimage2, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testHashByteVectors ) {
std::vector<unsigned char> preimage1(1);
libzerocash::convertBytesToBytesVector(sha256_preimage, preimage1);
std::vector<unsigned char> preimage2(SHA256_PREIMAGE_BYTES - 1);
libzerocash::convertBytesToBytesVector(sha256_preimage + 1, preimage2);
std::vector<unsigned char> expected(32);
libzerocash::convertBytesToBytesVector(sha256_hash, expected);
// TODO: evaluate whether this should be a necessary precondition.
std::vector<unsigned char> actual(32);
libzerocash::hashVectors(preimage1, preimage2, actual);
BOOST_CHECK( expected == actual );
}
BOOST_AUTO_TEST_CASE( testVectorIsZero ) {
std::vector<bool> 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) );
}

View File

@ -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 <stdlib.h>
#include <iostream>
#define BOOST_TEST_MODULE zerocashTest
#include <boost/test/included/unit_test.hpp>
#include "timer.h"
#include "libzerocash/Zerocash.h"
#include "libzerocash/ZerocashParams.h"
#include "libzerocash/Address.h"
#include "libzerocash/CoinCommitment.h"
#include "libzerocash/Coin.h"
#include "libzerocash/IncrementalMerkleTree.h"
#include "libzerocash/MintTransaction.h"
#include "libzerocash/PourTransaction.h"
#include "libzerocash/PourInput.h"
#include "libzerocash/PourOutput.h"
#include "libzerocash/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<libzerocash::Coin> coins;
vector<libzerocash::Address> 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<std::vector<bool>> coinValues(5);
vector<bool> 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<bool> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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
bool test_pour(libzerocash::ZerocashParams& p,
uint64_t vpub_in,
uint64_t vpub_out,
std::vector<uint64_t> inputs, // values of the inputs (max 2)
std::vector<uint64_t> outputs) // values of the outputs (max 2)
{
using pour_input_state = std::tuple<libzerocash::Address, libzerocash::Coin, std::vector<bool>>;
// Construct incremental merkle tree
libzerocash::IncrementalMerkleTree merkleTree(TEST_TREE_DEPTH);
// Dummy sig_pk
vector<unsigned char> as(ZC_SIG_PK_SIZE, 'a');
vector<libzerocash::PourInput> pour_inputs;
vector<libzerocash::PourOutput> pour_outputs;
vector<pour_input_state> input_state;
for(std::vector<uint64_t>::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<bool> commitment(ZC_CM_SIZE * 8);
libzerocash::convertBytesVectorToVector(coin.getCoinCommitment().getCommitmentValue(), commitment);
// insert commitment into the merkle tree
std::vector<bool> 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<unsigned char> rt(ZC_ROOT_SIZE);
{
vector<bool> 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<pour_input_state>::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<uint64_t>::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..
BOOST_CHECK(test_pour(p, 0, 0, {1}, {1}));
BOOST_CHECK(test_pour(p, 0, 0, {2}, {1, 1}));
BOOST_CHECK(test_pour(p, 0, 0, {2, 2}, {3, 1}));
BOOST_CHECK(test_pour(p, 0, 1, {1}, {}));
BOOST_CHECK(test_pour(p, 0, 1, {2}, {1}));
BOOST_CHECK(test_pour(p, 0, 1, {2, 2}, {2, 1}));
BOOST_CHECK(test_pour(p, 1, 0, {}, {1}));
BOOST_CHECK(test_pour(p, 1, 0, {1}, {1, 1}));
BOOST_CHECK(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<unsigned char> 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<libzerocash::Coin> coins;
vector<libzerocash::Address> 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<std::vector<bool>> coinValues(5);
vector<bool> 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<bool> 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<bool> root_bv(ZC_ROOT_SIZE * 8);
merkleTree.getRootValue(root_bv);
vector<unsigned char> rt(ZC_ROOT_SIZE);
libzerocash::convertVectorToBytesVector(root_bv, rt);
vector<unsigned char> ones(ZC_V_SIZE, 1);
vector<unsigned char> twos(ZC_V_SIZE, 2);
vector<unsigned char> 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<unsigned char> 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<libzerocash::Coin> coins;
vector<libzerocash::Address> 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<std::vector<bool>> coinValues(coins.size());
vector<bool> 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<bool> 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<bool> 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<bool> wit1(SHA256_BLOCK_SIZE * 8);
vector<bool> wit2(SHA256_BLOCK_SIZE * 8);
vector<bool> wit3(SHA256_BLOCK_SIZE * 8);
vector<bool> inter_1(SHA256_BLOCK_SIZE * 8);
vector<bool> inter_2(SHA256_BLOCK_SIZE * 8);
std::vector<bool> 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<libzerocash::Coin> coins;
vector<libzerocash::Address> 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<std::vector<bool>> coinValues(5);
vector<bool> 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<bool> 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<bool> root_bv(ZC_ROOT_SIZE * 8);
merkleTree.getRootValue(root_bv);
vector<unsigned char> rt(ZC_ROOT_SIZE);
libzerocash::convertVectorToBytesVector(root_bv, rt);
vector<unsigned char> 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<unsigned char> 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);
}

View File

@ -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<Fr<default_r1cs_ppzksnark_pp> > pb;
libzerocash::zerocash_pour_gadget<Fr<default_r1cs_ppzksnark_pp> > 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
}

View File

@ -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 <algorithm>
#include <random>
#include <set>
#include <vector>
#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_pour_ppzksnark/zerocash_pour_gadget.hpp"
#include "zerocash_pour_ppzksnark/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<size_t> 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<size_t> positions;
std::vector<size_t> 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<typename FieldT>
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<FieldT>::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<FieldT>::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<FieldT>::get_hash(coin_commitment_hash_input);
return coin_commitment;
}
std::vector<size_t> randomly_split_up_value(const size_t value, const size_t num_parts)
{
std::vector<size_t> 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<size_t> 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<typename ppT>
void test_zerocash_pour_ppzksnark(const size_t num_old_coins, const size_t num_new_coins, const size_t tree_depth)
{
typedef Fr<ppT> FieldT;
assert(log2(num_old_coins) <= tree_depth);
/* information used by the prover in the witness map */
std::vector<merkle_authentication_path> old_coin_authentication_paths(num_old_coins); //
std::vector<size_t> old_coin_merkle_tree_positions; //
bit_vector merkle_tree_root; //
std::vector<bit_vector> new_address_public_keys(num_new_coins); //
std::vector<bit_vector> old_address_secret_keys(num_old_coins); //
std::vector<bit_vector> new_address_commitment_nonces(num_new_coins); //
std::vector<bit_vector> old_address_commitment_nonces(num_old_coins); //
std::vector<bit_vector> new_coin_serial_number_nonces(num_new_coins); //
std::vector<bit_vector> old_coin_serial_number_nonces(num_old_coins); //
std::vector<bit_vector> new_coin_values(num_new_coins); //
bit_vector public_in_value; //
bit_vector public_out_value; //
std::vector<bit_vector> old_coin_values(num_old_coins); //
bit_vector signature_public_key_hash; //
/* generate split for the money */
std::vector<size_t> 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<size_t> 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<sha256_two_to_one_hash_gadget<FieldT> > 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<FieldT>::get_hash(old_address_public_key_hash_input);
const bit_vector old_coin = compute_coin_commitment<FieldT>(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<bit_vector> old_coin_serial_numbers(num_old_coins); //
std::vector<bit_vector> new_coin_commitments(num_new_coins); //
std::vector<bit_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<FieldT>::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<FieldT>::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<FieldT>(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<FieldT> pb;
zerocash_pour_gadget<FieldT> 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<ppT> keypair = zerocash_pour_ppzksnark_generator<ppT>(num_old_coins, num_new_coins, tree_depth);
keypair = reserialize<zerocash_pour_keypair<ppT> >(keypair);
zerocash_pour_proof<ppT> proof = zerocash_pour_ppzksnark_prover<ppT>(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<zerocash_pour_proof<ppT> >(proof);
const bool verification_result = zerocash_pour_ppzksnark_verifier<ppT>(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<default_r1cs_ppzksnark_pp>(2, 2, 4);
test_zerocash_pour_ppzksnark<default_r1cs_ppzksnark_pp>(2, 3, 4);
test_zerocash_pour_ppzksnark<default_r1cs_ppzksnark_pp>(3, 2, 4);
test_zerocash_pour_ppzksnark<default_r1cs_ppzksnark_pp>(3, 3, 4);
test_zerocash_pour_ppzksnark<default_r1cs_ppzksnark_pp>(2, 2, 32);
}

View File

@ -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,
<https://eprint.iacr.org/2014/349>
*****************************************************************************
* @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_ppzksnark/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<typename FieldT>
class zerocash_pour_gadget : public gadget<FieldT> {
public:
size_t tree_depth;
pb_variable_array<FieldT> input_as_field_elements; /* R1CS input */
pb_variable_array<FieldT> input_as_bits; /* unpacked R1CS input */
std::shared_ptr<multipacking_gadget<FieldT> > unpack_inputs;
/* individual components of the unpacked R1CS input */
std::shared_ptr<digest_variable<FieldT> > merkle_tree_root_variable;
std::vector<std::shared_ptr<digest_variable<FieldT> > > old_coin_serial_number_variables;
pb_variable_array<FieldT> old_coin_enforce_commitment;
std::vector<std::shared_ptr<digest_variable<FieldT> > > new_coin_commitment_variables;
pb_variable_array<FieldT> public_old_value_variable;
pb_variable_array<FieldT> public_new_value_variable;
std::shared_ptr<digest_variable<FieldT> > signature_public_key_hash_variable;
std::vector<std::shared_ptr<digest_variable<FieldT> > > mac_of_signature_public_key_hash_variables;
/* TODO */
pb_variable<FieldT> zero;
std::vector<pb_variable_array<FieldT> > new_address_public_key_variables;
std::vector<pb_variable_array<FieldT> > old_address_secret_key_variables;
std::vector<pb_variable_array<FieldT> > new_address_commitment_nonce_variables;
std::vector<pb_variable_array<FieldT> > old_address_commitment_nonce_variables;
std::vector<pb_variable_array<FieldT> > new_coin_serial_number_nonce_variables;
std::vector<pb_variable_array<FieldT> > old_coin_serial_number_nonce_variables;
std::vector<pb_variable_array<FieldT> > new_coin_value_variables;
std::vector<pb_variable_array<FieldT> > old_coin_value_variables;
std::vector<std::shared_ptr<block_variable<FieldT> > > prf_for_old_coin_serial_number_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > prfs_for_old_coin_serial_numbers; // (C)
std::vector<std::shared_ptr<digest_variable<FieldT> > > old_address_public_key_variables;
std::vector<std::shared_ptr<block_variable<FieldT> > > prf_for_old_address_public_key_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > prfs_for_old_address_public_keys; // (B)
std::vector<std::shared_ptr<digest_variable<FieldT> > > commitments_to_old_address_public_keys;
std::vector<std::shared_ptr<block_variable<FieldT> > > commit_to_old_address_public_key_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > commit_to_old_address_public_keys; // (D0)
std::vector<std::shared_ptr<digest_variable<FieldT> > > old_coin_value_commitment_nonces;
std::vector<std::shared_ptr<block_variable<FieldT> > > commit_to_old_coin_value_commitment_nonce_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > commit_to_old_coin_value_commitment_nonces; // (D1)
std::vector<std::shared_ptr<digest_variable<FieldT> > > old_coin_commitment_variables;
std::vector<std::shared_ptr<block_variable<FieldT> > > compute_old_coin_commitment_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > compute_old_coin_commitments; // (D2)
std::vector<std::shared_ptr<digest_variable<FieldT> > > commitments_to_new_address_public_keys;
std::vector<std::shared_ptr<block_variable<FieldT> > > commit_to_new_address_public_key_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > commit_to_new_address_public_keys; // (E0)
std::vector<std::shared_ptr<digest_variable<FieldT> > > new_coin_value_commitment_nonces;
std::vector<std::shared_ptr<block_variable<FieldT> > > commit_to_new_coin_value_commitment_nonce_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > commit_to_new_coin_value_commitment_nonces; // (E1)
std::vector<std::shared_ptr<block_variable<FieldT> > > compute_new_coin_commitment_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > compute_new_coin_commitments; // (E2)
std::vector<std::shared_ptr<block_variable<FieldT> > > prf_for_macs_of_signature_public_key_hash_input_variables;
std::vector<std::shared_ptr<sha256_compression_function_gadget<FieldT> > > prfs_for_macs_of_signature_public_key_hash; // (F)
std::vector<pb_variable_array<FieldT> > old_coin_merkle_tree_position_variables;
std::vector<std::shared_ptr<merkle_authentication_path_variable<FieldT, sha256_two_to_one_hash_gadget<FieldT> > > > old_coin_authentication_path_variables;
std::vector<std::shared_ptr<merkle_tree_check_read_gadget<FieldT, sha256_two_to_one_hash_gadget<FieldT> > > > old_coin_commitments_in_tree; // (A)
size_t num_old_coins;
size_t num_new_coins;
zerocash_pour_gadget(protoboard<FieldT> &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<merkle_authentication_path> &old_coin_authentication_paths,
const std::vector<size_t> &old_coin_merkle_tree_positions,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &new_address_public_keys,
const std::vector<bit_vector> &old_address_secret_keys,
const std::vector<bit_vector> &new_address_commitment_nonces,
const std::vector<bit_vector> &old_address_commitment_nonces,
const std::vector<bit_vector> &new_coin_serial_number_nonces,
const std::vector<bit_vector> &old_coin_serial_number_nonces,
const std::vector<bit_vector> &new_coin_values,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const std::vector<bit_vector> &old_coin_values,
const bit_vector &signature_public_key_hash);
};
template<typename FieldT>
r1cs_primary_input<FieldT> 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<bit_vector> &old_coin_serial_numbers,
const std::vector<bit_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<bit_vector> &signature_public_key_hash_macs);
} // libzerocash
#include "zerocash_pour_ppzksnark/zerocash_pour_gadget.tcc"
#endif // ZEROCASH_POUR_GADGET_HPP_

View File

@ -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<typename FieldT>
zerocash_pour_gadget<FieldT>::zerocash_pour_gadget(protoboard<FieldT> &pb,
const size_t num_old_coins,
const size_t num_new_coins,
const size_t tree_depth,
const std::string &annotation_prefix) :
gadget<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(this->pb, input_as_bits, input_as_field_elements, FieldT::capacity(), FMT(this->annotation_prefix, " unpack_inputs")));
pb_linear_combination_array<FieldT> 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<FieldT> 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<FieldT>(pb, {
old_address_secret_key_variables[i],
zero_one,
pb_variable_array<FieldT>(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<FieldT>(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<FieldT>(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<FieldT> addr_pk_pad(address_public_key_padding_length, zero);
prf_for_old_address_public_key_input_variables[i].reset(new block_variable<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(pb, { old_address_commitment_nonce_variables[i], pb_variable_array<FieldT>(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<FieldT>(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<FieldT> 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<FieldT>(pb, sha256_digest_len, FMT(annotation_prefix, " old_coin_commitment_variables_%zu", i)));
compute_old_coin_commitment_input_variables[i].reset(new block_variable<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(pb, { new_address_commitment_nonce_variables[i], pb_variable_array<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT> 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<FieldT>(pb, { old_address_secret_key_variables[i], prf_padding, pb_variable_array<FieldT>(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<FieldT>(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<FieldT, sha256_two_to_one_hash_gadget<FieldT> >(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<FieldT, sha256_two_to_one_hash_gadget<FieldT> >(
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<typename FieldT>
void zerocash_pour_gadget<FieldT>::generate_r1cs_constraints()
{
generate_r1cs_equals_const_constraint<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(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<FieldT>(this->pb, old_coin_enforce_commitment[i], FMT(this->annotation_prefix, " old_coin_enforce_commitment_%zu", i));
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(
pb_packing_sum<FieldT>(pb_variable_array<FieldT>(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<FieldT> old_packed_value;
for (size_t i = 0; i < num_old_coins; ++i)
{
old_packed_value = old_packed_value + pb_packing_sum<FieldT>(pb_variable_array<FieldT>(old_coin_value_variables[i].rbegin(), old_coin_value_variables[i].rend()));
}
old_packed_value = old_packed_value + pb_packing_sum<FieldT>(pb_variable_array<FieldT>(public_old_value_variable.rbegin(), public_old_value_variable.rend()));
linear_combination<FieldT> new_packed_value;
for (size_t i = 0; i < num_new_coins; ++i)
{
new_packed_value = new_packed_value + pb_packing_sum<FieldT>(pb_variable_array<FieldT>(new_coin_value_variables[i].rbegin(), new_coin_value_variables[i].rend()));
}
new_packed_value = new_packed_value + pb_packing_sum<FieldT>(pb_variable_array<FieldT>(public_new_value_variable.rbegin(), public_new_value_variable.rend()));
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(1, old_packed_value, new_packed_value), FMT(this->annotation_prefix, " balance"));
}
template<typename FieldT>
void zerocash_pour_gadget<FieldT>::generate_r1cs_witness(const std::vector<merkle_authentication_path> &old_coin_authentication_paths,
const std::vector<size_t> &old_coin_merkle_tree_positions,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &new_address_public_keys,
const std::vector<bit_vector> &old_address_secret_keys,
const std::vector<bit_vector> &new_address_commitment_nonces,
const std::vector<bit_vector> &old_address_commitment_nonces,
const std::vector<bit_vector> &new_coin_serial_number_nonces,
const std::vector<bit_vector> &old_coin_serial_number_nonces,
const std::vector<bit_vector> &new_coin_values,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const std::vector<bit_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<typename FieldT>
r1cs_primary_input<FieldT> 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<bit_vector> &old_coin_serial_numbers,
const std::vector<bit_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<bit_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<FieldT> input_as_field_elements = pack_bit_vector_into_field_element_vector<FieldT>(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

View File

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

View File

@ -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,
<https://eprint.iacr.org/2014/349>
*****************************************************************************
* @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 <stdexcept>
namespace libzerocash {
/******************************** Proving key ********************************/
/**
* A proving key for the Pour ppzkSNARK.
*/
template<typename ppzksnark_ppT>
class zerocash_pour_proving_key;
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_proving_key<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_proving_key<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
class zerocash_pour_proving_key {
public:
size_t num_old_coins;
size_t num_new_coins;
size_t tree_depth;
r1cs_ppzksnark_proving_key<ppzksnark_ppT> r1cs_pk;
zerocash_pour_proving_key() = default;
zerocash_pour_proving_key(const zerocash_pour_proving_key<ppzksnark_ppT> &other) = default;
zerocash_pour_proving_key(zerocash_pour_proving_key<ppzksnark_ppT> &&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<ppzksnark_ppT> &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<ppzksnark_ppT> &&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<ppzksnark_ppT>& operator=(const zerocash_pour_proving_key<ppzksnark_ppT> &other) = default;
bool operator==(const zerocash_pour_proving_key<ppzksnark_ppT> &other) const;
friend std::ostream& operator<< <ppzksnark_ppT>(std::ostream &out, const zerocash_pour_proving_key<ppzksnark_ppT> &pk);
friend std::istream& operator>> <ppzksnark_ppT>(std::istream &in, zerocash_pour_proving_key<ppzksnark_ppT> &pk);
};
/******************************* Verification key ****************************/
/**
* A verification key for the Pour ppzkSNARK.
*/
template<typename ppzksnark_ppT>
class zerocash_pour_verification_key;
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_verification_key<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_verification_key<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
class zerocash_pour_verification_key {
public:
size_t num_old_coins;
size_t num_new_coins;
r1cs_ppzksnark_verification_key<ppzksnark_ppT> r1cs_vk;
zerocash_pour_verification_key() = default;
zerocash_pour_verification_key(const zerocash_pour_verification_key<ppzksnark_ppT> &other) = default;
zerocash_pour_verification_key(zerocash_pour_verification_key<ppzksnark_ppT> &&other) = default;
zerocash_pour_verification_key(const size_t num_old_coins,
const size_t num_new_coins,
const r1cs_ppzksnark_verification_key<ppzksnark_ppT> &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<ppzksnark_ppT> &&r1cs_vk) :
num_old_coins(num_old_coins), num_new_coins(num_new_coins),
r1cs_vk(std::move(r1cs_vk)) {}
zerocash_pour_verification_key<ppzksnark_ppT>& operator=(const zerocash_pour_verification_key<ppzksnark_ppT> &other) = default;
bool operator==(const zerocash_pour_verification_key<ppzksnark_ppT> &other) const;
friend std::ostream& operator<< <ppzksnark_ppT>(std::ostream &out, const zerocash_pour_verification_key<ppzksnark_ppT> &pk);
friend std::istream& operator>> <ppzksnark_ppT>(std::istream &in, zerocash_pour_verification_key<ppzksnark_ppT> &pk);
};
/********************************** Key pair *********************************/
/**
* A key pair for the Pour ppzkSNARK, which consists of a proving key and a verification key.
*/
template<typename ppzksnark_ppT>
class zerocash_pour_keypair;
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_keypair<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_keypair<ppzksnark_ppT> &pk);
template<typename ppzksnark_ppT>
class zerocash_pour_keypair {
public:
zerocash_pour_proving_key<ppzksnark_ppT> pk;
zerocash_pour_verification_key<ppzksnark_ppT> vk;
zerocash_pour_keypair() = default;
zerocash_pour_keypair(const zerocash_pour_keypair<ppzksnark_ppT> &other) = default;
zerocash_pour_keypair(zerocash_pour_keypair<ppzksnark_ppT> &&other) = default;
zerocash_pour_keypair(const zerocash_pour_proving_key<ppzksnark_ppT> &pk,
const zerocash_pour_verification_key<ppzksnark_ppT> &vk) :
pk(pk), vk(vk) {}
zerocash_pour_keypair(zerocash_pour_proving_key<ppzksnark_ppT> &&pk,
zerocash_pour_verification_key<ppzksnark_ppT> &&vk) :
pk(std::move(pk)),
vk(std::move(vk)) {}
zerocash_pour_keypair<ppzksnark_ppT>& operator=(const zerocash_pour_keypair<ppzksnark_ppT> &other) = default;
bool operator==(const zerocash_pour_keypair<ppzksnark_ppT> &other) const;
friend std::ostream& operator<< <ppzksnark_ppT>(std::ostream &out, const zerocash_pour_keypair<ppzksnark_ppT> &pk);
friend std::istream& operator>> <ppzksnark_ppT>(std::istream &in, zerocash_pour_keypair<ppzksnark_ppT> &pk);
};
/*********************************** Proof ***********************************/
/**
* A proof for the Pour ppzkSNARK.
*/
template<typename ppzksnark_ppT>
using zerocash_pour_proof = r1cs_ppzksnark_proof<ppzksnark_ppT>;
/***************************** 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<typename ppzksnark_ppT>
zerocash_pour_keypair<ppzksnark_ppT> 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<typename ppzksnark_ppT>
zerocash_pour_proof<ppzksnark_ppT> zerocash_pour_ppzksnark_prover(const zerocash_pour_proving_key<ppzksnark_ppT> &pk,
const std::vector<merkle_authentication_path> &old_coin_authentication_paths,
const std::vector<size_t> &old_coin_merkle_tree_positions,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &new_address_public_keys,
const std::vector<bit_vector> &old_address_secret_keys,
const std::vector<bit_vector> &new_address_commitment_nonces,
const std::vector<bit_vector> &old_address_commitment_nonces,
const std::vector<bit_vector> &new_coin_serial_number_nonces,
const std::vector<bit_vector> &old_coin_serial_number_nonces,
const std::vector<bit_vector> &new_coin_values,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const std::vector<bit_vector> &old_coin_values,
const bit_vector &signature_public_key_hash);
/**
* A verifier algorithm for the Pour ppzkSNARK.
*/
template<typename ppzksnark_ppT>
bool zerocash_pour_ppzksnark_verifier(const zerocash_pour_verification_key<ppzksnark_ppT> &vk,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &old_coin_serial_numbers,
const std::vector<bit_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<bit_vector> &signature_public_key_hash_macs,
const zerocash_pour_proof<ppzksnark_ppT> &proof);
} // libzerocash
#include "zerocash_pour_ppzksnark/zerocash_pour_ppzksnark.tcc"
#endif // ZEROCASH_POUR_PPZKSNARK_HPP_

View File

@ -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_ppzksnark/zerocash_pour_gadget.hpp"
#include "common/profiling.hpp"
namespace libzerocash {
template<typename ppzksnark_ppT>
bool zerocash_pour_proving_key<ppzksnark_ppT>::operator==(const zerocash_pour_proving_key<ppzksnark_ppT> &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<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_proving_key<ppzksnark_ppT> &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<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_proving_key<ppzksnark_ppT> &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<typename ppzksnark_ppT>
bool zerocash_pour_verification_key<ppzksnark_ppT>::operator==(const zerocash_pour_verification_key<ppzksnark_ppT> &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<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_verification_key<ppzksnark_ppT> &vk)
{
out << vk.num_old_coins << "\n";
out << vk.num_new_coins << "\n";
out << vk.r1cs_vk;
return out;
}
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_verification_key<ppzksnark_ppT> &vk)
{
in >> vk.num_old_coins;
consume_newline(in);
in >> vk.num_new_coins;
consume_newline(in);
in >> vk.r1cs_vk;
return in;
}
template<typename ppzksnark_ppT>
bool zerocash_pour_keypair<ppzksnark_ppT>::operator==(const zerocash_pour_keypair<ppzksnark_ppT> &other) const
{
return (this->pk == other.pk &&
this->vk == other.vk);
}
template<typename ppzksnark_ppT>
std::ostream& operator<<(std::ostream &out, const zerocash_pour_keypair<ppzksnark_ppT> &kp)
{
out << kp.pk;
out << kp.vk;
return out;
}
template<typename ppzksnark_ppT>
std::istream& operator>>(std::istream &in, zerocash_pour_keypair<ppzksnark_ppT> &kp)
{
in >> kp.pk;
in >> kp.vk;
return in;
}
template<typename ppzksnark_ppT>
zerocash_pour_keypair<ppzksnark_ppT> zerocash_pour_ppzksnark_generator(const size_t num_old_coins,
const size_t num_new_coins,
const size_t tree_depth)
{
typedef Fr<ppzksnark_ppT> FieldT;
enter_block("Call to zerocash_pour_ppzksnark_generator");
protoboard<FieldT> pb;
zerocash_pour_gadget<FieldT> g(pb, num_old_coins, num_new_coins, tree_depth, "zerocash_pour");
g.generate_r1cs_constraints();
const r1cs_constraint_system<FieldT> constraint_system = pb.get_constraint_system();
r1cs_ppzksnark_keypair<ppzksnark_ppT> ppzksnark_keypair = r1cs_ppzksnark_generator<ppzksnark_ppT>(constraint_system);
leave_block("Call to zerocash_pour_ppzksnark_generator");
zerocash_pour_proving_key<ppzksnark_ppT> zerocash_pour_pk(num_old_coins, num_new_coins, tree_depth, std::move(ppzksnark_keypair.pk));
zerocash_pour_verification_key<ppzksnark_ppT> zerocash_pour_vk(num_old_coins, num_new_coins, std::move(ppzksnark_keypair.vk));
return zerocash_pour_keypair<ppzksnark_ppT>(std::move(zerocash_pour_pk), std::move(zerocash_pour_vk));
}
template<typename ppzksnark_ppT>
zerocash_pour_proof<ppzksnark_ppT> zerocash_pour_ppzksnark_prover(const zerocash_pour_proving_key<ppzksnark_ppT> &pk,
const std::vector<merkle_authentication_path> &old_coin_authentication_paths,
const std::vector<size_t> &old_coin_merkle_tree_positions,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &new_address_public_keys,
const std::vector<bit_vector> &old_address_secret_keys,
const std::vector<bit_vector> &new_address_commitment_nonces,
const std::vector<bit_vector> &old_address_commitment_nonces,
const std::vector<bit_vector> &new_coin_serial_number_nonces,
const std::vector<bit_vector> &old_coin_serial_number_nonces,
const std::vector<bit_vector> &new_coin_values,
const bit_vector &public_old_value,
const bit_vector &public_new_value,
const std::vector<bit_vector> &old_coin_values,
const bit_vector &signature_public_key_hash)
{
typedef Fr<ppzksnark_ppT> FieldT;
enter_block("Call to zerocash_pour_ppzksnark_prover");
protoboard<FieldT> pb;
zerocash_pour_gadget<FieldT > 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<ppzksnark_ppT> proof = r1cs_ppzksnark_prover<ppzksnark_ppT>(pk.r1cs_pk, pb.primary_input(), pb.auxiliary_input());
leave_block("Call to zerocash_pour_ppzksnark_prover");
return proof;
}
template<typename ppzksnark_ppT>
bool zerocash_pour_ppzksnark_verifier(const zerocash_pour_verification_key<ppzksnark_ppT> &vk,
const bit_vector &merkle_tree_root,
const std::vector<bit_vector> &old_coin_serial_numbers,
const std::vector<bit_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<bit_vector> &signature_public_key_hash_macs,
const zerocash_pour_proof<ppzksnark_ppT> &proof)
{
typedef Fr<ppzksnark_ppT> FieldT;
enter_block("Call to zerocash_pour_ppzksnark_verifier");
const r1cs_primary_input<FieldT> input = zerocash_pour_input_map<FieldT>(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<ppzksnark_ppT>(vk.r1cs_vk, input, proof);
leave_block("Call to zerocash_pour_ppzksnark_verifier");
return ans;
}
} // libzerocash
#endif // ZEROCASH_POUR_PPZKSNARK_TCC_