Move libzerocash (dd5db5815be70f0e4895784cc905df6f1c73cb17) into the src tree.
This commit is contained in:
parent
dba05929e7
commit
523bc77f64
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
||||||
|
Eli Ben-Sasson
|
||||||
|
Alessandro Chiesa
|
||||||
|
Christina Garman
|
||||||
|
Matthew Green
|
||||||
|
Ian Miers
|
||||||
|
Eran Tromer
|
||||||
|
Madars Virza
|
|
@ -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.
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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_ */
|
|
@ -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 */
|
|
@ -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_ */
|
|
@ -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 */
|
|
@ -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_ */
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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_ */
|
||||||
|
|
|
@ -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 */
|
|
@ -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_ */
|
|
@ -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 */
|
|
@ -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_ */
|
|
@ -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 */
|
|
@ -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_ */
|
|
@ -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"
|
|
@ -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_ */
|
|
@ -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 */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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 */
|
|
@ -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_ */
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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 */
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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_ */
|
||||||
|
|
|
@ -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) );
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
Loading…
Reference in New Issue