[build] autoconf adaptions for new files and targets and update dependencies

This commit is contained in:
jc 2018-04-01 10:32:10 -04:00
parent 77d280acfc
commit 8e04b9dd54
57 changed files with 6315 additions and 48 deletions

3
.gitignore vendored
View File

@ -116,3 +116,6 @@ test/cache/*
libbitcoinconsensus.pc
contrib/devtools/split-debug.sh
src/zcash/CreateJoinSplit
src/zcash/GenerateParams

View File

@ -65,7 +65,8 @@ OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) \
COVERAGE_INFO = baseline.info \
test_bitcoin_filtered.info total_coverage.info \
baseline_filtered.info functional_test.info functional_test_filtered.info \
test_bitcoin_coverage.info test_bitcoin.info
test_bitcoin_coverage.info test_bitcoin.info \
zcash-gtest.info zcash-gtest_filtered.info zcash-gtest_coverage.info
dist-hook:
-$(GIT) archive --format=tar HEAD -- src/clientversion.cpp | $(AMTAR) -C $(top_distdir) -xf -
@ -187,6 +188,24 @@ test_bitcoin_filtered.info: test_bitcoin.info
$(abs_builddir)/contrib/filter-lcov.py $(LCOV_FILTER_PATTERN) $< $@
$(LCOV) -a $@ $(LCOV_OPTS) -o $@
zcash-gtest.info: baseline_filtered_combined.info
$(MAKE) -C src/ zcash-gtest_check
$(LCOV) -c -d $(abs_builddir)/src -t zcash-gtest -o $@
$(LCOV) -z -d $(abs_builddir)/src
$(LCOV) -z -d $(abs_builddir)/src/leveldb
zcash-gtest_filtered.info: zcash-gtest.info
$(LCOV) -r $< "/usr/include/*" \
"$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \
"$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \
"$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \
"$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \
"$(abs_builddir)/src/gtest/*" \
"$(abs_builddir)/src/test/*" \
"$(abs_builddir)/src/wallet/gtest/*" \
"$(abs_builddir)/src/wallet/test/*" \
-o $@
functional_test.info: test_bitcoin_filtered.info
-@TIMEOUT=15 test/functional/test_runner.py $(EXTENDED_FUNCTIONAL_TESTS)
$(LCOV) -c $(LCOV_OPTS) -d $(abs_builddir)/src --t functional-tests -o $@
@ -199,24 +218,34 @@ functional_test_filtered.info: functional_test.info
test_bitcoin_coverage.info: baseline_filtered.info test_bitcoin_filtered.info
$(LCOV) -a $(LCOV_OPTS) baseline_filtered.info -a test_bitcoin_filtered.info -o $@
total_coverage.info: test_bitcoin_filtered.info functional_test_filtered.info
$(LCOV) -a $(LCOV_OPTS) baseline_filtered.info -a test_bitcoin_filtered.info -a functional_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt
zcash-gtest_coverage.info: baseline_filtered_combined.info zcash-gtest_filtered.info
$(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a zcash-gtest_filtered.info -o $@
total_coverage.info: test_bitcoin_filtered.info functional_test_filtered.info zcash-gtest_filtered.info
$(LCOV) -a $(LCOV_OPTS) baseline_filtered.info -a test_bitcoin_filtered.info -a functional_test_filtered.info -a zcash-gtest_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt
test_bitcoin.coverage/.dirstamp: test_bitcoin_coverage.info
$(GENHTML) -s $(LCOV_OPTS) $< -o $(@D)
@touch $@
zcash-gtest.coverage/.dirstamp: zcash-gtest_coverage.info
$(GENHTML) -s $< -o $(@D)
@touch $@
cov-zcash: zcash-gtest.coverage/.dirstamp
total.coverage/.dirstamp: total_coverage.info
$(GENHTML) -s $(LCOV_OPTS) $< -o $(@D)
@touch $@
cov: test_bitcoin.coverage/.dirstamp total.coverage/.dirstamp
cov: test_bitcoin.coverage/.dirstamp cov-zcash total.coverage/.dirstamp
endif
dist_bin_SCRIPTS = zcutil/fetch-params.sh
dist_noinst_SCRIPTS = autogen.sh
EXTRA_DIST = $(DIST_SHARE) test/functional/test_runner.py test/functional $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
EXTRA_DIST = $(DIST_SHARE) test/functional/test_runner.py test/functional test/zcash $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
EXTRA_DIST += \
test/util/bitcoin-util-test.py \
@ -273,6 +302,8 @@ EXTRA_DIST += \
test/util/rpcauth-test.py
CLEANFILES = $(OSX_DMG) $(BITCOIN_WIN_INSTALLER)
install-exec-hook:
mv $(DESTDIR)$(bindir)/fetch-params.sh $(DESTDIR)$(bindir)/zcash-fetch-params
.INTERMEDIATE: $(COVERAGE_INFO)
@ -294,6 +325,6 @@ clean-docs:
rm -rf doc/doxygen
clean-local: clean-docs
rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP)
rm -rf coverage_percent.txt test_bitcoin.coverage/ zcash-gtest.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP)
rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache

123
build-aux/m4/ax_openmp.m4 Normal file
View File

@ -0,0 +1,123 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_openmp.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_OPENMP([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro tries to find out how to compile programs that use OpenMP a
# standard API and set of compiler directives for parallel programming
# (see http://www-unix.mcs/)
#
# On success, it sets the OPENMP_CFLAGS/OPENMP_CXXFLAGS/OPENMP_F77FLAGS
# output variable to the flag (e.g. -omp) used both to compile *and* link
# OpenMP programs in the current language.
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also link it with them as well.
#
# If you want to compile everything with OpenMP, you should set:
#
# CFLAGS="$CFLAGS $OPENMP_CFLAGS"
# #OR# CXXFLAGS="$CXXFLAGS $OPENMP_CXXFLAGS"
# #OR# FFLAGS="$FFLAGS $OPENMP_FFLAGS"
#
# (depending on the selected language).
#
# The user can override the default choice by setting the corresponding
# environment variable (e.g. OPENMP_CFLAGS).
#
# ACTION-IF-FOUND is a list of shell commands to run if an OpenMP flag is
# found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it is
# not found. If ACTION-IF-FOUND is not specified, the default action will
# define HAVE_OPENMP.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
# Copyright (c) 2015 John W. Peterson <jwpeterson@gmail.com>
# Copyright (c) 2016 Nick R. Papior <nickpapior@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 13
AC_DEFUN([AX_OPENMP], [
AC_PREREQ([2.69]) dnl for _AC_LANG_PREFIX
AC_CACHE_CHECK([for OpenMP flag of _AC_LANG compiler], ax_cv_[]_AC_LANG_ABBREV[]_openmp, [save[]_AC_LANG_PREFIX[]FLAGS=$[]_AC_LANG_PREFIX[]FLAGS
ax_cv_[]_AC_LANG_ABBREV[]_openmp=unknown
# Flags to try: -fopenmp (gcc), -mp (SGI & PGI),
# -qopenmp (icc>=15), -openmp (icc),
# -xopenmp (Sun), -omp (Tru64),
# -qsmp=omp (AIX),
# none
ax_openmp_flags="-fopenmp -openmp -qopenmp -mp -xopenmp -omp -qsmp=omp none"
if test "x$OPENMP_[]_AC_LANG_PREFIX[]FLAGS" != x; then
ax_openmp_flags="$OPENMP_[]_AC_LANG_PREFIX[]FLAGS $ax_openmp_flags"
fi
for ax_openmp_flag in $ax_openmp_flags; do
case $ax_openmp_flag in
none) []_AC_LANG_PREFIX[]FLAGS=$save[]_AC_LANG_PREFIX[] ;;
*) []_AC_LANG_PREFIX[]FLAGS="$save[]_AC_LANG_PREFIX[]FLAGS $ax_openmp_flag" ;;
esac
AC_LINK_IFELSE([AC_LANG_SOURCE([[
@%:@include <omp.h>
static void
parallel_fill(int * data, int n)
{
int i;
@%:@pragma omp parallel for
for (i = 0; i < n; ++i)
data[i] = i;
}
int
main()
{
int arr[100000];
omp_set_num_threads(2);
parallel_fill(arr, 100000);
return 0;
}
]])],[ax_cv_[]_AC_LANG_ABBREV[]_openmp=$ax_openmp_flag; break],[])
done
[]_AC_LANG_PREFIX[]FLAGS=$save[]_AC_LANG_PREFIX[]FLAGS
])
if test "x$ax_cv_[]_AC_LANG_ABBREV[]_openmp" = "xunknown"; then
m4_default([$2],:)
else
if test "x$ax_cv_[]_AC_LANG_ABBREV[]_openmp" != "xnone"; then
OPENMP_[]_AC_LANG_PREFIX[]FLAGS=$ax_cv_[]_AC_LANG_ABBREV[]_openmp
fi
m4_default([$1], [AC_DEFINE(HAVE_OPENMP,1,[Define if OpenMP is enabled])])
fi
])dnl AX_OPENMP

View File

@ -2,7 +2,7 @@ dnl Copyright (c) 2013-2015 The Bitcoin Core developers
dnl Distributed under the MIT software license, see the accompanying
dnl file COPYING or http://www.opensource.org/licenses/mit-license.php.
AC_DEFUN([BITCOIN_FIND_BDB48],[
AC_DEFUN([BITCOIN_FIND_BDB62],[
AC_ARG_VAR(BDB_CFLAGS, [C compiler flags for BerkeleyDB, bypasses autodetection])
AC_ARG_VAR(BDB_LIBS, [Linker flags for BerkeleyDB, bypasses autodetection])
@ -10,9 +10,9 @@ AC_DEFUN([BITCOIN_FIND_BDB48],[
AC_MSG_CHECKING([for Berkeley DB C++ headers])
BDB_CPPFLAGS=
bdbpath=X
bdb48path=X
bdb62path=X
bdbdirlist=
for _vn in 4.8 48 4 5 5.3 ''; do
for _vn in 6.2 62 6 ''; do
for _pfx in b lib ''; do
bdbdirlist="$bdbdirlist ${_pfx}db${_vn}"
done
@ -22,8 +22,8 @@ AC_DEFUN([BITCOIN_FIND_BDB48],[
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <${searchpath}db_cxx.h>
]],[[
#if !((DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 8) || DB_VERSION_MAJOR > 4)
#error "failed to find bdb 4.8+"
#if !((DB_VERSION_MAJOR == 6 && DB_VERSION_MINOR >= 2) || DB_VERSION_MAJOR > 6)
#error "failed to find bdb 6.2+"
#endif
]])],[
if test "x$bdbpath" = "xX"; then
@ -35,36 +35,36 @@ AC_DEFUN([BITCOIN_FIND_BDB48],[
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <${searchpath}db_cxx.h>
]],[[
#if !(DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 8)
#error "failed to find bdb 4.8"
#if !(DB_VERSION_MAJOR == 6 && DB_VERSION_MINOR == 2)
#error "failed to find bdb 6.2"
#endif
]])],[
bdb48path="${searchpath}"
bdb62path="${searchpath}"
break
],[])
done
if test "x$bdbpath" = "xX"; then
AC_MSG_RESULT([no])
AC_MSG_ERROR([libdb_cxx headers missing, ]AC_PACKAGE_NAME[ requires this library for wallet functionality (--disable-wallet to disable wallet functionality)])
elif test "x$bdb48path" = "xX"; then
elif test "x$bdb62path" = "xX"; then
BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdbpath}],db_cxx)
AC_ARG_WITH([incompatible-bdb],[AS_HELP_STRING([--with-incompatible-bdb], [allow using a bdb version other than 4.8])],[
AC_MSG_WARN([Found Berkeley DB other than 4.8; wallets opened by this build will not be portable!])
AC_ARG_WITH([incompatible-bdb],[AS_HELP_STRING([--with-incompatible-bdb], [allow using a bdb version other than 6.2])],[
AC_MSG_WARN([Found Berkeley DB other than 6.2; wallets opened by this build will not be portable!])
],[
AC_MSG_ERROR([Found Berkeley DB other than 4.8, required for portable wallets (--with-incompatible-bdb to ignore or --disable-wallet to disable wallet functionality)])
AC_MSG_ERROR([Found Berkeley DB other than 6.2, required for portable wallets (--with-incompatible-bdb to ignore or --disable-wallet to disable wallet functionality)])
])
else
BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdb48path}],db_cxx)
bdbpath="${bdb48path}"
BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdb62path}],db_cxx)
bdbpath="${bdb62path}"
fi
else
BDB_CPPFLAGS=${BDB_CFLAGS}
fi
AC_SUBST(BDB_CPPFLAGS)
if test "x$BDB_LIBS" = "x"; then
# TODO: Ideally this could find the library version and make sure it matches the headers being used
for searchlib in db_cxx-4.8 db_cxx db4_cxx; do
for searchlib in db_cxx-6.2 db_cxx db6_cxx; do
AC_CHECK_LIB([$searchlib],[main],[
BDB_LIBS="-l${searchlib}"
break

View File

@ -8,6 +8,7 @@ define(_CLIENT_VERSION_IS_RELEASE, false)
define(_COPYRIGHT_YEAR, 2018)
define(_COPYRIGHT_HOLDERS,[The %s developers])
define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Bitcoin Core]])
define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50)))
AC_INIT([Bitcoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[https://github.com/bitcoin/bitcoin/issues],[bitcoin],[https://bitcoincore.org/])
AC_CONFIG_SRCDIR([src/validation.cpp])
AC_CONFIG_HEADERS([src/config/bitcoin-config.h])
@ -108,6 +109,12 @@ AC_ARG_ENABLE([wallet],
[enable_wallet=$enableval],
[enable_wallet=yes])
AC_ARG_ENABLE([mining],
[AS_HELP_STRING([--enable-mining],
[enable mining (default is yes)])],
[enable_mining=$enableval],
[enable_mining=yes])
AC_ARG_WITH([miniupnpc],
[AS_HELP_STRING([--with-miniupnpc],
[enable UPNP (default is yes if libminiupnpc is found)])],
@ -281,6 +288,26 @@ if test x$use_sanitizers != x; then
[AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])])
fi
if test x$use_sanitizers != x; then
# First check if the compiler accepts flags. If an incompatible pair like
# -fsanitize=address,thread is used here, this check will fail. This will also
# fail if a bad argument is passed, e.g. -fsanitize=undfeined
AX_CHECK_COMPILE_FLAG(
[[-fsanitize=$use_sanitizers]],
[[SANITIZER_CXXFLAGS=-fsanitize=$use_sanitizers]],
[AC_MSG_ERROR([compiler did not accept requested flags])])
# Some compilers (e.g. GCC) require additional libraries like libasan,
# libtsan, libubsan, etc. Make sure linking still works with the sanitize
# flag. This is a separate check so we can give a better error message when
# the sanitize flags are supported by the compiler but the actual sanitizer
# libs are missing.
AX_CHECK_LINK_FLAG(
[[-fsanitize=$use_sanitizers]],
[[SANITIZER_LDFLAGS=-fsanitize=$use_sanitizers]],
[AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])])
fi
ERROR_CXXFLAGS=
if test "x$enable_werror" = "xyes"; then
if test "x$CXXFLAG_WERROR" = "x"; then
@ -428,9 +455,9 @@ case $host in
dnl add default macports paths
CPPFLAGS="$CPPFLAGS -isystem /opt/local/include"
LIBS="$LIBS -L/opt/local/lib"
if test -d /opt/local/include/db48; then
CPPFLAGS="$CPPFLAGS -I/opt/local/include/db48"
LIBS="$LIBS -L/opt/local/lib/db48"
if test -d /opt/local/include/db62; then
CPPFLAGS="$CPPFLAGS -I/opt/local/include/db62"
LIBS="$LIBS -L/opt/local/lib/db62"
fi
fi
@ -778,7 +805,7 @@ AC_SUBST(LIBMEMENV)
if test x$enable_wallet != xno; then
dnl Check for libdb_cxx only if wallet enabled
BITCOIN_FIND_BDB48
BITCOIN_FIND_BDB62
fi
dnl Check for libminiupnpc (optional)
@ -804,7 +831,7 @@ fi
if test x$use_boost = xyes; then
dnl Minimum required Boost version
define(MINIMUM_REQUIRED_BOOST, 1.47.0)
define(MINIMUM_REQUIRED_BOOST, 1.62)
dnl Check for boost libs
AX_BOOST_BASE([MINIMUM_REQUIRED_BOOST])
@ -1102,6 +1129,40 @@ AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$need_bundled_univalue = xyes])
AC_SUBST(UNIVALUE_CFLAGS)
AC_SUBST(UNIVALUE_LIBS)
# These packages don't provide pkgconfig config files across all
# platforms, so we use older autoconf detection mechanisms:
AC_CHECK_HEADER([gmp.h],,AC_MSG_ERROR(libgmp headers missing))
AC_CHECK_LIB([gmp],[[__gmpn_sub_n]],GMP_LIBS=-lgmp, [AC_MSG_ERROR(libgmp missing)])
AC_CHECK_HEADER([gmpxx.h],,AC_MSG_ERROR(libgmpxx headers missing))
AC_CHECK_LIB([gmpxx],[main],GMPXX_LIBS=-lgmpxx, [AC_MSG_ERROR(libgmpxx missing)])
RUST_LIBS="-lrustzcash"
dnl Check for OpenMP support
AX_OPENMP(
[AC_DEFINE(HAVE_OPENMP, 1, [Define if OpenMP is enabled])
AM_CONDITIONAL([HAVE_OPENMP], [true])
CXXFLAGS="$CXXFLAGS $OPENMP_CXXFLAGS"],
[AC_MSG_WARN([OpenMP not supported, disabling multithreading])
AC_DEFINE(HAVE_OPENMP, 0, [Define if OpenMP is enabled])
AM_CONDITIONAL([HAVE_OPENMP], [false])])
# Gitian uses a config.site that sets depends_prefix, and then sets --prefix=/
# build.sh just uses --prefix
if test x$depends_prefix != x; then
LIBSNARK_DEPINST="$depends_prefix"
else
LIBSNARK_DEPINST="$prefix"
fi
# Additional Zcash flags
AX_CHECK_COMPILE_FLAG([-fwrapv],[CXXFLAGS="$CXXFLAGS -fwrapv"])
AX_CHECK_COMPILE_FLAG([-fno-strict-aliasing],[CXXFLAGS="$CXXFLAGS -fno-strict-aliasing"])
AX_CHECK_COMPILE_FLAG([-Wno-builtin-declaration-mismatch],[CXXFLAGS="$CXXFLAGS -Wno-builtin-declaration-mismatch"],,[[$CXXFLAG_WERROR]])
LIBZCASH_LIBS="-lgmp -lgmpxx $BOOST_SYSTEM_LIB -lcrypto -lsodium $RUST_LIBS"
BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path)
AC_MSG_CHECKING([whether to build bitcoind])
@ -1151,6 +1212,15 @@ else
AC_MSG_RESULT(no)
fi
dnl enable mining
AC_MSG_CHECKING([if mining should be enabled])
if test x$enable_mining != xno; then
AC_MSG_RESULT(yes)
AC_DEFINE(ENABLE_MINING, 1, [Define to 1 to enable mining functions])
else
AC_MSG_RESULT(no)
fi
dnl enable upnp support
AC_MSG_CHECKING([whether to build with support for UPnP])
if test x$have_miniupnpc = xno; then
@ -1244,6 +1314,7 @@ AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin])
AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin])
AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows])
AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
AM_CONDITIONAL([ENABLE_MINING],[test x$enable_mining = xyes])
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])
AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes])
AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes])
@ -1310,6 +1381,10 @@ AC_SUBST(EVENT_PTHREADS_LIBS)
AC_SUBST(ZMQ_LIBS)
AC_SUBST(PROTOBUF_LIBS)
AC_SUBST(QR_LIBS)
AC_SUBST(GMP_LIBS)
AC_SUBST(GMPXX_LIBS)
AC_SUBST(LIBSNARK_DEPINST)
AC_SUBST(LIBZCASH_LIBS)
AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini])
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile])])
@ -1346,7 +1421,7 @@ if test x$need_bundled_univalue = xyes; then
fi
ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery --disable-jni"
AC_CONFIG_SUBDIRS([src/secp256k1])
AC_CONFIG_SUBDIRS([src/secp256k1 src/snark])
AC_OUTPUT

View File

@ -7,6 +7,7 @@ SDK_PATH ?= $(BASEDIR)/SDKs
NO_QT ?=
NO_WALLET ?=
NO_UPNP ?=
PRIORITY_DOWNLOAD_PATH ?= https://z.cash/depends-sources
FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources
BUILD = $(shell ./config.guess)
@ -16,12 +17,14 @@ BASEDIR = $(CURDIR)
HASH_LENGTH:=11
DOWNLOAD_CONNECT_TIMEOUT:=10
DOWNLOAD_RETRIES:=3
CRATE_REGISTRY:=vendored-sources
HOST_ID_SALT ?= salt
BUILD_ID_SALT ?= salt
host:=$(BUILD)
ifneq ($(HOST),)
host:=$(HOST)
host_toolchain:=$(HOST)-
endif
ifneq ($(DEBUG),)
@ -93,7 +96,7 @@ qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch
wallet_packages_$(NO_WALLET) = $(wallet_packages)
upnp_packages_$(NO_UPNP) = $(upnp_packages)
packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_)
packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(rust_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_)
native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages)
ifneq ($(qt_packages_),)
@ -102,7 +105,7 @@ endif
all_packages = $(packages) $(native_packages)
meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk
meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk cargo-checksum.sh
$(host_arch)_$(host_os)_native_toolchain?=$($(host_os)_native_toolchain)

13
depends/cargo-checksum.sh Executable file
View File

@ -0,0 +1,13 @@
echo "{\"files\":{$(
find . -type f | # Get list of file paths
grep -v $1 | # Exclude Makefile hashes
grep -v '[.]stamp_' | # Exclude Makefile stamps
sed 's|^[.]/||' | # Remove leading ./
sort | # Sort (for uniformity)
xargs $2 | # Get SHA256 hashes (assumes standard 'H(A) A' format)
awk -v OFS='":"' '{print $2, $1}' | # 'H(A) A' -> 'A":"H(A)'
sed 's|^|"|' | # 'A":"H(A)' -> '"A":"H(A)'
sed 's|$|"|' | # '"A":"H(A)' -> '"A":"H(A)"'
tr '\n' ',' | # Concatenate lines with commas
sed 's|,$||' # Remove any trailing comma (to fit JSON spec)
)},\"package\":\"$3\"}" > .cargo-checksum.json

View File

@ -31,9 +31,22 @@ endef
define fetch_file
( test -f $$($(1)_source_dir)/$(4) || \
( $(call fetch_file_inner,$(1),$(2),$(3),$(4),$(5)) || \
$(call fetch_file_inner,$(1),$(PRIORITY_DOWNLOAD_PATH),$(3),$(4),$(5)) || \
$(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(3),$(4),$(5))))
endef
define generate_crate_checksum
rm .gitignore && \
$(BASEDIR)/cargo-checksum.sh "$($(1)_file_name)" "$(build_SHA256SUM)" "$($(1)_sha256_hash)"
endef
define vendor_crate_source
mkdir -p $($(1)_staging_prefix_dir)/$(CRATE_REGISTRY) && \
cp -r $($(1)_extract_dir) $($(1)_staging_prefix_dir)/$(CRATE_REGISTRY)/$($(1)_crate_name) && \
cd $($(1)_staging_prefix_dir)/$(CRATE_REGISTRY)/$($(1)_crate_versioned_name) && \
rm -r `basename $($(1)_patch_dir)` .stamp_* .$($(1)_file_name).hash
endef
define int_get_build_recipe_hash
$(eval $(1)_all_file_checksums:=$(shell $(build_SHA256SUM) $(meta_depends) packages/$(1).mk $(addprefix $(PATCHES_PATH)/$(1)/,$($(1)_patches)) | cut -d" " -f1))
$(eval $(1)_recipe_hash:=$(shell echo -n "$($(1)_all_file_checksums)" | $(build_SHA256SUM) | cut -d" " -f1))
@ -47,6 +60,10 @@ $(eval $(1)_build_id_long:=$(1)-$($(1)_version)-$($(1)_recipe_hash)-$(release_ty
$(eval $(1)_build_id:=$(shell echo -n "$($(1)_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH)))
final_build_id_long+=$($(package)_build_id_long)
#override platform specific files and hashes
$(eval $(1)_file_name=$(if $($(1)_file_name_$(host_os)),$($(1)_file_name_$(host_os)),$($(1)_file_name)))
$(eval $(1)_sha256_hash=$(if $($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash)))
#compute package-specific paths
$(1)_build_subdir?=.
$(1)_download_file?=$($(1)_file_name)

View File

@ -1,8 +1,8 @@
package=bdb
$(package)_version=4.8.30
$(package)_version=6.2.23
$(package)_download_path=http://download.oracle.com/berkeley-db
$(package)_file_name=db-$($(package)_version).NC.tar.gz
$(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef
$(package)_file_name=db-$($(package)_version).tar.gz
$(package)_sha256_hash=47612c8991aa9ac2f6be721267c8d3cdccf5ac83105df8e50809daea24e95dc7
$(package)_build_subdir=build_unix
define $(package)_set_vars
@ -13,9 +13,8 @@ $(package)_cxxflags=-std=c++11
endef
define $(package)_preprocess_cmds
sed -i.old 's/__atomic_compare_exchange/__atomic_compare_exchange_db/' dbinc/atomic.h && \
sed -i.old 's/atomic_init/atomic_init_db/' dbinc/atomic.h mp/mp_region.c mp/mp_mvcc.c mp/mp_fget.c mutex/mut_method.c mutex/mut_tas.c && \
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist
sed -i.old 's/__atomic_compare_exchange\\(/__atomic_compare_exchange_db(/' src/dbinc/atomic.h && \
sed -i.old 's/atomic_init/atomic_init_db/' src/dbinc/atomic.h src/mp/mp_region.c src/mp/mp_mvcc.c src/mp/mp_fget.c src/mutex/mut_method.c src/mutex/mut_tas.c
endef
define $(package)_config_cmds
@ -23,7 +22,7 @@ define $(package)_config_cmds
endef
define $(package)_build_cmds
$(MAKE) libdb_cxx-4.8.a libdb-4.8.a
$(MAKE) libdb_cxx-6.2.a libdb-6.2.a
endef
define $(package)_stage_cmds

View File

@ -0,0 +1,15 @@
package=crate_libc
$(package)_crate_name=libc
$(package)_version=0.2.21
$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name)
$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate
$(package)_sha256_hash=88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135
$(package)_crate_versioned_name=$($(package)_crate_name)
define $(package)_preprocess_cmds
$(call generate_crate_checksum,$(package))
endef
define $(package)_stage_cmds
$(call vendor_crate_source,$(package))
endef

View File

@ -0,0 +1,19 @@
package=googletest
$(package)_version=1.8.0
$(package)_download_path=https://github.com/google/$(package)/archive/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_download_file=release-$($(package)_version).tar.gz
$(package)_sha256_hash=58a6f4277ca2bc8565222b3bbd58a177609e9c488e8a72649359ba51450db7d8
define $(package)_build_cmds
$(MAKE) -C googlemock/make CXXFLAGS=-fPIC gmock.a && \
$(MAKE) -C googletest/make CXXFLAGS=-fPIC gtest.a
endef
define $(package)_stage_cmds
mkdir -p $($(package)_staging_dir)$(host_prefix)/lib && \
install ./googlemock/make/gmock.a $($(package)_staging_dir)$(host_prefix)/lib/libgmock.a && \
install ./googletest/make/gtest.a $($(package)_staging_dir)$(host_prefix)/lib/libgtest.a && \
cp -a ./googlemock/include $($(package)_staging_dir)$(host_prefix)/ && \
cp -a ./googletest/include $($(package)_staging_dir)$(host_prefix)/
endef

View File

@ -0,0 +1,19 @@
package=libgmp
$(package)_version=6.1.1
$(package)_download_path=https://gmplib.org/download/gmp/
$(package)_file_name=gmp-$($(package)_version).tar.bz2
$(package)_sha256_hash=a8109865f2893f1373b0a8ed5ff7429de8db696fc451b1036bd7bdf95bbeffd6
$(package)_dependencies=
$(package)_config_opts=--enable-cxx --disable-shared
define $(package)_config_cmds
$($(package)_autoconf) --host=$(host) --build=$(build)
endef
define $(package)_build_cmds
$(MAKE) CPPFLAGS='-fPIC'
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install ; echo '=== staging find for $(package):' ; find $($(package)_staging_dir)
endef

View File

@ -0,0 +1,25 @@
package=librustzcash
$(package)_version=0.1
$(package)_download_path=https://github.com/zcash/$(package)/archive/
$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz
$(package)_download_file=$($(package)_git_commit).tar.gz
$(package)_sha256_hash=a5760a90d4a1045c8944204f29fa2a3cf2f800afee400f88bf89bbfe2cce1279
$(package)_git_commit=91348647a86201a9482ad4ad68398152dc3d635e
$(package)_dependencies=rust $(rust_crates)
$(package)_patches=cargo.config
define $(package)_preprocess_cmds
mkdir .cargo && \
cat $($(package)_patch_dir)/cargo.config | sed 's|CRATE_REGISTRY|$(host_prefix)/$(CRATE_REGISTRY)|' > .cargo/config
endef
define $(package)_build_cmds
cargo build --frozen --release
endef
define $(package)_stage_cmds
mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \
mkdir $($(package)_staging_dir)$(host_prefix)/include/ && \
cp target/release/librustzcash.a $($(package)_staging_dir)$(host_prefix)/lib/ && \
cp include/librustzcash.h $($(package)_staging_dir)$(host_prefix)/include/
endef

View File

@ -0,0 +1,23 @@
package=libsodium
$(package)_version=1.0.15
$(package)_download_path=https://download.libsodium.org/libsodium/releases/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4
$(package)_dependencies=
$(package)_config_opts=
define $(package)_preprocess_cmds
cd $($(package)_build_subdir); ./autogen.sh
endef
define $(package)_config_cmds
$($(package)_autoconf) --enable-static --disable-shared
endef
define $(package)_build_cmds
$(MAKE)
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef

View File

@ -0,0 +1,25 @@
package=native_ccache
$(package)_version=3.3.1
$(package)_download_path=https://www.samba.org/ftp/ccache
$(package)_file_name=ccache-$($(package)_version).tar.bz2
$(package)_sha256_hash=cb6e4bafbb19ba0a2ec43386b123a5f92a20e1e3384c071d5d13e0cb3c84bf73
define $(package)_set_vars
$(package)_config_opts=
endef
define $(package)_config_cmds
$($(package)_autoconf)
endef
define $(package)_build_cmds
$(MAKE)
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef
define $(package)_postprocess_cmds
rm -rf lib include
endef

View File

@ -1,4 +1,10 @@
packages:=boost openssl libevent zeromq
rust_crates := crate_libc
rust_packages := rust $(rust_crates) librustzcash
proton_packages := proton
zcash_packages := libgmp libsodium
native_packages := native_ccache
packages:=boost openssl libevent zeromq $(zcash_packages) googletest
qt_native_packages = native_protobuf
qt_packages = qrencode protobuf zlib

14
depends/packages/rust.mk Normal file
View File

@ -0,0 +1,14 @@
package=rust
$(package)_version=1.16.0
$(package)_download_path=https://static.rust-lang.org/dist
$(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz
$(package)_sha256_hash_linux=48621912c242753ba37cad5145df375eeba41c81079df46f93ffb4896542e8fd
$(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz
$(package)_sha256_hash_darwin=2d08259ee038d3a2c77a93f1a31fc59e7a1d6d1bbfcba3dba3c8213b2e5d1926
$(package)_file_name_mingw32=rust-mingw-$($(package)_version)-x86_64-pc-windows-gnu.tar.gz
$(package)_sha256_hash_mingw32=4975e1f8eade1afca5dd17e71659f665f3bcc645733a545179c887543da1e27c
define $(package)_stage_cmds
./install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig
endef

View File

@ -0,0 +1,5 @@
[source.crates-io]
replace-with = "vendored-sources"
[source.vendored-sources]
directory = "CRATE_REGISTRY"

107
scripts/build.sh Executable file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -eu -o pipefail
function cmd_pref() {
if type -p "$2" > /dev/null; then
eval "$1=$2"
else
eval "$1=$3"
fi
}
# If a g-prefixed version of the command exists, use it preferentially.
function gprefix() {
cmd_pref "$1" "g$2" "$2"
}
gprefix READLINK readlink
cd "$(dirname "$("$READLINK" -f "$0")")/.."
# Allow user overrides to $MAKE. Typical usage for users who need it:
# MAKE=gmake ./zcutil/build.sh -j$(nproc)
if [[ -z "${MAKE-}" ]]; then
MAKE=make
fi
# Allow overrides to $BUILD and $HOST for porters. Most users will not need it.
# BUILD=i686-pc-linux-gnu ./zcutil/build.sh
if [[ -z "${BUILD-}" ]]; then
BUILD="$(./depends/config.guess)"
fi
if [[ -z "${HOST-}" ]]; then
HOST="$BUILD"
fi
# Allow override to $CC and $CXX for porters. Most users will not need it.
if [[ -z "${CC-}" ]]; then
CC=gcc
fi
if [[ -z "${CXX-}" ]]; then
CXX=g++
fi
# Allow users to set arbitary compile flags. Most users will not need this.
if [[ -z "${CONFIGURE_FLAGS-}" ]]; then
CONFIGURE_FLAGS=""
fi
if [ "x$*" = 'x--help' ]
then
cat <<EOF
Usage:
$0 --help
Show this help message and exit.
$0 [ --enable-lcov || --disable-tests ] [ --disable-mining ] [ MAKEARGS... ]
Build Zcash and most of its transitive dependencies from
source. MAKEARGS are applied to both dependencies and Zcash itself.
If --enable-lcov is passed, Zcash is configured to add coverage
instrumentation, thus enabling "make cov" to work.
If --disable-tests is passed instead, the Zcash tests are not built.
If --disable-mining is passed, Zcash is configured to not build any mining
code. It must be passed after the test arguments, if present.
EOF
exit 0
fi
set -x
# If --enable-lcov is the first argument, enable lcov coverage support:
LCOV_ARG=''
HARDENING_ARG='--enable-hardening'
TEST_ARG=''
if [ "x${1:-}" = 'x--enable-lcov' ]
then
LCOV_ARG='--enable-lcov'
HARDENING_ARG='--disable-hardening'
shift
elif [ "x${1:-}" = 'x--disable-tests' ]
then
TEST_ARG='--enable-tests=no'
shift
fi
# If --disable-mining is the next argument, disable mining code:
MINING_ARG=''
if [ "x${1:-}" = 'x--disable-mining' ]
then
MINING_ARG='--enable-mining=no'
shift
fi
PREFIX="$(pwd)/depends/$BUILD/"
eval "$MAKE" --version
eval "$CC" --version
eval "$CXX" --version
as --version
ld -v
HOST="$HOST" BUILD="$BUILD" "$MAKE" "$@" -C ./depends/ V=1
./autogen.sh
CC="$CC" CXX="$CXX" ./configure --disable-wallet --prefix="${PREFIX}" --host="$HOST" --build="$BUILD" "$HARDENING_ARG" "$LCOV_ARG" "$TEST_ARG" "$MINING_ARG" $CONFIGURE_FLAGS --enable-werror CXXFLAGS='-g'
"$MAKE" "$@" V=1

View File

@ -22,6 +22,8 @@ endif
BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
BITCOIN_INCLUDES += -I$(srcdir)/snark
BITCOIN_INCLUDES += -I$(srcdir)/snark/libsnark
BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS)
LIBBITCOIN_SERVER=libbitcoin_server.a
@ -32,6 +34,8 @@ LIBBITCOIN_UTIL=libbitcoin_util.a
LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a
LIBBITCOINQT=qt/libbitcoinqt.a
LIBSECP256K1=secp256k1/libsecp256k1.la
LIBSNARK=snark/libsnark.a
LIBZCASH=libzcash.a
if ENABLE_ZMQ
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
@ -46,6 +50,18 @@ endif
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
LIBSNARK_CXXFLAGS = -fPIC -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1 -fstack-protector-all
LIBSNARK_CONFIG_FLAGS = CURVE=ALT_BN128 NO_PROCPS=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT NO_COPY_DEPINST=1 NO_COMPILE_LIBGTEST=1
if HAVE_OPENMP
LIBSNARK_CONFIG_FLAGS += MULTICORE=1
endif
$(LIBSNARK): $(wildcard snark/src/*)
$(AM_V_at) CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64"
libsnark-tests: $(wildcard snark/src/*)
$(AM_V_at) CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64"
# Make is not made aware of per-object dependencies to avoid limiting building parallelization
# But to build the less dependent modules first, we manually select their order here:
EXTRA_LIBRARIES += \
@ -56,7 +72,8 @@ EXTRA_LIBRARIES += \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_CLI) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_ZMQ)
$(LIBBITCOIN_ZMQ) \
$(LIBZCASH)
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
@ -73,11 +90,24 @@ if BUILD_BITCOIN_UTILS
bin_PROGRAMS += bitcoin-cli bitcoin-tx
endif
.PHONY: FORCE check-symbols check-security
LIBZCASH_H = \
zcash/IncrementalMerkleTree.hpp \
zcash/NoteEncryption.hpp \
zcash/Address.hpp \
zcash/JoinSplit.hpp \
zcash/Note.hpp \
zcash/prf.h \
zcash/Proof.hpp \
zcash/util.h \
zcash/Zcash.h
.PHONY: FORCE collate-libsnark check-symbols check-security
# bitcoin core #
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
asyncrpcoperation.h \
asyncrpcqueue.h \
base58.h \
bech32.h \
bloom.h \
@ -124,6 +154,8 @@ BITCOIN_CORE_H = \
netbase.h \
netmessagemaker.h \
noui.h \
paymentdisclosure.h \
paymentdisclosuredb.h \
policy/feerate.h \
policy/fees.h \
policy/policy.h \
@ -168,6 +200,9 @@ BITCOIN_CORE_H = \
validationinterface.h \
versionbits.h \
walletinitinterface.h \
wallet/asyncrpcoperation_mergetoaddress.h \
wallet/asyncrpcoperation_sendmany.h \
wallet/asyncrpcoperation_shieldcoinbase.h \
wallet/coincontrol.h \
wallet/crypter.h \
wallet/db.h \
@ -182,7 +217,8 @@ BITCOIN_CORE_H = \
zmq/zmqabstractnotifier.h \
zmq/zmqconfig.h\
zmq/zmqnotificationinterface.h \
zmq/zmqpublishnotifier.h
zmq/zmqpublishnotifier.h \
$(LIBZCASH_H)
obj/build.h: FORCE
@ -197,6 +233,8 @@ libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
addrman.cpp \
asyncrpcoperation.cpp \
asyncrpcqueue.cpp \
bloom.cpp \
blockencodings.cpp \
chain.cpp \
@ -212,6 +250,8 @@ libbitcoin_server_a_SOURCES = \
net.cpp \
net_processing.cpp \
noui.cpp \
paymentdisclosure.cpp \
paymentdisclosuredb.cpp \
policy/fees.cpp \
policy/policy.cpp \
policy/rbf.cpp \
@ -250,11 +290,17 @@ libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
interfaces/wallet.cpp \
wallet/asyncrpcoperation_mergetoaddress.cpp \
wallet/asyncrpcoperation_sendmany.cpp \
wallet/asyncrpcoperation_shieldcoinbase.cpp \
wallet/crypter.cpp \
wallet/db.cpp \
wallet/feebumper.cpp \
wallet/fees.cpp \
wallet/init.cpp \
paymentdisclosure.cpp \
paymentdisclosuredb.cpp \
wallet/rpcdisclosure.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
wallet/wallet.cpp \
@ -272,6 +318,9 @@ crypto_libbitcoin_crypto_a_SOURCES = \
crypto/chacha20.h \
crypto/chacha20.cpp \
crypto/common.h \
crypto/equihash.cpp \
crypto/equihash.h \
crypto/equihash.tcc \
crypto/hmac_sha256.cpp \
crypto/hmac_sha256.h \
crypto/hmac_sha512.cpp \
@ -289,6 +338,18 @@ if USE_ASM
crypto_libbitcoin_crypto_a_SOURCES += crypto/sha256_sse4.cpp
endif
if ENABLE_MINING
EQUIHASH_TROMP_SOURCES = \
pow/tromp/equi_miner.h \
pow/tromp/equi.h \
pow/tromp/osx_barrier.h
crypto_libbitcoin_crypto_a_CPPFLAGS += \
-DEQUIHASH_TROMP_ATOMIC
crypto_libbitcoin_crypto_a_SOURCES += \
${EQUIHASH_TROMP_SOURCES}
endif
# consensus: shared between all executables that validate any consensus rules.
libbitcoin_consensus_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_consensus_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
@ -321,6 +382,7 @@ libbitcoin_consensus_a_SOURCES = \
tinyformat.h \
uint256.cpp \
uint256.h \
uint252.h \
utilstrencodings.cpp \
utilstrencodings.h \
version.h
@ -411,6 +473,9 @@ bitcoind_LDADD = \
$(LIBBITCOIN_ZMQ) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH) \
$(LIBSNARK) \
$(LIBZCASH_LIBS) \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
@ -432,7 +497,10 @@ bitcoin_cli_LDADD = \
$(LIBBITCOIN_CLI) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO)
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH) \
$(LIBSNARK) \
$(LIBZCASH_LIBS)
bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS)
#
@ -453,11 +521,39 @@ bitcoin_tx_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBSECP256K1)
$(LIBSECP256K1) \
$(LIBZCASH) \
$(LIBSNARK) \
$(LIBZCASH_LIBS)
bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
#
# zcash protocol primitives #
libzcash_a_SOURCES = \
zcash/IncrementalMerkleTree.cpp \
zcash/NoteEncryption.cpp \
zcash/Address.cpp \
zcash/JoinSplit.cpp \
zcash/Proof.cpp \
zcash/Note.cpp \
zcash/prf.cpp \
zcash/util.cpp \
zcash/circuit/commitment.tcc \
zcash/circuit/gadget.tcc \
zcash/circuit/merkle.tcc \
zcash/circuit/note.tcc \
zcash/circuit/prfs.tcc \
zcash/circuit/utils.tcc
libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) $(HARDENED_CXXFLAGS) $(HARDENED_LDFLAGS) -pipe $(SAN_LDFLAGS) -O1 -g -Wstack-protector $(SAN_CXXFLAGS) -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES)
libzcash_a_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing
libzcash_a_LIBADD = $(SAN_LDFLAGS) $(HARDENED_LDFLAGS)
libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT
# bitcoinconsensus library #
if BUILD_BITCOIN_LIBS
include_HEADERS = script/bitcoinconsensus.h
@ -509,6 +605,7 @@ $(top_srcdir)/$(subdir)/config/bitcoin-config.h.in: $(am__configure_deps)
clean-local:
-$(MAKE) -C secp256k1 clean
-$(MAKE) -C snark clean
-$(MAKE) -C univalue clean
-rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno
-rm -f config.h
@ -541,6 +638,7 @@ endif
if ENABLE_TESTS
include Makefile.test.include
include Makefile.gtest.include
endif
if ENABLE_BENCH
@ -554,3 +652,5 @@ endif
if ENABLE_QT_TESTS
include Makefile.qttest.include
endif
include Makefile.zcash.include

View File

@ -52,7 +52,7 @@ if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
endif
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(LIBZCASH) $(LIBSNARK) $(LIBZCASH_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)

View File

@ -0,0 +1,69 @@
TESTS += zcash-gtest
noinst_PROGRAMS += zcash-gtest
# tool for generating our public parameters.
# test_checktransaction.cpp MUST be before
# any test that calls SelectParams().
zcash_gtest_SOURCES = \
gtest/main.cpp \
gtest/utils.cpp \
gtest/test_checktransaction.cpp \
gtest/json_test_vectors.cpp \
gtest/json_test_vectors.h \
gtest/test_foundersreward.cpp
# These tests are order-dependent, because they
# depend on global state (see #1539)
if ENABLE_WALLET
zcash_gtest_SOURCES += \
wallet/gtest/test_wallet_zkeys.cpp
endif
zcash_gtest_SOURCES += \
gtest/test_tautology.cpp \
gtest/test_deprecation.cpp \
gtest/test_equihash.cpp \
gtest/test_httprpc.cpp \
gtest/test_joinsplit.cpp \
gtest/test_keystore.cpp \
gtest/test_noteencryption.cpp \
gtest/test_mempool.cpp \
gtest/test_merkletree.cpp \
gtest/test_metrics.cpp \
gtest/test_miner.cpp \
gtest/test_pow.cpp \
gtest/test_random.cpp \
gtest/test_rpc.cpp \
gtest/test_transaction.cpp \
gtest/test_upgrades.cpp \
gtest/test_validation.cpp \
gtest/test_circuit.cpp \
gtest/test_txid.cpp \
gtest/test_libzcash_utils.cpp \
gtest/test_proofs.cpp \
gtest/test_paymentdisclosure.cpp \
gtest/test_checkblock.cpp
if ENABLE_WALLET
zcash_gtest_SOURCES += \
wallet/gtest/test_wallet.cpp
endif
zcash_gtest_CPPFLAGS = $(AM_CPPFLAGS) -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES)
zcash_gtest_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
zcash_gtest_LDADD = -lgtest -lgmock $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)
if ENABLE_ZMQ
zcash_gtest_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
if ENABLE_WALLET
zcash_gtest_LDADD += $(LIBBITCOIN_WALLET)
endif
zcash_gtest_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(LIBZCASH) $(LIBSNARK) $(LIBZCASH_LIBS)
zcash_gtest_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
zcash-gtest_check: zcash-gtest FORCE
./zcash-gtest
zcash-gtest-expected-failures: zcash-gtest FORCE
./zcash-gtest --gtest_filter=*DISABLED_* --gtest_also_run_disabled_tests

View File

@ -357,7 +357,7 @@ if ENABLE_WALLET
BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_CPP)
endif
RES_IMAGES =
RES_IMAGES =
RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png)
@ -410,7 +410,7 @@ qt_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(LIBZCASH) $(LIBSNARK) $(LIBZCASH_LIBS)
qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_bitcoin_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX

View File

@ -65,7 +65,7 @@ endif
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
$(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(LIBZCASH) $(LIBSNARK) $(LIBZCASH_LIBS)
qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)

View File

@ -47,6 +47,7 @@ BITCOIN_TESTS =\
test/crypto_tests.cpp \
test/cuckoocache_tests.cpp \
test/DoS_tests.cpp \
test/equihash_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
test/key_io_tests.cpp \
@ -113,7 +114,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH) $(LIBSNARK) $(LIBZCASH_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ

View File

@ -0,0 +1,28 @@
noinst_PROGRAMS += \
zcash/GenerateParams \
zcash/CreateJoinSplit
# tool for generating our public parameters
zcash_GenerateParams_SOURCES = zcash/GenerateParams.cpp
zcash_GenerateParams_CPPFLAGS = $(AM_CPPFLAGS)
zcash_GenerateParams_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
zcash_GenerateParams_LDADD = \
$(BOOST_LIBS) \
$(LIBZCASH) \
$(LIBSNARK) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH_LIBS)
# tool for profiling the creation of joinsplits
zcash_CreateJoinSplit_SOURCES = zcash/CreateJoinSplit.cpp
zcash_CreateJoinSplit_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
zcash_CreateJoinSplit_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
zcash_CreateJoinSplit_LDADD = \
$(LIBBITCOIN_COMMON) \
$(LIBZCASH) \
$(LIBSNARK) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(BOOST_LIBS) \
$(LIBZCASH_LIBS)

View File

@ -0,0 +1,14 @@
#include "json_test_vectors.h"
UniValue
read_json(const std::string& jsondata)
{
UniValue v;
if (!(v.read(jsondata) && v.isArray()))
{
ADD_FAILURE();
return UniValue(UniValue::VARR);
}
return v.get_array();
}

View File

@ -0,0 +1,51 @@
#include <gtest/gtest.h>
#include "utilstrencodings.h"
#include "version.h"
#include "serialize.h"
#include "streams.h"
#include <univalue.h>
UniValue
read_json(const std::string& jsondata);
// #define PRINT_JSON 1
template<typename T>
void expect_deser_same(const T& expected)
{
CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION);
ss1 << expected;
auto serialized_size = ss1.size();
T object;
ss1 >> object;
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
ss2 << object;
ASSERT_TRUE(serialized_size == ss2.size());
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), serialized_size) == 0);
}
template<typename T, typename U>
void expect_test_vector(T& v, const U& expected)
{
expect_deser_same(expected);
CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION);
ss1 << expected;
#ifdef PRINT_JSON
std::cout << "\t\"" ;
std::cout << HexStr(ss1.begin(), ss1.end()) << "\",\n";
#else
std::string raw = v.get_str();
CDataStream ss2(ParseHex(raw), SER_NETWORK, PROTOCOL_VERSION);
ASSERT_TRUE(ss1.size() == ss2.size());
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), ss1.size()) == 0);
#endif
}

30
src/gtest/main.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "gmock/gmock.h"
#include "crypto/common.h"
#include "pubkey.h"
#include "zcash/JoinSplit.hpp"
#include "util.h"
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>
struct ECCryptoClosure
{
ECCVerifyHandle handle;
};
ECCryptoClosure instance_of_eccryptoclosure;
ZCJoinSplit* params;
int main(int argc, char **argv) {
assert(init_and_check_sodium() != -1);
libsnark::default_r1cs_ppzksnark_pp::init_public_params();
libsnark::inhibit_profiling_info = true;
libsnark::inhibit_profiling_counters = true;
boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key";
boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key";
params = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string());
testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}

13
src/gtest/test_block.cpp Normal file
View File

@ -0,0 +1,13 @@
#include <gtest/gtest.h>
#include "primitives/block.h"
TEST(block_tests, header_size_is_expected) {
// Dummy header with an empty Equihash solution.
CBlockHeader header;
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << header;
ASSERT_EQ(ss.size(), CBlockHeader::HEADER_SIZE);
}

View File

@ -0,0 +1,233 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "consensus/validation.h"
#include "main.h"
#include "zcash/Proof.hpp"
class MockCValidationState : public CValidationState {
public:
MOCK_METHOD5(DoS, bool(int level, bool ret,
unsigned char chRejectCodeIn, std::string strRejectReasonIn,
bool corruptionIn));
MOCK_METHOD3(Invalid, bool(bool ret,
unsigned char _chRejectCode, std::string _strRejectReason));
MOCK_METHOD1(Error, bool(std::string strRejectReasonIn));
MOCK_CONST_METHOD0(IsValid, bool());
MOCK_CONST_METHOD0(IsInvalid, bool());
MOCK_CONST_METHOD0(IsError, bool());
MOCK_CONST_METHOD1(IsInvalid, bool(int &nDoSOut));
MOCK_CONST_METHOD0(CorruptionPossible, bool());
MOCK_CONST_METHOD0(GetRejectCode, unsigned char());
MOCK_CONST_METHOD0(GetRejectReason, std::string());
};
TEST(CheckBlock, VersionTooLow) {
auto verifier = libzcash::ProofVerifier::Strict();
CBlock block;
block.nVersion = 1;
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "version-too-low", false)).Times(1);
EXPECT_FALSE(CheckBlock(block, state, verifier, false, false));
}
// Test that a Sprout tx with negative version is still rejected
// by CheckBlock under Sprout consensus rules.
TEST(CheckBlock, BlockSproutRejectsBadVersion) {
SelectParams(CBaseChainParams::MAIN);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
mtx.fOverwintered = false;
mtx.nVersion = -1;
mtx.nVersionGroupId = 0;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
auto verifier = libzcash::ProofVerifier::Strict();
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1);
EXPECT_FALSE(CheckBlock(block, state, verifier, false, false));
}
TEST(ContextualCheckBlock, BadCoinbaseHeight) {
SelectParams(CBaseChainParams::MAIN);
// Create a block with no height in scriptSig
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
// Treating block as genesis should pass
MockCValidationState state;
EXPECT_TRUE(ContextualCheckBlock(block, state, NULL));
// Treating block as non-genesis should fail
mtx.vout.push_back(CTxOut(GetBlockSubsidy(1, Params().GetConsensus())/5, Params().GetFoundersRewardScriptAtHeight(1)));
CTransaction tx2 {mtx};
block.vtx[0] = tx2;
CBlock prev;
CBlockIndex indexPrev {prev};
indexPrev.nHeight = 0;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-height", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
// Setting to an incorrect height should fail
mtx.vin[0].scriptSig = CScript() << 2 << OP_0;
CTransaction tx3 {mtx};
block.vtx[0] = tx3;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-height", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
// After correcting the scriptSig, should pass
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
CTransaction tx4 {mtx};
block.vtx[0] = tx4;
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
}
// Test that a block evaluated under Sprout rules cannot contain Overwinter transactions.
// This test assumes that mainnet Overwinter activation is at least height 2.
TEST(ContextualCheckBlock, BlockSproutRulesRejectOverwinterTx) {
SelectParams(CBaseChainParams::MAIN);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
}
// Test block evaluated under Sprout rules will accept Sprout transactions
TEST(ContextualCheckBlock, BlockSproutRulesAcceptSproutTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
mtx.fOverwintered = false;
mtx.nVersion = 1;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Test block evaluated under Overwinter rules will accept Overwinter transactions
TEST(ContextualCheckBlock, BlockOverwinterRulesAcceptOverwinterTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Test block evaluated under Overwinter rules will reject Sprout transactions
TEST(ContextualCheckBlock, BlockOverwinterRulesRejectSproutTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.nVersion = 2;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-active", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}

View File

@ -0,0 +1,774 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <sodium.h>
#include "main.h"
#include "primitives/transaction.h"
#include "consensus/validation.h"
TEST(checktransaction_tests, check_vpub_not_both_nonzero) {
CMutableTransaction tx;
tx.nVersion = 2;
{
// Ensure that values within the joinsplit are well-formed.
CMutableTransaction newTx(tx);
CValidationState state;
newTx.vjoinsplit.push_back(JSDescription());
JSDescription *jsdesc = &newTx.vjoinsplit[0];
jsdesc->vpub_old = 1;
jsdesc->vpub_new = 1;
EXPECT_FALSE(CheckTransactionWithoutProofVerification(newTx, state));
EXPECT_EQ(state.GetRejectReason(), "bad-txns-vpubs-both-nonzero");
}
}
class MockCValidationState : public CValidationState {
public:
MOCK_METHOD5(DoS, bool(int level, bool ret,
unsigned char chRejectCodeIn, std::string strRejectReasonIn,
bool corruptionIn));
MOCK_METHOD3(Invalid, bool(bool ret,
unsigned char _chRejectCode, std::string _strRejectReason));
MOCK_METHOD1(Error, bool(std::string strRejectReasonIn));
MOCK_CONST_METHOD0(IsValid, bool());
MOCK_CONST_METHOD0(IsInvalid, bool());
MOCK_CONST_METHOD0(IsError, bool());
MOCK_CONST_METHOD1(IsInvalid, bool(int &nDoSOut));
MOCK_CONST_METHOD0(CorruptionPossible, bool());
MOCK_CONST_METHOD0(GetRejectCode, unsigned char());
MOCK_CONST_METHOD0(GetRejectReason, std::string());
};
CMutableTransaction GetValidTransaction() {
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
CMutableTransaction mtx;
mtx.vin.resize(2);
mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
mtx.vin[0].prevout.n = 0;
mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
mtx.vin[1].prevout.n = 0;
mtx.vout.resize(2);
// mtx.vout[0].scriptPubKey =
mtx.vout[0].nValue = 0;
mtx.vout[1].nValue = 0;
mtx.vjoinsplit.resize(2);
mtx.vjoinsplit[0].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
mtx.vjoinsplit[0].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
mtx.vjoinsplit[1].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
mtx.vjoinsplit[1].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000003");
// Generate an ephemeral keypair.
uint256 joinSplitPubKey;
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey);
mtx.joinSplitPubKey = joinSplitPubKey;
// Compute the correct hSig.
// TODO: #966.
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
// Empty output script.
CScript scriptCode;
CTransaction signTx(mtx);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
if (dataToBeSigned == one) {
throw std::runtime_error("SignatureHash failed");
}
// Add the signature
assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
dataToBeSigned.begin(), 32,
joinSplitPrivKey
) == 0);
return mtx;
}
TEST(checktransaction_tests, valid_transaction) {
CMutableTransaction mtx = GetValidTransaction();
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
}
TEST(checktransaction_tests, BadVersionTooLow) {
CMutableTransaction mtx = GetValidTransaction();
mtx.nVersion = 0;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_vin_empty) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.vin.resize(0);
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_vout_empty) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.vout.resize(0);
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_oversize) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vin[0].scriptSig = CScript();
std::vector<unsigned char> vchData(520);
for (unsigned int i = 0; i < 190; ++i)
mtx.vin[0].scriptSig << vchData << OP_DROP;
mtx.vin[0].scriptSig << OP_1;
{
// Transaction is just under the limit...
CTransaction tx(mtx);
CValidationState state;
ASSERT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
}
// Not anymore!
mtx.vin[1].scriptSig << vchData << OP_DROP;
mtx.vin[1].scriptSig << OP_1;
{
CTransaction tx(mtx);
ASSERT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), 100202);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
}
TEST(checktransaction_tests, bad_txns_vout_negative) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vout[0].nValue = -1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_vout_toolarge) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vout[0].nValue = MAX_MONEY + 1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_outputs) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vout[0].nValue = MAX_MONEY;
mtx.vout[1].nValue = 1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_joinsplit) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vout[0].nValue = 1;
mtx.vjoinsplit[0].vpub_old = MAX_MONEY;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_txintotal_toolarge_joinsplit) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit[0].vpub_new = MAX_MONEY - 1;
mtx.vjoinsplit[1].vpub_new = MAX_MONEY - 1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txintotal-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_vpub_old_negative) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit[0].vpub_old = -1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_old-negative", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_vpub_new_negative) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit[0].vpub_new = -1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_new-negative", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_vpub_old_toolarge) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit[0].vpub_old = MAX_MONEY + 1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_old-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_vpub_new_toolarge) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit[0].vpub_new = MAX_MONEY + 1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_new-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_vpubs_both_nonzero) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit[0].vpub_old = 1;
mtx.vjoinsplit[0].vpub_new = 1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpubs-both-nonzero", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_inputs_duplicate) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vin[1].prevout.hash = mtx.vin[0].prevout.hash;
mtx.vin[1].prevout.n = mtx.vin[0].prevout.n;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_joinsplits_nullifiers_duplicate_same_joinsplit) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit[0].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
mtx.vjoinsplit[0].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_joinsplits_nullifiers_duplicate_different_joinsplit) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit[0].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
mtx.vjoinsplit[1].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_cb_has_joinsplits) {
CMutableTransaction mtx = GetValidTransaction();
// Make it a coinbase.
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vjoinsplit.resize(1);
CTransaction tx(mtx);
EXPECT_TRUE(tx.IsCoinBase());
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-has-joinsplits", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_cb_empty_scriptsig) {
CMutableTransaction mtx = GetValidTransaction();
// Make it a coinbase.
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vjoinsplit.resize(0);
CTransaction tx(mtx);
EXPECT_TRUE(tx.IsCoinBase());
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-length", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_prevout_null) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vin[1].prevout.SetNull();
CTransaction tx(mtx);
EXPECT_FALSE(tx.IsCoinBase());
MockCValidationState state;
EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) {
SelectParams(CBaseChainParams::REGTEST);
CMutableTransaction mtx = GetValidTransaction();
mtx.joinSplitSig[0] += 1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
ContextualCheckTransaction(tx, state, 0, 100);
}
TEST(checktransaction_tests, non_canonical_ed25519_signature) {
SelectParams(CBaseChainParams::REGTEST);
CMutableTransaction mtx = GetValidTransaction();
// Check that the signature is valid before we add L
{
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_TRUE(ContextualCheckTransaction(tx, state, 0, 100));
}
// Copied from libsodium/crypto_sign/ed25519/ref10/open.c
static const unsigned char L[32] =
{ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 };
// Add L to S, which starts at mtx.joinSplitSig[32].
unsigned int s = 0;
for (size_t i = 0; i < 32; i++) {
s = mtx.joinSplitSig[32 + i] + L[i] + (s >> 8);
mtx.joinSplitSig[32 + i] = s & 0xff;
}
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
ContextualCheckTransaction(tx, state, 0, 100);
}
TEST(checktransaction_tests, OverwinterConstructors) {
CMutableTransaction mtx;
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 20;
// Check constructor with overwinter fields
CTransaction tx(mtx);
EXPECT_EQ(tx.nVersion, mtx.nVersion);
EXPECT_EQ(tx.fOverwintered, mtx.fOverwintered);
EXPECT_EQ(tx.nVersionGroupId, mtx.nVersionGroupId);
EXPECT_EQ(tx.nExpiryHeight, mtx.nExpiryHeight);
// Check constructor of mutable transaction struct
CMutableTransaction mtx2(tx);
EXPECT_EQ(mtx2.nVersion, mtx.nVersion);
EXPECT_EQ(mtx2.fOverwintered, mtx.fOverwintered);
EXPECT_EQ(mtx2.nVersionGroupId, mtx.nVersionGroupId);
EXPECT_EQ(mtx2.nExpiryHeight, mtx.nExpiryHeight);
EXPECT_TRUE(mtx2.GetHash() == mtx.GetHash());
// Check assignment of overwinter fields
CTransaction tx2 = tx;
EXPECT_EQ(tx2.nVersion, mtx.nVersion);
EXPECT_EQ(tx2.fOverwintered, mtx.fOverwintered);
EXPECT_EQ(tx2.nVersionGroupId, mtx.nVersionGroupId);
EXPECT_EQ(tx2.nExpiryHeight, mtx.nExpiryHeight);
EXPECT_TRUE(tx2 == tx);
}
TEST(checktransaction_tests, OverwinterSerialization) {
CMutableTransaction mtx;
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 99;
// Check round-trip serialization and deserialization from mtx to tx.
{
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
ss << mtx;
CTransaction tx;
ss >> tx;
EXPECT_EQ(mtx.nVersion, tx.nVersion);
EXPECT_EQ(mtx.fOverwintered, tx.fOverwintered);
EXPECT_EQ(mtx.nVersionGroupId, tx.nVersionGroupId);
EXPECT_EQ(mtx.nExpiryHeight, tx.nExpiryHeight);
EXPECT_EQ(mtx.GetHash(), CMutableTransaction(tx).GetHash());
EXPECT_EQ(tx.GetHash(), CTransaction(mtx).GetHash());
}
// Also check mtx to mtx
{
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
ss << mtx;
CMutableTransaction mtx2;
ss >> mtx2;
EXPECT_EQ(mtx.nVersion, mtx2.nVersion);
EXPECT_EQ(mtx.fOverwintered, mtx2.fOverwintered);
EXPECT_EQ(mtx.nVersionGroupId, mtx2.nVersionGroupId);
EXPECT_EQ(mtx.nExpiryHeight, mtx2.nExpiryHeight);
EXPECT_EQ(mtx.GetHash(), mtx2.GetHash());
}
// Also check tx to tx
{
CTransaction tx(mtx);
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
ss << tx;
CTransaction tx2;
ss >> tx2;
EXPECT_EQ(tx.nVersion, tx2.nVersion);
EXPECT_EQ(tx.fOverwintered, tx2.fOverwintered);
EXPECT_EQ(tx.nVersionGroupId, tx2.nVersionGroupId);
EXPECT_EQ(tx.nExpiryHeight, tx2.nExpiryHeight);
EXPECT_EQ(mtx.GetHash(), CMutableTransaction(tx).GetHash());
EXPECT_EQ(tx.GetHash(), tx2.GetHash());
}
}
TEST(checktransaction_tests, OverwinterDefaultValues) {
// Check default values (this will fail when defaults change; test should then be updated)
CTransaction tx;
EXPECT_EQ(tx.nVersion, 1);
EXPECT_EQ(tx.fOverwintered, false);
EXPECT_EQ(tx.nVersionGroupId, 0);
EXPECT_EQ(tx.nExpiryHeight, 0);
}
// A valid v3 transaction with no joinsplits
TEST(checktransaction_tests, OverwinterValidTx) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
}
TEST(checktransaction_tests, OverwinterExpiryHeight) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
{
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
}
{
mtx.nExpiryHeight = TX_EXPIRY_HEIGHT_THRESHOLD - 1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
}
{
mtx.nExpiryHeight = TX_EXPIRY_HEIGHT_THRESHOLD;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-expiry-height-too-high", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
{
mtx.nExpiryHeight = std::numeric_limits<uint32_t>::max();
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-expiry-height-too-high", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
}
// Test that a Sprout tx with a negative version number is detected
// given the new Overwinter logic
TEST(checktransaction_tests, SproutTxVersionTooLow) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = false;
mtx.nVersion = -1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
// Subclass of CTransaction which doesn't call UpdateHash when constructing
// from a CMutableTransaction. This enables us to create a CTransaction
// with bad values which normally trigger an exception during construction.
class UNSAFE_CTransaction : public CTransaction {
public:
UNSAFE_CTransaction(const CMutableTransaction &tx) : CTransaction(tx, true) {}
};
// Test bad Overwinter version number in CheckTransactionWithoutProofVerification
TEST(checktransaction_tests, OverwinterVersionNumberLow) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = true;
mtx.nVersion = OVERWINTER_MIN_TX_VERSION - 1;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
UNSAFE_CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-low", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
// Test bad Overwinter version number in ContextualCheckTransaction
TEST(checktransaction_tests, OverwinterVersionNumberHigh) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = true;
mtx.nVersion = OVERWINTER_MAX_TX_VERSION + 1;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
UNSAFE_CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-high", false)).Times(1);
ContextualCheckTransaction(tx, state, 1, 100);
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Test bad Overwinter version group id
TEST(checktransaction_tests, OverwinterBadVersionGroupId) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nExpiryHeight = 0;
mtx.nVersionGroupId = 0x12345678;
UNSAFE_CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-version-group-id", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
// This tests an Overwinter transaction checked against Sprout
TEST(checktransaction_tests, OverwinterNotActive) {
SelectParams(CBaseChainParams::TESTNET);
CMutableTransaction mtx = GetValidTransaction();
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
ContextualCheckTransaction(tx, state, 1, 100);
}
// This tests a transaction without the fOverwintered flag set, against the Overwinter consensus rule set.
TEST(checktransaction_tests, OverwinterFlagNotSet) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
CMutableTransaction mtx = GetValidTransaction();
mtx.fOverwintered = false;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-flag-not-set", false)).Times(1);
ContextualCheckTransaction(tx, state, 1, 100);
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Overwinter (NU0) does not allow soft fork to version 4 Overwintered tx.
TEST(checktransaction_tests, OverwinterInvalidSoftForkVersion) {
CMutableTransaction mtx = GetValidTransaction();
mtx.fOverwintered = true;
mtx.nVersion = 4; // This is not allowed
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
try {
ss << mtx;
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format'";
}
catch(std::ios_base::failure & err) {
EXPECT_THAT(err.what(), testing::HasSubstr(std::string("Unknown transaction format")));
}
catch(...) {
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception";
}
}
// Test CreateNewContextualCMutableTransaction sets default values based on height
TEST(checktransaction_tests, OverwinteredContextualCreateTx) {
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& consensusParams = Params().GetConsensus();
int activationHeight = 5;
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, activationHeight);
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
consensusParams, activationHeight - 1);
EXPECT_EQ(mtx.nVersion, 1);
EXPECT_EQ(mtx.fOverwintered, false);
EXPECT_EQ(mtx.nVersionGroupId, 0);
EXPECT_EQ(mtx.nExpiryHeight, 0);
}
// Overwinter activates
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
consensusParams, activationHeight );
EXPECT_EQ(mtx.nVersion, 3);
EXPECT_EQ(mtx.fOverwintered, true);
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
EXPECT_EQ(mtx.nExpiryHeight, 0);
}
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Test a v1 transaction which has a malformed header, perhaps modified in-flight
TEST(checktransaction_tests, BadTxReceivedOverNetwork)
{
// First four bytes <01 00 00 00> have been modified to be <FC FF FF FF> (-4 as an int32)
std::string goodPrefix = "01000000";
std::string badPrefix = "fcffffff";
std::string hexTx = "0176c6541939b95f8d8b7779a77a0863b2a0267e281a050148326f0ea07c3608fb000000006a47304402207c68117a6263486281af0cc5d3bee6db565b6dce19ffacc4cb361906eece82f8022007f604382dee2c1fde41c4e6e7c1ae36cfa28b5b27350c4bfaa27f555529eace01210307ff9bef60f2ac4ceb1169a9f7d2c773d6c7f4ab6699e1e5ebc2e0c6d291c733feffffff02c0d45407000000001976a9145eaaf6718517ec8a291c6e64b16183292e7011f788ac5ef44534000000001976a91485e12fb9967c96759eae1c6b1e9c07ce977b638788acbe000000";
// Good v1 tx
{
std::vector<unsigned char> txData(ParseHex(goodPrefix + hexTx ));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx;
ssData >> tx;
EXPECT_EQ(tx.nVersion, 1);
EXPECT_EQ(tx.fOverwintered, false);
}
// Good v1 mutable tx
{
std::vector<unsigned char> txData(ParseHex(goodPrefix + hexTx ));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CMutableTransaction mtx;
ssData >> mtx;
EXPECT_EQ(mtx.nVersion, 1);
}
// Bad tx
{
std::vector<unsigned char> txData(ParseHex(badPrefix + hexTx ));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
try {
CTransaction tx;
ssData >> tx;
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format'";
}
catch(std::ios_base::failure & err) {
EXPECT_THAT(err.what(), testing::HasSubstr(std::string("Unknown transaction format")));
}
catch(...) {
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception";
}
}
// Bad mutable tx
{
std::vector<unsigned char> txData(ParseHex(badPrefix + hexTx ));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
try {
CMutableTransaction mtx;
ssData >> mtx;
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format'";
}
catch(std::ios_base::failure & err) {
EXPECT_THAT(err.what(), testing::HasSubstr(std::string("Unknown transaction format")));
}
catch(...) {
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception";
}
}
}

183
src/gtest/test_circuit.cpp Normal file
View File

@ -0,0 +1,183 @@
#include <gtest/gtest.h>
#include "uint256.h"
#include "zcash/util.h"
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/optional.hpp>
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>
#include <libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp>
#include <libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp>
#include "zcash/IncrementalMerkleTree.hpp"
using namespace libsnark;
using namespace libzcash;
#include "zcash/circuit/utils.tcc"
#include "zcash/circuit/merkle.tcc"
template<typename FieldT>
void test_value_equals(uint64_t i) {
protoboard<FieldT> pb;
pb_variable_array<FieldT> num;
num.allocate(pb, 64, "");
num.fill_with_bits(pb, uint64_to_bool_vector(i));
pb.add_r1cs_constraint(r1cs_constraint<FieldT>(
packed_addition(num),
FieldT::one(),
FieldT::one() * i
), "");
ASSERT_TRUE(pb.is_satisfied());
}
TEST(circuit, values)
{
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
test_value_equals<FieldT>(0);
test_value_equals<FieldT>(1);
test_value_equals<FieldT>(3);
test_value_equals<FieldT>(5391);
test_value_equals<FieldT>(883128374);
test_value_equals<FieldT>(173419028459);
test_value_equals<FieldT>(2205843009213693953);
}
TEST(circuit, endianness)
{
std::vector<unsigned char> before = {
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63
};
auto result = swap_endianness_u64(before);
std::vector<unsigned char> after = {
56, 57, 58, 59, 60, 61, 62, 63,
48, 49, 50, 51, 52, 53, 54, 55,
40, 41, 42, 43, 44, 45, 46, 47,
32, 33, 34, 35, 36, 37, 38, 39,
24, 25, 26, 27, 28, 29, 30, 31,
16, 17, 18, 19, 20, 21, 22, 23,
8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7
};
EXPECT_EQ(after, result);
std::vector<unsigned char> bad = {0, 1, 2, 3};
ASSERT_THROW(swap_endianness_u64(bad), std::length_error);
}
template<typename FieldT>
bool test_merkle_gadget(
bool enforce_a,
bool enforce_b,
bool write_root_first
)
{
protoboard<FieldT> pb;
digest_variable<FieldT> root(pb, 256, "root");
pb.set_input_sizes(256);
digest_variable<FieldT> commitment1(pb, 256, "commitment1");
digest_variable<FieldT> commitment2(pb, 256, "commitment2");
pb_variable<FieldT> commitment1_read;
commitment1_read.allocate(pb);
pb_variable<FieldT> commitment2_read;
commitment2_read.allocate(pb);
merkle_tree_gadget<FieldT> mgadget1(pb, commitment1, root, commitment1_read);
merkle_tree_gadget<FieldT> mgadget2(pb, commitment2, root, commitment2_read);
commitment1.generate_r1cs_constraints();
commitment2.generate_r1cs_constraints();
root.generate_r1cs_constraints();
mgadget1.generate_r1cs_constraints();
mgadget2.generate_r1cs_constraints();
ZCIncrementalMerkleTree tree;
uint256 commitment1_data = uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c");
uint256 commitment2_data = uint256S("59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7");
tree.append(commitment1_data);
auto wit1 = tree.witness();
tree.append(commitment2_data);
wit1.append(commitment2_data);
auto wit2 = tree.witness();
auto expected_root = tree.root();
tree.append(uint256S("3e243c8798678570bb8d42616c23a536af44be15c4eef073490c2a44ae5f32c3"));
auto unexpected_root = tree.root();
tree.append(uint256S("26d9b20c7f1c3d2528bbcd43cd63344b0afd3b6a0a8ebd37ec51cba34907bec7"));
auto badwit1 = tree.witness();
tree.append(uint256S("02c2467c9cd15e0d150f74cd636505ed675b0b71b66a719f6f52fdb49a5937bb"));
auto badwit2 = tree.witness();
// Perform the test
pb.val(commitment1_read) = enforce_a ? FieldT::one() : FieldT::zero();
pb.val(commitment2_read) = enforce_b ? FieldT::one() : FieldT::zero();
commitment1.bits.fill_with_bits(pb, uint256_to_bool_vector(commitment1_data));
commitment2.bits.fill_with_bits(pb, uint256_to_bool_vector(commitment2_data));
if (write_root_first) {
root.bits.fill_with_bits(pb, uint256_to_bool_vector(expected_root));
}
mgadget1.generate_r1cs_witness(wit1.path());
mgadget2.generate_r1cs_witness(wit2.path());
// Overwrite with our expected root
root.bits.fill_with_bits(pb, uint256_to_bool_vector(expected_root));
return pb.is_satisfied();
}
TEST(circuit, merkle_tree_gadget_weirdness)
{
/*
The merkle tree gadget takes a leaf in the merkle tree (the Note commitment),
a merkle tree authentication path, and a root (anchor). It also takes a parameter
called read_success, which is used to determine if the commitment actually needs to
appear in the tree.
If two input notes use the same root (which our protocol does) then if `read_success`
is disabled on the first note but enabled on the second note (i.e., the first note
has value of zero and second note has nonzero value) then there is an edge case in
the witnessing behavior. The first witness will accidentally constrain the root to
equal null (the default value of the anchor) and the second witness will actually
copy the bits, violating the constraint system.
Notice that this edge case is not in the constraint system but in the witnessing
behavior.
*/
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
// Test the normal case
ASSERT_TRUE(test_merkle_gadget<FieldT>(true, true, false));
ASSERT_TRUE(test_merkle_gadget<FieldT>(true, true, true));
// Test the case where the first commitment is enforced but the second isn't
// Works because the first read is performed before the second one
ASSERT_TRUE(test_merkle_gadget<FieldT>(true, false, false));
ASSERT_TRUE(test_merkle_gadget<FieldT>(true, false, true));
// Test the case where the first commitment isn't enforced but the second is
// Doesn't work because the first multipacker witnesses the existing root (which
// is null)
ASSERT_TRUE(!test_merkle_gadget<FieldT>(false, true, false));
// Test the last again, except this time write the root first.
ASSERT_TRUE(test_merkle_gadget<FieldT>(false, true, true));
}

View File

@ -0,0 +1,122 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "clientversion.h"
#include "deprecation.h"
#include "init.h"
#include "ui_interface.h"
#include "util.h"
#include "chainparams.h"
using ::testing::StrictMock;
static const std::string CLIENT_VERSION_STR = FormatVersion(CLIENT_VERSION);
extern std::atomic<bool> fRequestShutdown;
class MockUIInterface {
public:
MOCK_METHOD3(ThreadSafeMessageBox, bool(const std::string& message,
const std::string& caption,
unsigned int style));
};
static bool ThreadSafeMessageBox(MockUIInterface *mock,
const std::string& message,
const std::string& caption,
unsigned int style)
{
return mock->ThreadSafeMessageBox(message, caption, style);
}
class DeprecationTest : public ::testing::Test {
protected:
virtual void SetUp() {
uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, &mock_, _1, _2, _3));
SelectParams(CBaseChainParams::MAIN);
}
virtual void TearDown() {
fRequestShutdown = false;
mapArgs.clear();
}
StrictMock<MockUIInterface> mock_;
};
TEST_F(DeprecationTest, NonDeprecatedNodeKeepsRunning) {
EXPECT_FALSE(ShutdownRequested());
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT - 1);
EXPECT_FALSE(ShutdownRequested());
}
TEST_F(DeprecationTest, NodeNearDeprecationIsWarned) {
EXPECT_FALSE(ShutdownRequested());
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING));
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT);
EXPECT_FALSE(ShutdownRequested());
}
TEST_F(DeprecationTest, NodeNearDeprecationWarningIsNotDuplicated) {
EXPECT_FALSE(ShutdownRequested());
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1);
EXPECT_FALSE(ShutdownRequested());
}
TEST_F(DeprecationTest, NodeNearDeprecationWarningIsRepeatedOnStartup) {
EXPECT_FALSE(ShutdownRequested());
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING));
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1, true);
EXPECT_FALSE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeShutsDown) {
EXPECT_FALSE(ShutdownRequested());
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
EXPECT_TRUE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeErrorIsNotDuplicated) {
EXPECT_FALSE(ShutdownRequested());
EnforceNodeDeprecation(DEPRECATION_HEIGHT + 1);
EXPECT_TRUE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeErrorIsRepeatedOnStartup) {
EXPECT_FALSE(ShutdownRequested());
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
EnforceNodeDeprecation(DEPRECATION_HEIGHT + 1, true);
EXPECT_TRUE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeShutsDownIfOldVersionDisabled) {
EXPECT_FALSE(ShutdownRequested());
mapArgs["-disabledeprecation"] = "1.0.0";
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
EXPECT_TRUE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeKeepsRunningIfCurrentVersionDisabled) {
EXPECT_FALSE(ShutdownRequested());
mapArgs["-disabledeprecation"] = CLIENT_VERSION_STR;
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
EXPECT_FALSE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnRegtest) {
SelectParams(CBaseChainParams::REGTEST);
EXPECT_FALSE(ShutdownRequested());
EnforceNodeDeprecation(DEPRECATION_HEIGHT+1);
EXPECT_FALSE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnTestnet) {
SelectParams(CBaseChainParams::TESTNET);
EXPECT_FALSE(ShutdownRequested());
EnforceNodeDeprecation(DEPRECATION_HEIGHT+1);
EXPECT_FALSE(ShutdownRequested());
}

291
src/gtest/test_equihash.cpp Normal file
View File

@ -0,0 +1,291 @@
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "crypto/equihash.h"
#include "uint256.h"
void TestExpandAndCompress(const std::string &scope, size_t bit_len, size_t byte_pad,
std::vector<unsigned char> compact,
std::vector<unsigned char> expanded)
{
SCOPED_TRACE(scope);
std::vector<unsigned char> out(expanded.size());
ExpandArray(compact.data(), compact.size(),
out.data(), out.size(), bit_len, byte_pad);
EXPECT_EQ(expanded, out);
out.resize(compact.size());
CompressArray(expanded.data(), expanded.size(),
out.data(), out.size(), bit_len, byte_pad);
EXPECT_EQ(compact, out);
}
TEST(equihash_tests, expand_and_contract_arrays) {
TestExpandAndCompress("8 11-bit chunks, all-ones", 11, 0,
ParseHex("ffffffffffffffffffffff"),
ParseHex("07ff07ff07ff07ff07ff07ff07ff07ff"));
TestExpandAndCompress("8 21-bit chunks, alternating 1s and 0s", 21, 0,
ParseHex("aaaaad55556aaaab55555aaaaad55556aaaab55555"),
ParseHex("155555155555155555155555155555155555155555155555"));
TestExpandAndCompress("8 21-bit chunks, based on example in the spec", 21, 0,
ParseHex("000220000a7ffffe00123022b38226ac19bdf23456"),
ParseHex("0000440000291fffff0001230045670089ab00cdef123456"));
TestExpandAndCompress("16 14-bit chunks, alternating 11s and 00s", 14, 0,
ParseHex("cccf333cccf333cccf333cccf333cccf333cccf333cccf333cccf333"),
ParseHex("3333333333333333333333333333333333333333333333333333333333333333"));
TestExpandAndCompress("8 11-bit chunks, all-ones, 2-byte padding", 11, 2,
ParseHex("ffffffffffffffffffffff"),
ParseHex("000007ff000007ff000007ff000007ff000007ff000007ff000007ff000007ff"));
}
void TestMinimalSolnRepr(const std::string &scope, size_t cBitLen,
std::vector<eh_index> indices,
std::vector<unsigned char> minimal)
{
SCOPED_TRACE(scope);
EXPECT_EQ(indices, GetIndicesFromMinimal(minimal, cBitLen));
EXPECT_EQ(minimal, GetMinimalFromIndices(indices, cBitLen));
}
TEST(equihash_tests, minimal_solution_representation) {
TestMinimalSolnRepr("Test 1", 20,
{1, 1, 1, 1, 1, 1, 1, 1},
ParseHex("000008000040000200001000008000040000200001"));
TestMinimalSolnRepr("Test 2", 20,
{2097151, 2097151, 2097151, 2097151,
2097151, 2097151, 2097151, 2097151},
ParseHex("ffffffffffffffffffffffffffffffffffffffffff"));
TestMinimalSolnRepr("Test 3", 20,
{131071, 128, 131071, 128, 131071, 128, 131071, 128},
ParseHex("0ffff8002003fffe000800ffff8002003fffe00080"));
TestMinimalSolnRepr("Test 4", 20,
{68, 41, 2097151, 1233, 665, 1023, 1, 1048575},
ParseHex("000220000a7ffffe004d10014c800ffc00002fffff"));
}
TEST(equihash_tests, is_probably_duplicate) {
std::shared_ptr<eh_trunc> p1 (new eh_trunc[4] {0, 1, 2, 3}, std::default_delete<eh_trunc[]>());
std::shared_ptr<eh_trunc> p2 (new eh_trunc[4] {0, 1, 1, 3}, std::default_delete<eh_trunc[]>());
std::shared_ptr<eh_trunc> p3 (new eh_trunc[4] {3, 1, 1, 3}, std::default_delete<eh_trunc[]>());
ASSERT_FALSE(IsProbablyDuplicate<4>(p1, 4));
ASSERT_FALSE(IsProbablyDuplicate<4>(p2, 4));
ASSERT_TRUE(IsProbablyDuplicate<4>(p3, 4));
}
#ifdef ENABLE_MINING
TEST(equihash_tests, check_basic_solver_cancelled) {
Equihash<48,5> Eh48_5;
crypto_generichash_blake2b_state state;
Eh48_5.InitialiseState(state);
uint256 V = uint256S("0x00");
crypto_generichash_blake2b_update(&state, V.begin(), V.size());
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return false;
}));
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == ListGeneration;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == ListSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == ListColliding;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == RoundEnd;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == FinalSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == FinalColliding;
}), EhSolverCancelledException);
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialGeneration;
}));
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialSorting;
}));
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialSubtreeEnd;
}));
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialIndexEnd;
}));
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialEnd;
}));
}
}
TEST(equihash_tests, check_optimised_solver_cancelled) {
Equihash<48,5> Eh48_5;
crypto_generichash_blake2b_state state;
Eh48_5.InitialiseState(state);
uint256 V = uint256S("0x00");
crypto_generichash_blake2b_update(&state, V.begin(), V.size());
{
ASSERT_NO_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return false;
}));
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == ListGeneration;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == ListSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == ListColliding;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == RoundEnd;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == FinalSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == FinalColliding;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialGeneration;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialSubtreeEnd;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialIndexEnd;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector<unsigned char> soln) {
return false;
}, [](EhSolverCancelCheck pos) {
return pos == PartialEnd;
}), EhSolverCancelledException);
}
}
#endif // ENABLE_MINING

View File

@ -0,0 +1,195 @@
#include <gtest/gtest.h>
#include "main.h"
#include "utilmoneystr.h"
#include "chainparams.h"
#include "utilstrencodings.h"
#include "zcash/Address.hpp"
#include "wallet/wallet.h"
#include "amount.h"
#include <memory>
#include <string>
#include <set>
#include <vector>
#include <boost/filesystem.hpp>
#include "util.h"
// To run tests:
// ./zcash-gtest --gtest_filter="founders_reward_test.*"
//
// Enable this test to generate and print 48 testnet 2-of-3 multisig addresses.
// The output can be copied into chainparams.cpp.
// The temporary wallet file can be renamed as wallet.dat and used for testing with zcashd.
//
#if 0
TEST(founders_reward_test, create_testnet_2of3multisig) {
ECC_Start();
SelectParams(CBaseChainParams::TESTNET);
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
boost::filesystem::create_directories(pathTemp);
mapArgs["-datadir"] = pathTemp.string();
bool fFirstRun;
auto pWallet = std::make_shared<CWallet>("wallet.dat");
ASSERT_EQ(DB_LOAD_OK, pWallet->LoadWallet(fFirstRun));
pWallet->TopUpKeyPool();
std::cout << "Test wallet and logs saved in folder: " << pathTemp.native() << std::endl;
int numKeys = 48;
std::vector<CPubKey> pubkeys;
pubkeys.resize(3);
CPubKey newKey;
std::vector<std::string> addresses;
for (int i = 0; i < numKeys; i++) {
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
pubkeys[0] = newKey;
pWallet->SetAddressBook(newKey.GetID(), "", "receive");
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
pubkeys[1] = newKey;
pWallet->SetAddressBook(newKey.GetID(), "", "receive");
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
pubkeys[2] = newKey;
pWallet->SetAddressBook(newKey.GetID(), "", "receive");
CScript result = GetScriptForMultisig(2, pubkeys);
ASSERT_FALSE(result.size() > MAX_SCRIPT_ELEMENT_SIZE);
CScriptID innerID(result);
pWallet->AddCScript(result);
pWallet->SetAddressBook(innerID, "", "receive");
std::string address = CBitcoinAddress(innerID).ToString();
addresses.push_back(address);
}
// Print out the addresses, 4 on each line.
std::string s = "vFoundersRewardAddress = {\n";
int i=0;
int colsPerRow = 4;
ASSERT_TRUE(numKeys % colsPerRow == 0);
int numRows = numKeys/colsPerRow;
for (int row=0; row<numRows; row++) {
s += " ";
for (int col=0; col<colsPerRow; col++) {
s += "\"" + addresses[i++] + "\", ";
}
s += "\n";
}
s += " };";
std::cout << s << std::endl;
pWallet->Flush(true);
ECC_Stop();
}
#endif
// Utility method to check the number of unique addresses from height 1 to maxHeight
void checkNumberOfUniqueAddresses(int nUnique) {
int maxHeight = Params().GetConsensus().GetLastFoundersRewardBlockHeight();
std::set<std::string> addresses;
for (int i = 1; i <= maxHeight; i++) {
addresses.insert(Params().GetFoundersRewardAddressAtHeight(i));
}
ASSERT_TRUE(addresses.size() == nUnique);
}
TEST(founders_reward_test, general) {
SelectParams(CBaseChainParams::TESTNET);
CChainParams params = Params();
// Fourth testnet reward:
// address = t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy
// script.ToString() = OP_HASH160 55d64928e69829d9376c776550b6cc710d427153 OP_EQUAL
// HexStr(script) = a91455d64928e69829d9376c776550b6cc710d42715387
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(1), ParseHex("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"));
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(1), "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi");
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(53126), ParseHex("a914ac67f4c072668138d88a86ff21b27207b283212f87"));
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(53126), "t2NGQjYMQhFndDHguvUw4wZdNdsssA6K7x2");
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(53127), ParseHex("a91455d64928e69829d9376c776550b6cc710d42715387"));
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(53127), "t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy");
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();
// If the block height parameter is out of bounds, there is an assert.
EXPECT_DEATH(params.GetFoundersRewardScriptAtHeight(0), "nHeight");
EXPECT_DEATH(params.GetFoundersRewardScriptAtHeight(maxHeight+1), "nHeight");
EXPECT_DEATH(params.GetFoundersRewardAddressAtHeight(0), "nHeight");
EXPECT_DEATH(params.GetFoundersRewardAddressAtHeight(maxHeight+1), "nHeight");
}
#define NUM_MAINNET_FOUNDER_ADDRESSES 48
TEST(founders_reward_test, mainnet) {
SelectParams(CBaseChainParams::MAIN);
checkNumberOfUniqueAddresses(NUM_MAINNET_FOUNDER_ADDRESSES);
}
#define NUM_TESTNET_FOUNDER_ADDRESSES 48
TEST(founders_reward_test, testnet) {
SelectParams(CBaseChainParams::TESTNET);
checkNumberOfUniqueAddresses(NUM_TESTNET_FOUNDER_ADDRESSES);
}
#define NUM_REGTEST_FOUNDER_ADDRESSES 1
TEST(founders_reward_test, regtest) {
SelectParams(CBaseChainParams::REGTEST);
checkNumberOfUniqueAddresses(NUM_REGTEST_FOUNDER_ADDRESSES);
}
// Test that 10% founders reward is fully rewarded after the first halving and slow start shift.
// On Mainnet, this would be 2,100,000 ZEC after 850,000 blocks (840,000 + 10,000).
TEST(founders_reward_test, slow_start_subsidy) {
SelectParams(CBaseChainParams::MAIN);
CChainParams params = Params();
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();
CAmount totalSubsidy = 0;
for (int nHeight = 1; nHeight <= maxHeight; nHeight++) {
CAmount nSubsidy = GetBlockSubsidy(nHeight, params.GetConsensus()) / 5;
totalSubsidy += nSubsidy;
}
ASSERT_TRUE(totalSubsidy == MAX_MONEY/10.0);
}
// For use with mainnet and testnet which each have 48 addresses.
// Verify the number of rewards each individual address receives.
void verifyNumberOfRewards() {
CChainParams params = Params();
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();
std::multiset<std::string> ms;
for (int nHeight = 1; nHeight <= maxHeight; nHeight++) {
ms.insert(params.GetFoundersRewardAddressAtHeight(nHeight));
}
ASSERT_TRUE(ms.count(params.GetFoundersRewardAddressAtIndex(0)) == 17708);
for (int i = 1; i <= 46; i++) {
ASSERT_TRUE(ms.count(params.GetFoundersRewardAddressAtIndex(i)) == 17709);
}
ASSERT_TRUE(ms.count(params.GetFoundersRewardAddressAtIndex(47)) == 17677);
}
// Verify the number of rewards going to each mainnet address
TEST(founders_reward_test, per_address_reward_mainnet) {
SelectParams(CBaseChainParams::MAIN);
verifyNumberOfRewards();
}
// Verify the number of rewards going to each testnet address
TEST(founders_reward_test, per_address_reward_testnet) {
SelectParams(CBaseChainParams::TESTNET);
verifyNumberOfRewards();
}

View File

@ -0,0 +1,62 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "httprpc.cpp"
#include "httpserver.h"
using ::testing::Return;
class MockHTTPRequest : public HTTPRequest {
public:
MOCK_METHOD0(GetPeer, CService());
MOCK_METHOD0(GetRequestMethod, HTTPRequest::RequestMethod());
MOCK_METHOD1(GetHeader, std::pair<bool, std::string>(const std::string& hdr));
MOCK_METHOD2(WriteHeader, void(const std::string& hdr, const std::string& value));
MOCK_METHOD2(WriteReply, void(int nStatus, const std::string& strReply));
MockHTTPRequest() : HTTPRequest(nullptr) {}
void CleanUp() {
// So the parent destructor doesn't try to send a reply
replySent = true;
}
};
TEST(HTTPRPC, FailsOnGET) {
MockHTTPRequest req;
EXPECT_CALL(req, GetRequestMethod())
.WillRepeatedly(Return(HTTPRequest::GET));
EXPECT_CALL(req, WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests"))
.Times(1);
EXPECT_FALSE(HTTPReq_JSONRPC(&req, ""));
req.CleanUp();
}
TEST(HTTPRPC, FailsWithoutAuthHeader) {
MockHTTPRequest req;
EXPECT_CALL(req, GetRequestMethod())
.WillRepeatedly(Return(HTTPRequest::POST));
EXPECT_CALL(req, GetHeader("authorization"))
.WillRepeatedly(Return(std::make_pair(false, "")));
EXPECT_CALL(req, WriteHeader("WWW-Authenticate", "Basic realm=\"jsonrpc\""))
.Times(1);
EXPECT_CALL(req, WriteReply(HTTP_UNAUTHORIZED, ""))
.Times(1);
EXPECT_FALSE(HTTPReq_JSONRPC(&req, ""));
req.CleanUp();
}
TEST(HTTPRPC, FailsWithBadAuth) {
MockHTTPRequest req;
EXPECT_CALL(req, GetRequestMethod())
.WillRepeatedly(Return(HTTPRequest::POST));
EXPECT_CALL(req, GetHeader("authorization"))
.WillRepeatedly(Return(std::make_pair(true, "Basic spam:eggs")));
EXPECT_CALL(req, GetPeer())
.WillRepeatedly(Return(CService("127.0.0.1:1337")));
EXPECT_CALL(req, WriteHeader("WWW-Authenticate", "Basic realm=\"jsonrpc\""))
.Times(1);
EXPECT_CALL(req, WriteReply(HTTP_UNAUTHORIZED, ""))
.Times(1);
EXPECT_FALSE(HTTPReq_JSONRPC(&req, ""));
req.CleanUp();
}

View File

@ -0,0 +1,549 @@
#include <gtest/gtest.h>
#include "utilstrencodings.h"
#include <boost/foreach.hpp>
#include "zcash/prf.h"
#include "util.h"
#include "zcash/JoinSplit.hpp"
#include "zcash/Note.hpp"
#include "zcash/NoteEncryption.hpp"
#include "zcash/IncrementalMerkleTree.hpp"
using namespace libzcash;
extern ZCJoinSplit* params;
void test_full_api(ZCJoinSplit* js)
{
// Create verification context.
auto verifier = libzcash::ProofVerifier::Strict();
// The recipient's information.
SpendingKey recipient_key = SpendingKey::random();
PaymentAddress recipient_addr = recipient_key.address();
// Create the commitment tree
ZCIncrementalMerkleTree tree;
// Set up a JoinSplit description
uint256 ephemeralKey;
uint256 randomSeed;
uint64_t vpub_old = 10;
uint64_t vpub_new = 0;
uint256 pubKeyHash = random_uint256();
boost::array<uint256, 2> macs;
boost::array<uint256, 2> nullifiers;
boost::array<uint256, 2> commitments;
uint256 rt = tree.root();
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
ZCProof proof;
{
boost::array<JSInput, 2> inputs = {
JSInput(), // dummy input
JSInput() // dummy input
};
boost::array<JSOutput, 2> outputs = {
JSOutput(recipient_addr, 10),
JSOutput() // dummy output
};
boost::array<Note, 2> output_notes;
// Perform the proof
proof = js->prove(
inputs,
outputs,
output_notes,
ciphertexts,
ephemeralKey,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
);
}
// Verify the transaction:
ASSERT_TRUE(js->verify(
proof,
verifier,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
));
// Recipient should decrypt
// Now the recipient should spend the money again
auto h_sig = js->h_sig(randomSeed, nullifiers, pubKeyHash);
ZCNoteDecryption decryptor(recipient_key.receiving_key());
auto note_pt = NotePlaintext::decrypt(
decryptor,
ciphertexts[0],
ephemeralKey,
h_sig,
0
);
auto decrypted_note = note_pt.note(recipient_addr);
ASSERT_TRUE(decrypted_note.value == 10);
// Insert the commitments from the last tx into the tree
tree.append(commitments[0]);
auto witness_recipient = tree.witness();
tree.append(commitments[1]);
witness_recipient.append(commitments[1]);
vpub_old = 0;
vpub_new = 1;
rt = tree.root();
pubKeyHash = random_uint256();
{
boost::array<JSInput, 2> inputs = {
JSInput(), // dummy input
JSInput(witness_recipient, decrypted_note, recipient_key)
};
SpendingKey second_recipient = SpendingKey::random();
PaymentAddress second_addr = second_recipient.address();
boost::array<JSOutput, 2> outputs = {
JSOutput(second_addr, 9),
JSOutput() // dummy output
};
boost::array<Note, 2> output_notes;
// Perform the proof
proof = js->prove(
inputs,
outputs,
output_notes,
ciphertexts,
ephemeralKey,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
);
}
// Verify the transaction:
ASSERT_TRUE(js->verify(
proof,
verifier,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
));
}
// Invokes the API (but does not compute a proof)
// to test exceptions
void invokeAPI(
ZCJoinSplit* js,
const boost::array<JSInput, 2>& inputs,
const boost::array<JSOutput, 2>& outputs,
uint64_t vpub_old,
uint64_t vpub_new,
const uint256& rt
) {
uint256 ephemeralKey;
uint256 randomSeed;
uint256 pubKeyHash = random_uint256();
boost::array<uint256, 2> macs;
boost::array<uint256, 2> nullifiers;
boost::array<uint256, 2> commitments;
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
boost::array<Note, 2> output_notes;
ZCProof proof = js->prove(
inputs,
outputs,
output_notes,
ciphertexts,
ephemeralKey,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt,
false
);
}
void invokeAPIFailure(
ZCJoinSplit* js,
const boost::array<JSInput, 2>& inputs,
const boost::array<JSOutput, 2>& outputs,
uint64_t vpub_old,
uint64_t vpub_new,
const uint256& rt,
std::string reason
)
{
try {
invokeAPI(js, inputs, outputs, vpub_old, vpub_new, rt);
FAIL() << "It worked, when it shouldn't have!";
} catch(std::invalid_argument const & err) {
EXPECT_EQ(err.what(), reason);
} catch(...) {
FAIL() << "Expected invalid_argument exception.";
}
}
TEST(joinsplit, h_sig)
{
/*
// by Taylor Hornby
import pyblake2
import binascii
def hSig(randomSeed, nf1, nf2, pubKeyHash):
return pyblake2.blake2b(
data=(randomSeed + nf1 + nf2 + pubKeyHash),
digest_size=32,
person=b"ZcashComputehSig"
).digest()
INCREASING = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
TEST_VECTORS = [
[b"a" * 32, b"b" * 32, b"c" * 32, b"d" * 32],
[b"\x00" * 32, b"\x00" * 32, b"\x00" * 32, b"\x00" * 32],
[b"\xFF" * 32, b"\xFF" * 32, b"\xFF" * 32, b"\xFF" * 32],
[INCREASING, INCREASING, INCREASING, INCREASING]
]
for test_input in TEST_VECTORS:
print "---"
print "\"" + binascii.hexlify(test_input[0][::-1]) + "\""
print "\"" + binascii.hexlify(test_input[1][::-1]) + "\""
print "\"" + binascii.hexlify(test_input[2][::-1]) + "\""
print "\"" + binascii.hexlify(test_input[3][::-1]) + "\""
print "\"" + binascii.hexlify(hSig(test_input[0], test_input[1], test_input[2], test_input[3])[::-1]) + "\""
*/
std::vector<std::vector<std::string>> tests = {
{
"6161616161616161616161616161616161616161616161616161616161616161",
"6262626262626262626262626262626262626262626262626262626262626262",
"6363636363636363636363636363636363636363636363636363636363636363",
"6464646464646464646464646464646464646464646464646464646464646464",
"a8cba69f1fa329c055756b4af900f8a00b61e44f4cb8a1824ceb58b90a5b8113"
},
{
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"697322276b5dd93b12fb1fcbd2144b2960f24c73aac6c6a0811447be1e7f1e19"
},
{
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"4961048919f0ca79d49c9378c36a91a8767060001f4212fe6f7d426f3ccf9f32"
},
{
"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
"b61110ec162693bc3d9ca7fb0eec3afd2e278e2f41394b3ff11d7cb761ad4b27"
}
};
BOOST_FOREACH(std::vector<std::string>& v, tests) {
auto expected = ZCJoinSplit::h_sig(
uint256S(v[0]),
{uint256S(v[1]), uint256S(v[2])},
uint256S(v[3])
);
EXPECT_EQ(expected, uint256S(v[4]));
}
}
void increment_note_witnesses(
const uint256& element,
std::vector<ZCIncrementalWitness>& witnesses,
ZCIncrementalMerkleTree& tree
)
{
tree.append(element);
for (ZCIncrementalWitness& w : witnesses) {
w.append(element);
}
witnesses.push_back(tree.witness());
}
TEST(joinsplit, full_api_test)
{
{
std::vector<ZCIncrementalWitness> witnesses;
ZCIncrementalMerkleTree tree;
increment_note_witnesses(uint256(), witnesses, tree);
SpendingKey sk = SpendingKey::random();
PaymentAddress addr = sk.address();
Note note1(addr.a_pk, 100, random_uint256(), random_uint256());
increment_note_witnesses(note1.cm(), witnesses, tree);
Note note2(addr.a_pk, 100, random_uint256(), random_uint256());
increment_note_witnesses(note2.cm(), witnesses, tree);
Note note3(addr.a_pk, 2100000000000001, random_uint256(), random_uint256());
increment_note_witnesses(note3.cm(), witnesses, tree);
Note note4(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
increment_note_witnesses(note4.cm(), witnesses, tree);
Note note5(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
increment_note_witnesses(note5.cm(), witnesses, tree);
// Should work
invokeAPI(params,
{
JSInput(),
JSInput()
},
{
JSOutput(),
JSOutput()
},
0,
0,
tree.root());
// lhs > MAX_MONEY
invokeAPIFailure(params,
{
JSInput(),
JSInput()
},
{
JSOutput(),
JSOutput()
},
2100000000000001,
0,
tree.root(),
"nonsensical vpub_old value");
// rhs > MAX_MONEY
invokeAPIFailure(params,
{
JSInput(),
JSInput()
},
{
JSOutput(),
JSOutput()
},
0,
2100000000000001,
tree.root(),
"nonsensical vpub_new value");
// input witness for the wrong element
invokeAPIFailure(params,
{
JSInput(witnesses[0], note1, sk),
JSInput()
},
{
JSOutput(),
JSOutput()
},
0,
100,
tree.root(),
"witness of wrong element for joinsplit input");
// input witness doesn't match up with
// real root
invokeAPIFailure(params,
{
JSInput(witnesses[1], note1, sk),
JSInput()
},
{
JSOutput(),
JSOutput()
},
0,
100,
uint256(),
"joinsplit not anchored to the correct root");
// input is in the tree now! this should work
invokeAPI(params,
{
JSInput(witnesses[1], note1, sk),
JSInput()
},
{
JSOutput(),
JSOutput()
},
0,
100,
tree.root());
// Wrong secret key
invokeAPIFailure(params,
{
JSInput(witnesses[1], note1, SpendingKey::random()),
JSInput()
},
{
JSOutput(),
JSOutput()
},
0,
0,
tree.root(),
"input note not authorized to spend with given key");
// Absurd input value
invokeAPIFailure(params,
{
JSInput(witnesses[3], note3, sk),
JSInput()
},
{
JSOutput(),
JSOutput()
},
0,
0,
tree.root(),
"nonsensical input note value");
// Absurd total input value
invokeAPIFailure(params,
{
JSInput(witnesses[4], note4, sk),
JSInput(witnesses[5], note5, sk)
},
{
JSOutput(),
JSOutput()
},
0,
0,
tree.root(),
"nonsensical left hand size of joinsplit balance");
// Absurd output value
invokeAPIFailure(params,
{
JSInput(),
JSInput()
},
{
JSOutput(addr, 2100000000000001),
JSOutput()
},
0,
0,
tree.root(),
"nonsensical output value");
// Absurd total output value
invokeAPIFailure(params,
{
JSInput(),
JSInput()
},
{
JSOutput(addr, 1900000000000000),
JSOutput(addr, 1900000000000000)
},
0,
0,
tree.root(),
"nonsensical right hand side of joinsplit balance");
// Absurd total output value
invokeAPIFailure(params,
{
JSInput(),
JSInput()
},
{
JSOutput(addr, 1900000000000000),
JSOutput()
},
0,
0,
tree.root(),
"invalid joinsplit balance");
}
test_full_api(params);
}
TEST(joinsplit, note_plaintexts)
{
uint252 a_sk = uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516840e"));
uint256 a_pk = PRF_addr_a_pk(a_sk);
uint256 sk_enc = ZCNoteEncryption::generate_privkey(a_sk);
uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc);
PaymentAddress addr_pk(a_pk, pk_enc);
uint256 h_sig;
ZCNoteEncryption encryptor(h_sig);
uint256 epk = encryptor.get_epk();
Note note(a_pk,
1945813,
random_uint256(),
random_uint256()
);
boost::array<unsigned char, ZC_MEMO_SIZE> memo;
NotePlaintext note_pt(note, memo);
ZCNoteEncryption::Ciphertext ct = note_pt.encrypt(encryptor, pk_enc);
ZCNoteDecryption decryptor(sk_enc);
auto decrypted = NotePlaintext::decrypt(decryptor, ct, epk, h_sig, 0);
auto decrypted_note = decrypted.note(addr_pk);
ASSERT_TRUE(decrypted_note.a_pk == note.a_pk);
ASSERT_TRUE(decrypted_note.rho == note.rho);
ASSERT_TRUE(decrypted_note.r == note.r);
ASSERT_TRUE(decrypted_note.value == note.value);
ASSERT_TRUE(decrypted.memo == note_pt.memo);
}

188
src/gtest/test_keystore.cpp Normal file
View File

@ -0,0 +1,188 @@
#include <gtest/gtest.h>
#include "keystore.h"
#include "random.h"
#ifdef ENABLE_WALLET
#include "wallet/crypter.h"
#endif
#include "zcash/Address.hpp"
TEST(keystore_tests, store_and_retrieve_spending_key) {
CBasicKeyStore keyStore;
libzcash::SpendingKey skOut;
std::set<libzcash::PaymentAddress> addrs;
keyStore.GetPaymentAddresses(addrs);
EXPECT_EQ(0, addrs.size());
auto sk = libzcash::SpendingKey::random();
auto addr = sk.address();
// Sanity-check: we can't get a key we haven't added
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
keyStore.AddSpendingKey(sk);
EXPECT_TRUE(keyStore.HaveSpendingKey(addr));
EXPECT_TRUE(keyStore.GetSpendingKey(addr, skOut));
EXPECT_EQ(sk, skOut);
keyStore.GetPaymentAddresses(addrs);
EXPECT_EQ(1, addrs.size());
EXPECT_EQ(1, addrs.count(addr));
}
TEST(keystore_tests, store_and_retrieve_note_decryptor) {
CBasicKeyStore keyStore;
ZCNoteDecryption decOut;
auto sk = libzcash::SpendingKey::random();
auto addr = sk.address();
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
keyStore.AddSpendingKey(sk);
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
}
TEST(keystore_tests, StoreAndRetrieveViewingKey) {
CBasicKeyStore keyStore;
libzcash::ViewingKey vkOut;
libzcash::SpendingKey skOut;
ZCNoteDecryption decOut;
auto sk = libzcash::SpendingKey::random();
auto vk = sk.viewing_key();
auto addr = sk.address();
// Sanity-check: we can't get a viewing key we haven't added
EXPECT_FALSE(keyStore.HaveViewingKey(addr));
EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut));
// and we shouldn't have a spending key or decryptor either
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
// and we can't find it in our list of addresses
std::set<libzcash::PaymentAddress> addresses;
keyStore.GetPaymentAddresses(addresses);
EXPECT_FALSE(addresses.count(addr));
keyStore.AddViewingKey(vk);
EXPECT_TRUE(keyStore.HaveViewingKey(addr));
EXPECT_TRUE(keyStore.GetViewingKey(addr, vkOut));
EXPECT_EQ(vk, vkOut);
// We should still not have the spending key...
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
// ... but we should have a decryptor
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
// ... and we should find it in our list of addresses
addresses.clear();
keyStore.GetPaymentAddresses(addresses);
EXPECT_TRUE(addresses.count(addr));
keyStore.RemoveViewingKey(vk);
EXPECT_FALSE(keyStore.HaveViewingKey(addr));
EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut));
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
addresses.clear();
keyStore.GetPaymentAddresses(addresses);
EXPECT_FALSE(addresses.count(addr));
// We still have a decryptor because those are cached in memory
// (and also we only remove viewing keys when adding a spending key)
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
}
#ifdef ENABLE_WALLET
class TestCCryptoKeyStore : public CCryptoKeyStore
{
public:
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn) { return CCryptoKeyStore::EncryptKeys(vMasterKeyIn); }
bool Unlock(const CKeyingMaterial& vMasterKeyIn) { return CCryptoKeyStore::Unlock(vMasterKeyIn); }
};
TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) {
TestCCryptoKeyStore keyStore;
uint256 r {GetRandHash()};
CKeyingMaterial vMasterKey (r.begin(), r.end());
libzcash::SpendingKey keyOut;
ZCNoteDecryption decOut;
std::set<libzcash::PaymentAddress> addrs;
// 1) Test adding a key to an unencrypted key store, then encrypting it
auto sk = libzcash::SpendingKey::random();
auto addr = sk.address();
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
keyStore.AddSpendingKey(sk);
ASSERT_TRUE(keyStore.HaveSpendingKey(addr));
ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut));
ASSERT_EQ(sk, keyOut);
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey));
ASSERT_TRUE(keyStore.HaveSpendingKey(addr));
ASSERT_FALSE(keyStore.GetSpendingKey(addr, keyOut));
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
// Unlocking with a random key should fail
uint256 r2 {GetRandHash()};
CKeyingMaterial vRandomKey (r2.begin(), r2.end());
EXPECT_FALSE(keyStore.Unlock(vRandomKey));
// Unlocking with a slightly-modified vMasterKey should fail
CKeyingMaterial vModifiedKey (r.begin(), r.end());
vModifiedKey[0] += 1;
EXPECT_FALSE(keyStore.Unlock(vModifiedKey));
// Unlocking with vMasterKey should succeed
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut));
ASSERT_EQ(sk, keyOut);
keyStore.GetPaymentAddresses(addrs);
ASSERT_EQ(1, addrs.size());
ASSERT_EQ(1, addrs.count(addr));
// 2) Test adding a spending key to an already-encrypted key store
auto sk2 = libzcash::SpendingKey::random();
auto addr2 = sk2.address();
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr2, decOut));
keyStore.AddSpendingKey(sk2);
ASSERT_TRUE(keyStore.HaveSpendingKey(addr2));
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
ASSERT_EQ(sk2, keyOut);
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
ASSERT_TRUE(keyStore.Lock());
ASSERT_TRUE(keyStore.HaveSpendingKey(addr2));
ASSERT_FALSE(keyStore.GetSpendingKey(addr2, keyOut));
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
ASSERT_EQ(sk2, keyOut);
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
keyStore.GetPaymentAddresses(addrs);
ASSERT_EQ(2, addrs.size());
ASSERT_EQ(1, addrs.count(addr));
ASSERT_EQ(1, addrs.count(addr2));
}
#endif

View File

@ -0,0 +1,44 @@
#include <gtest/gtest.h>
#include "zcash/util.h"
TEST(libzcash_utils, convertBytesVectorToVector)
{
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
};
ASSERT_TRUE(convertBytesVectorToVector(bytes) == expected_bits);
}
TEST(libzcash_utils, convertVectorToInt)
{
ASSERT_TRUE(convertVectorToInt({0}) == 0);
ASSERT_TRUE(convertVectorToInt({1}) == 1);
ASSERT_TRUE(convertVectorToInt({0,1}) == 1);
ASSERT_TRUE(convertVectorToInt({1,0}) == 2);
ASSERT_TRUE(convertVectorToInt({1,1}) == 3);
ASSERT_TRUE(convertVectorToInt({1,0,0}) == 4);
ASSERT_TRUE(convertVectorToInt({1,0,1}) == 5);
ASSERT_TRUE(convertVectorToInt({1,1,0}) == 6);
ASSERT_THROW(convertVectorToInt(std::vector<bool>(100)), std::length_error);
{
std::vector<bool> v(63, 1);
ASSERT_TRUE(convertVectorToInt(v) == 0x7fffffffffffffff);
}
{
std::vector<bool> v(64, 1);
ASSERT_TRUE(convertVectorToInt(v) == 0xffffffffffffffff);
}
}

263
src/gtest/test_mempool.cpp Normal file
View File

@ -0,0 +1,263 @@
#include <gtest/gtest.h>
#include <gtest/gtest-spi.h>
#include "consensus/upgrades.h"
#include "consensus/validation.h"
#include "core_io.h"
#include "main.h"
#include "primitives/transaction.h"
#include "txmempool.h"
#include "policy/fees.h"
#include "util.h"
// Implementation is in test_checktransaction.cpp
extern CMutableTransaction GetValidTransaction();
// Fake the input of transaction 5295156213414ed77f6e538e7e8ebe14492156906b9fe995b242477818789364
// - 532639cc6bebed47c1c69ae36dd498c68a012e74ad12729adbd3dbb56f8f3f4a, 0
class FakeCoinsViewDB : public CCoinsView {
public:
FakeCoinsViewDB() {}
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
return false;
}
bool GetNullifier(const uint256 &nf) const {
return false;
}
bool GetCoins(const uint256 &txid, CCoins &coins) const {
CTxOut txOut;
txOut.nValue = 4288035;
CCoins newCoins;
newCoins.vout.resize(2);
newCoins.vout[0] = txOut;
newCoins.nHeight = 92045;
coins.swap(newCoins);
return true;
}
bool HaveCoins(const uint256 &txid) const {
return true;
}
uint256 GetBestBlock() const {
uint256 a;
return a;
}
uint256 GetBestAnchor() const {
uint256 a;
return a;
}
bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors,
CNullifiersMap &mapNullifiers) {
return false;
}
bool GetStats(CCoinsStats &stats) const {
return false;
}
};
TEST(Mempool, PriorityStatsDoNotCrash) {
// Test for security issue 2017-04-11.a
// https://z.cash/blog/security-announcement-2017-04-12.html
// Trigger transaction in block 92046
std::string triggerTx = "02000000014a3f8f6fb5dbd3db9a7212ad742e018ac698d46de39ac6c147edeb6bcc392653000000006b483045022100da0514afd80d3bbd0743458efe3b2abd18f727b4268b124c3885094c26ea09cd02207d37d7934ec90618fc5a345cb2a6d1755d8b1a432ea1df517a85e36628449196012103e9b41072e9d2cbe04e6b22a6ac4862ec3f5a76b3823b071ded0dfd5455a0803fffffffff000000000001236e4100000000000000000000000000cf592f6776810cf9fb961d80e683f5529a6b34894b00446c396022512a02dc2e96918294bffdc988a2627d9b12a9f3176b671e286fc62e3c7441cf35ea5e03d561bd5817ca5827cb2761f88dde280a6da5281af2cc69053816f31abd2170722f72c258c7c6b865c97ff8ae53b697f3b77c239a73e1d0296c2c73d21c3b50059d82866cf9f6e2f5dbbf9b4de9caa3cf836080373311978e1f1b1150ce564336ebf0fd502c2e783ff23252fba4efbb110c28c4fbe31efb6a67bc24c0ad8fd8346c5c9ed1791b555b3e43a309aa8d855a294847368ebdf30365d4dfa9b25dd8ed7adf272ecc08f4756fb9d93b8e20d45548dd4aeab6abb76a2081b6e36a9af9d38ebae378df422f40589769c07a3c12e5da253330314cbc4beaa863fac7ab10055d0310089a44c45179a39b2c4e210cec2e053f54983d744abed49f66959f45785ea90325a310fba15f946e245a0e64caec303f2a3e1d457e3e3ca5d892956c1a93b05e0b0373cf862d7bbb5908148b475c03eec96c9b9ecc7d2d78716d2c2e8ccf96175b25738dfb5b0d356a7e30604ee014c6be1db5e43af0fa6ad3f4e917a9f84b4d6f030cad0ffe0738e1effe570b0552b407ca9c26023b74b99f431cc28b79116f717703158404e343b1b47a0556f593441dc64758930f19e84d5ee468fd9a7958c6c63503054f60680f7147e88bf6da65415450230ef7437481023fc5d94872d5aa18bf3b0212b4c0d938e6c84debb8a4e65f99970c59193873a72b2440f19a652073abd960021bfef4e1e52b8f353c6e517bb97053afd4c8035defc27c3fd16faba5bc924a4179f69cfdcdb82253b5f6472a99d4b78ad2c6c18c45ed4dda5bf2adc019c99b55702f4e7b3fcaeb6f3b84ad411d36e901cba9d49ac1d6b916aa88794fb23501aeb0c585cbc2bed952846f41a03bd5c74dfe004e7ac21f7a20d32b009ccf6f70b3e577d25c679421225522b6290d5fa00a5d9a02b97a62aab60e040a03efa946d87c5e65dbf10d66df5b0834c262c31c23f3c2643451e614695003fb3a95bf21444bebb45cdcb8169245e34a76f754c89c3a90f36598a71ef4645eef4c82f1fb322536097fcf0cbe061e80ae887dbb88d8ed910be9ef18b8794930addab1a140b16c4b50f93926b1e5df03ee6e4b5ec6d7f0ed49fbbae50330ae94c5ae9182f4b58870022e423e7d80adccdb90680f7a7fe11a4ed4fe005a0af2d22bf9e7d1bef7caf4f37f5777e4aa6c9b9ea44f5973575c20fb3482fe357c19fc0c20594f492f5694e3e8eb3599e968fd23b5bdd6c4bf5aee1374b38aafe59dd5af83011e642a9427b5ff03e7a4cce92ee201a0fac0acb69d6ad3b7e4c26dfefaa53a737889e759c4b5695c1a7fd5d988e531acf66dae5067f252a25a102d92916b2d84c730645e15a78d3dce1c787634f6f7323cb949a5b6ad004e208cb8c6b734761629c13b9974dc80b082f83357f3bc703d835acbbf72aba225ffe69396c151d2646fac9bd1acc184dd047ebfaadc6b60a9185ce80c7bc8ac5dbb2219cbc0d35af91673b95d28335f0ee2774b8084871d54ca8eab3a285e4b4adf3f34b4263d67474bb5de2e1e37aa7a4ecbd5b49575caaa9e7208c2b9871946b11f2c54cd1ca7660dff44cf206e7da46ab57dd49ec0aa06ded7980f1557cc7c84023690b4df77f26d6b4eff7553b9a8628c28e8e5c38c6170bc61af0969b072586fa740f68ab33c0f62d0507cc8fe41c680b2f077e49cc2691048006311b46cd5ed18e63089f11c115b797ed5fbcd86836a4da2ab90a00745077f2f13bc9e390fc2f92b941d4fb70a3f098548953566141670317fc17e0ba81e98b8a94919992fb008c5480f4018f3a1ea673fe94a6ba3363656a944855d7c799ccb73d95d4ed6ce04c26f79c4cf79f883f0f810519f28eabe8cf6d833f24227f5074763c7b80f1431e5463cc07eff2f1d6cbfaf0411d38e62528a39b093ed7b51fa8c1008e5c1ce4bf39e67b1703554cefef44b71457bfddf43d23a54fa0145fa0e716d02a5304d85345a2b4ebf98c5010d0df468c8cbfc2db22083b0f5a74d4324ee74b46daa5ab70f2575ef5390e6aa2acb8d3b3eb2065e8c06fd6276aca283f5850e7a8b4da37455430df55621e4af59bb355ba2db0ac6cae6ceed2f538ec8c928ee895bd190fd9c1dff4956bad27d567023bc847dd64d83bac399f8d10248a077c9b2f50d5dda4789e09eae4ed8609da085b6370f6529f3c7b8b13442f9a1cc93565734a81c38c6360235ba23ddf87d1b44413c24b363552a322b01d88d1cae6c5ccd88f8cef15776035934fd24adce913282983d9b55181b382ed61242fb4a5e459c3cad8d38626ef8ccdccb9899d5962dec3f3cc2422f109d902e764186cf166ed88c383f428f195dd5fec4f781bcd2308dec66927f41c9e06369c6806ed8ec9a59c096b29b2a74dc85f4f7cd77a23650c0662b5c2602ef5e41cdc13d16074500aac461f823e3bba7178bcffa000db4ffc9b1618395824ffbee1cad1d9c138a20e0b8bbea2d9a07fade932f81c3daf2d64c4991daf4a1b8b531f9b958a252c6c38cd463342aef3e03e3dae3370581d6cddf5af3ef1585780cf83db1909a1daca156018cd2f7483e53a5fccda49640de60b24523617c7ae84ec5fa987ba8a108";
CTransaction tx;
ASSERT_TRUE(DecodeHexTx(tx, triggerTx));
ASSERT_EQ(tx.GetHash().GetHex(), "5295156213414ed77f6e538e7e8ebe14492156906b9fe995b242477818789364");
// Fake its inputs
FakeCoinsViewDB fakeDB;
CCoinsViewCache view(&fakeDB);
CTxMemPool testPool(CFeeRate(0));
// Values taken from core dump (parameters of entry)
CAmount nFees = 0;
int64_t nTime = 0x58e5fed9;
unsigned int nHeight = 92045;
double dPriority = view.GetPriority(tx, nHeight);
CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, false, SPROUT_BRANCH_ID);
// Check it does not crash (ie. the death test fails)
EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), "");
EXPECT_EQ(dPriority, MAX_PRIORITY);
}
TEST(Mempool, TxInputLimit) {
SelectParams(CBaseChainParams::TESTNET);
CTxMemPool pool(::minRelayTxFee);
bool missingInputs;
// Create an obviously-invalid transaction
// We intentionally set tx.nVersion = 0 to reliably trigger an error, as
// it's the first check that occurs after the -mempooltxinputlimit check,
// and it means that we don't have to mock out a lot of global state.
CMutableTransaction mtx;
mtx.nVersion = 0;
mtx.vin.resize(10);
// Check it fails as expected
CValidationState state1;
CTransaction tx1(mtx);
EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs));
EXPECT_EQ(state1.GetRejectReason(), "bad-txns-version-too-low");
// Set a limit
mapArgs["-mempooltxinputlimit"] = "10";
// Check it still fails as expected
CValidationState state2;
EXPECT_FALSE(AcceptToMemoryPool(pool, state2, tx1, false, &missingInputs));
EXPECT_EQ(state2.GetRejectReason(), "bad-txns-version-too-low");
// Resize the transaction
mtx.vin.resize(11);
// Check it now fails due to exceeding the limit
CValidationState state3;
CTransaction tx3(mtx);
EXPECT_FALSE(AcceptToMemoryPool(pool, state3, tx3, false, &missingInputs));
// The -mempooltxinputlimit check doesn't set a reason
EXPECT_EQ(state3.GetRejectReason(), "");
// Clear the limit
mapArgs.erase("-mempooltxinputlimit");
// Check it no longer fails due to exceeding the limit
CValidationState state4;
EXPECT_FALSE(AcceptToMemoryPool(pool, state4, tx3, false, &missingInputs));
EXPECT_EQ(state4.GetRejectReason(), "bad-txns-version-too-low");
}
// Valid overwinter v3 format tx gets rejected because overwinter hasn't activated yet.
TEST(Mempool, OverwinterNotActiveYet) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
CTxMemPool pool(::minRelayTxFee);
bool missingInputs;
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0); // no joinsplits
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
CValidationState state1;
CTransaction tx1(mtx);
EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs));
EXPECT_EQ(state1.GetRejectReason(), "tx-overwinter-not-active");
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Sprout transaction version 3 when Overwinter is not active:
// 1. pass CheckTransaction (and CheckTransactionWithoutProofVerification)
// 2. pass ContextualCheckTransaction
// 3. fail IsStandardTx
TEST(Mempool, SproutV3TxFailsAsExpected) {
SelectParams(CBaseChainParams::TESTNET);
CTxMemPool pool(::minRelayTxFee);
bool missingInputs;
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0); // no joinsplits
mtx.fOverwintered = false;
mtx.nVersion = 3;
CValidationState state1;
CTransaction tx1(mtx);
EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs));
EXPECT_EQ(state1.GetRejectReason(), "version");
}
// Sprout transaction version 3 when Overwinter is always active:
// 1. pass CheckTransaction (and CheckTransactionWithoutProofVerification)
// 2. fails ContextualCheckTransaction
TEST(Mempool, SproutV3TxWhenOverwinterActive) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
CTxMemPool pool(::minRelayTxFee);
bool missingInputs;
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0); // no joinsplits
mtx.fOverwintered = false;
mtx.nVersion = 3;
CValidationState state1;
CTransaction tx1(mtx);
EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs));
EXPECT_EQ(state1.GetRejectReason(), "tx-overwinter-flag-not-set");
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Sprout transaction with negative version, rejected by the mempool in CheckTransaction
// under Sprout consensus rules, should still be rejected under Overwinter consensus rules.
// 1. fails CheckTransaction (specifically CheckTransactionWithoutProofVerification)
TEST(Mempool, SproutNegativeVersionTxWhenOverwinterActive) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
CTxMemPool pool(::minRelayTxFee);
bool missingInputs;
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0); // no joinsplits
mtx.fOverwintered = false;
// A Sprout transaction with version -3 is created using Sprout code (as found in Zcashd <= 1.0.14).
// First four bytes of transaction, parsed as an uint32_t, has the value: 0xfffffffd
// This test simulates an Overwinter node receiving this transaction, but incorrectly deserializing the
// transaction due to a (pretend) bug of not detecting the most significant bit, which leads
// to not setting fOverwintered and not masking off the most significant bit of the header field.
// The resulting Sprout tx with nVersion -3 should be rejected by the Overwinter node's mempool.
{
mtx.nVersion = -3;
EXPECT_EQ(mtx.nVersion, static_cast<int32_t>(0xfffffffd));
CTransaction tx1(mtx);
EXPECT_EQ(tx1.nVersion, -3);
CValidationState state1;
EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs));
EXPECT_EQ(state1.GetRejectReason(), "bad-txns-version-too-low");
}
// A Sprout transaction with version -3 created using Overwinter code (as found in Zcashd >= 1.0.15).
// First four bytes of transaction, parsed as an uint32_t, has the value: 0x80000003
// This test simulates the same pretend bug described above.
// The resulting Sprout tx with nVersion -2147483645 should be rejected by the Overwinter node's mempool.
{
mtx.nVersion = static_cast<int32_t>((1 << 31) | 3);
EXPECT_EQ(mtx.nVersion, static_cast<int32_t>(0x80000003));
CTransaction tx1(mtx);
EXPECT_EQ(tx1.nVersion, -2147483645);
CValidationState state1;
EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs));
EXPECT_EQ(state1.GetRejectReason(), "bad-txns-version-too-low");
}
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}

View File

@ -0,0 +1,301 @@
#include <gtest/gtest.h>
#include "test/data/merkle_roots.json.h"
#include "test/data/merkle_roots_empty.json.h"
#include "test/data/merkle_serialization.json.h"
#include "test/data/merkle_witness_serialization.json.h"
#include "test/data/merkle_path.json.h"
#include "test/data/merkle_commitments.json.h"
#include <iostream>
#include <stdexcept>
#include "utilstrencodings.h"
#include "version.h"
#include "serialize.h"
#include "streams.h"
#include "zcash/IncrementalMerkleTree.hpp"
#include "zcash/util.h"
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>
#include <libsnark/gadgetlib1/gadgets/hashes/sha256/sha256_gadget.hpp>
#include <libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp>
#include <boost/foreach.hpp>
#include "json_test_vectors.h"
using namespace std;
using namespace libsnark;
template<>
void expect_deser_same(const ZCTestingIncrementalWitness& expected)
{
// Cannot check this; IncrementalWitness cannot be
// deserialized because it can only be constructed by
// IncrementalMerkleTree, and it does not yet have a
// canonical serialized representation.
}
template<>
void expect_deser_same(const libzcash::MerklePath& expected)
{
// This deserialization check is pointless for MerklePath,
// since we only serialize it to check it against test
// vectors. See `expect_test_vector` for that. Also,
// it doesn't seem that vector<bool> can be properly
// deserialized by Bitcoin's serialization code.
}
template<typename A, typename B, typename C>
void expect_ser_test_vector(B& b, const C& c, const A& tree) {
expect_test_vector<B, C>(b, c);
}
template<typename Tree, typename Witness>
void test_tree(
UniValue commitment_tests,
UniValue root_tests,
UniValue ser_tests,
UniValue witness_ser_tests,
UniValue path_tests
)
{
size_t witness_ser_i = 0;
size_t path_i = 0;
Tree tree;
// The root of the tree at this point is expected to be the root of the
// empty tree.
ASSERT_TRUE(tree.root() == Tree::empty_root());
// The tree doesn't have a 'last' element added since it's blank.
ASSERT_THROW(tree.last(), std::runtime_error);
// The tree is empty.
ASSERT_TRUE(tree.size() == 0);
// We need to witness at every single point in the tree, so
// that the consistency of the tree and the merkle paths can
// be checked.
vector<Witness> witnesses;
for (size_t i = 0; i < 16; i++) {
uint256 test_commitment = uint256S(commitment_tests[i].get_str());
// Witness here
witnesses.push_back(tree.witness());
// Now append a commitment to the tree
tree.append(test_commitment);
// Size incremented by one.
ASSERT_TRUE(tree.size() == i+1);
// Last element added to the tree was `test_commitment`
ASSERT_TRUE(tree.last() == test_commitment);
// Check tree root consistency
expect_test_vector(root_tests[i], tree.root());
// Check serialization of tree
expect_ser_test_vector(ser_tests[i], tree, tree);
bool first = true; // The first witness can never form a path
BOOST_FOREACH(Witness& wit, witnesses)
{
// Append the same commitment to all the witnesses
wit.append(test_commitment);
if (first) {
ASSERT_THROW(wit.path(), std::runtime_error);
ASSERT_THROW(wit.element(), std::runtime_error);
} else {
auto path = wit.path();
{
expect_test_vector(path_tests[path_i++], path);
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
protoboard<FieldT> pb;
pb_variable_array<FieldT> positions;
digest_variable<FieldT> commitment(pb, 256, "commitment");
digest_variable<FieldT> root(pb, 256, "root");
positions.allocate(pb, INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, "pos");
merkle_authentication_path_variable<FieldT, sha256_two_to_one_hash_gadget<FieldT>> authvars(pb, INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, "auth");
merkle_tree_check_read_gadget<FieldT, sha256_two_to_one_hash_gadget<FieldT>> auth(
pb, INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, positions, commitment, root, authvars, ONE, "path"
);
commitment.generate_r1cs_constraints();
root.generate_r1cs_constraints();
authvars.generate_r1cs_constraints();
auth.generate_r1cs_constraints();
std::vector<bool> commitment_bv;
{
uint256 witnessed_commitment = wit.element();
std::vector<unsigned char> commitment_v(witnessed_commitment.begin(), witnessed_commitment.end());
commitment_bv = convertBytesVectorToVector(commitment_v);
}
size_t path_index = convertVectorToInt(path.index);
commitment.bits.fill_with_bits(pb, bit_vector(commitment_bv));
positions.fill_with_bits_of_ulong(pb, path_index);
authvars.generate_r1cs_witness(path_index, path.authentication_path);
auth.generate_r1cs_witness();
std::vector<bool> root_bv;
{
uint256 witroot = wit.root();
std::vector<unsigned char> root_v(witroot.begin(), witroot.end());
root_bv = convertBytesVectorToVector(root_v);
}
root.bits.fill_with_bits(pb, bit_vector(root_bv));
ASSERT_TRUE(pb.is_satisfied());
root_bv[0] = !root_bv[0];
root.bits.fill_with_bits(pb, bit_vector(root_bv));
ASSERT_TRUE(!pb.is_satisfied());
}
}
// Check witness serialization
expect_ser_test_vector(witness_ser_tests[witness_ser_i++], wit, tree);
ASSERT_TRUE(wit.root() == tree.root());
first = false;
}
}
{
// Tree should be full now
ASSERT_THROW(tree.append(uint256()), std::runtime_error);
BOOST_FOREACH(Witness& wit, witnesses)
{
ASSERT_THROW(wit.append(uint256()), std::runtime_error);
}
}
}
#define MAKE_STRING(x) std::string((x), (x)+sizeof(x))
TEST(merkletree, vectors) {
UniValue root_tests = read_json(MAKE_STRING(json_tests::merkle_roots));
UniValue ser_tests = read_json(MAKE_STRING(json_tests::merkle_serialization));
UniValue witness_ser_tests = read_json(MAKE_STRING(json_tests::merkle_witness_serialization));
UniValue path_tests = read_json(MAKE_STRING(json_tests::merkle_path));
UniValue commitment_tests = read_json(MAKE_STRING(json_tests::merkle_commitments));
test_tree<ZCTestingIncrementalMerkleTree, ZCTestingIncrementalWitness>(commitment_tests, root_tests, ser_tests, witness_ser_tests, path_tests);
}
TEST(merkletree, emptyroots) {
UniValue empty_roots = read_json(MAKE_STRING(json_tests::merkle_roots_empty));
libzcash::EmptyMerkleRoots<64, libzcash::SHA256Compress> emptyroots;
for (size_t depth = 0; depth <= 64; depth++) {
expect_test_vector(empty_roots[depth], emptyroots.empty_root(depth));
}
// Double check that we're testing (at least) all the empty roots we'll use.
ASSERT_TRUE(INCREMENTAL_MERKLE_TREE_DEPTH <= 64);
}
TEST(merkletree, emptyroot) {
// This literal is the depth-20 empty tree root with the bytes reversed to
// account for the fact that uint256S() loads a big-endian representation of
// an integer which converted to little-endian internally.
uint256 expected = uint256S("59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7");
ASSERT_TRUE(ZCIncrementalMerkleTree::empty_root() == expected);
}
TEST(merkletree, deserializeInvalid) {
// attempt to deserialize a small tree from a serialized large tree
// (exceeds depth well-formedness check)
ZCIncrementalMerkleTree newTree;
for (size_t i = 0; i < 16; i++) {
newTree.append(uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c"));
}
newTree.append(uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c"));
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << newTree;
ZCTestingIncrementalMerkleTree newTreeSmall;
ASSERT_THROW({ss >> newTreeSmall;}, std::ios_base::failure);
}
TEST(merkletree, deserializeInvalid2) {
// the most ancestral parent is empty
CDataStream ss(
ParseHex("0155b852781b9995a44c939b64e441ae2724b96f99c8f4fb9a141cfc9842c4b0e3000100"),
SER_NETWORK,
PROTOCOL_VERSION
);
ZCIncrementalMerkleTree tree;
ASSERT_THROW(ss >> tree, std::ios_base::failure);
}
TEST(merkletree, deserializeInvalid3) {
// left doesn't exist but right does
CDataStream ss(
ParseHex("000155b852781b9995a44c939b64e441ae2724b96f99c8f4fb9a141cfc9842c4b0e300"),
SER_NETWORK,
PROTOCOL_VERSION
);
ZCIncrementalMerkleTree tree;
ASSERT_THROW(ss >> tree, std::ios_base::failure);
}
TEST(merkletree, deserializeInvalid4) {
// left doesn't exist but a parent does
CDataStream ss(
ParseHex("000001018695873d63ec0bceeadb5bf4ccc6723ac803c1826fc7cfb34fc76180305ae27d"),
SER_NETWORK,
PROTOCOL_VERSION
);
ZCIncrementalMerkleTree tree;
ASSERT_THROW(ss >> tree, std::ios_base::failure);
}
TEST(merkletree, testZeroElements) {
for (int start = 0; start < 20; start++) {
ZCIncrementalMerkleTree newTree;
ASSERT_TRUE(newTree.root() == ZCIncrementalMerkleTree::empty_root());
for (int i = start; i > 0; i--) {
newTree.append(uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c"));
}
uint256 oldroot = newTree.root();
// At this point, appending tons of null objects to the tree
// should preserve its root.
for (int i = 0; i < 100; i++) {
newTree.append(uint256());
}
ASSERT_TRUE(newTree.root() == oldroot);
}
}

135
src/gtest/test_metrics.cpp Normal file
View File

@ -0,0 +1,135 @@
#include <gtest/gtest.h>
#include "metrics.h"
#include "utiltime.h"
TEST(Metrics, AtomicTimer) {
AtomicTimer t;
SetMockTime(100);
EXPECT_FALSE(t.running());
t.start();
EXPECT_TRUE(t.running());
t.start();
EXPECT_TRUE(t.running());
t.stop();
EXPECT_TRUE(t.running());
t.stop();
EXPECT_FALSE(t.running());
// Additional calls to stop() are ignored.
t.stop();
EXPECT_FALSE(t.running());
t.start();
EXPECT_TRUE(t.running());
AtomicCounter c;
EXPECT_EQ(0, t.rate(c));
c.increment();
EXPECT_EQ(0, t.rate(c));
SetMockTime(101);
EXPECT_EQ(1, t.rate(c));
c.decrement();
EXPECT_EQ(0, t.rate(c));
SetMockTime(102);
EXPECT_EQ(0, t.rate(c));
c.increment();
EXPECT_EQ(0.5, t.rate(c));
t.stop();
EXPECT_FALSE(t.running());
EXPECT_EQ(0.5, t.rate(c));
}
TEST(Metrics, GetLocalSolPS) {
SetMockTime(100);
miningTimer.start();
// No time has passed
EXPECT_EQ(0, GetLocalSolPS());
// Increment time
SetMockTime(101);
EXPECT_EQ(0, GetLocalSolPS());
// Increment solutions
solutionTargetChecks.increment();
EXPECT_EQ(1, GetLocalSolPS());
// Increment time
SetMockTime(102);
EXPECT_EQ(0.5, GetLocalSolPS());
// Increment solutions
solutionTargetChecks.increment();
solutionTargetChecks.increment();
EXPECT_EQ(1.5, GetLocalSolPS());
// Stop timing
miningTimer.stop();
EXPECT_EQ(1.5, GetLocalSolPS());
// Increment time
SetMockTime(103);
EXPECT_EQ(1.5, GetLocalSolPS());
// Start timing again
miningTimer.start();
EXPECT_EQ(1.5, GetLocalSolPS());
// Increment time
SetMockTime(104);
EXPECT_EQ(1, GetLocalSolPS());
}
TEST(Metrics, EstimateNetHeightInner) {
// Ensure that the (rounded) current height is returned if the tip is current
SetMockTime(15000);
EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 0, 150));
SetMockTime(15150);
EXPECT_EQ(100, EstimateNetHeightInner(101, 14250, 50, 7500, 0, 150));
// Ensure that correct estimates are returned if the tip is in the past
SetMockTime(15300); // Tip is 2 blocks behind
EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 0, 150));
SetMockTime(15900); // Tip is 6 blocks behind
EXPECT_EQ(110, EstimateNetHeightInner(100, 14100, 50, 7500, 0, 150));
// Check estimates during resync
SetMockTime(15000);
EXPECT_EQ(100, EstimateNetHeightInner( 0, 0, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner( 7, 600, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner( 8, 600, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(10, 750, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(11, 900, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(20, 2100, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(49, 6450, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(50, 6600, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(51, 6750, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(55, 7350, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(56, 7500, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(57, 7650, 50, 7500, 0, 150));
EXPECT_EQ(100, EstimateNetHeightInner(75, 10350, 50, 7500, 0, 150));
// More complex calculations:
SetMockTime(20000);
// - Checkpoint spacing: 200
// -> Average spacing: 175
// -> estimated height: 127 -> 130
EXPECT_EQ(130, EstimateNetHeightInner(100, 14100, 50, 5250, 0, 150));
// - Checkpoint spacing: 50
// -> Average spacing: 100
// -> estimated height: 153 -> 150
EXPECT_EQ(150, EstimateNetHeightInner(100, 14100, 50, 12000, 0, 150));
}

103
src/gtest/test_miner.cpp Normal file
View File

@ -0,0 +1,103 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "chainparams.h"
#include "key.h"
#include "miner.h"
#include "util.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif
#include <boost/optional.hpp>
using ::testing::Return;
#ifdef ENABLE_WALLET
class MockReserveKey : public CReserveKey {
public:
MockReserveKey() : CReserveKey(nullptr) { }
MOCK_METHOD1(GetReservedKey, bool(CPubKey &pubkey));
};
#endif
TEST(Miner, GetMinerScriptPubKey) {
SelectParams(CBaseChainParams::MAIN);
boost::optional<CScript> scriptPubKey;
#ifdef ENABLE_WALLET
MockReserveKey reservekey;
EXPECT_CALL(reservekey, GetReservedKey(::testing::_))
.WillRepeatedly(Return(false));
#endif
// No miner address set
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_FALSE((bool) scriptPubKey);
mapArgs["-mineraddress"] = "notAnAddress";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_FALSE((bool) scriptPubKey);
// Partial address
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqq";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_FALSE((bool) scriptPubKey);
// Typo in address
mapArgs["-mineraddress"] = "t1TByaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_FALSE((bool) scriptPubKey);
// Set up expected scriptPubKey for t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF
CKeyID keyID;
keyID.SetHex("eb88f1c65b39a823479ac9c7db2f4a865960a165");
CScript expectedScriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
// Valid address
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_TRUE((bool) scriptPubKey);
EXPECT_EQ(expectedScriptPubKey, *scriptPubKey);
// Valid address with leading whitespace
mapArgs["-mineraddress"] = " t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_TRUE((bool) scriptPubKey);
EXPECT_EQ(expectedScriptPubKey, *scriptPubKey);
// Valid address with trailing whitespace
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF ";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_TRUE((bool) scriptPubKey);
EXPECT_EQ(expectedScriptPubKey, *scriptPubKey);
}

View File

@ -0,0 +1,204 @@
#include <gtest/gtest.h>
#include "sodium.h"
#include <stdexcept>
#include "zcash/NoteEncryption.hpp"
#include "zcash/prf.h"
#include "crypto/sha256.h"
class TestNoteDecryption : public ZCNoteDecryption {
public:
TestNoteDecryption(uint256 sk_enc) : ZCNoteDecryption(sk_enc) {}
void change_pk_enc(uint256 to) {
pk_enc = to;
}
};
TEST(noteencryption, api)
{
uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint252(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a07")));
uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc);
ZCNoteEncryption b = ZCNoteEncryption(uint256());
for (size_t i = 0; i < 100; i++)
{
ZCNoteEncryption c = ZCNoteEncryption(uint256());
ASSERT_TRUE(b.get_epk() != c.get_epk());
}
boost::array<unsigned char, ZC_NOTEPLAINTEXT_SIZE> message;
for (size_t i = 0; i < ZC_NOTEPLAINTEXT_SIZE; i++) {
// Fill the message with dummy data
message[i] = (unsigned char) i;
}
for (int i = 0; i < 255; i++) {
auto ciphertext = b.encrypt(pk_enc, message);
{
ZCNoteDecryption decrypter(sk_enc);
// Test decryption
auto plaintext = decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i);
ASSERT_TRUE(plaintext == message);
// Test wrong nonce
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), (i == 0) ? 1 : (i - 1)),
libzcash::note_decryption_failed);
// Test wrong ephemeral key
{
ZCNoteEncryption c = ZCNoteEncryption(uint256());
ASSERT_THROW(decrypter.decrypt(ciphertext, c.get_epk(), uint256(), i),
libzcash::note_decryption_failed);
}
// Test wrong seed
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256S("11035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a77"), i),
libzcash::note_decryption_failed);
// Test corrupted ciphertext
ciphertext[10] ^= 0xff;
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
libzcash::note_decryption_failed);
ciphertext[10] ^= 0xff;
}
{
// Test wrong private key
uint256 sk_enc_2 = ZCNoteEncryption::generate_privkey(uint252());
ZCNoteDecryption decrypter(sk_enc_2);
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
libzcash::note_decryption_failed);
}
{
TestNoteDecryption decrypter(sk_enc);
// Test decryption
auto plaintext = decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i);
ASSERT_TRUE(plaintext == message);
// Test wrong public key (test of KDF)
decrypter.change_pk_enc(uint256());
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
libzcash::note_decryption_failed);
}
}
// Nonce space should run out here
try {
b.encrypt(pk_enc, message);
FAIL() << "Expected std::logic_error";
}
catch(std::logic_error const & err) {
EXPECT_EQ(err.what(), std::string("no additional nonce space for KDF"));
}
catch(...) {
FAIL() << "Expected std::logic_error";
}
}
uint256 test_prf(
unsigned char distinguisher,
uint252 seed_x,
uint256 y
) {
uint256 x = seed_x.inner();
*x.begin() &= 0x0f;
*x.begin() |= distinguisher;
CSHA256 hasher;
hasher.Write(x.begin(), 32);
hasher.Write(y.begin(), 32);
uint256 ret;
hasher.FinalizeNoPadding(ret.begin());
return ret;
}
TEST(noteencryption, prf_addr)
{
for (size_t i = 0; i < 100; i++) {
uint252 a_sk = libzcash::random_uint252();
uint256 rest;
ASSERT_TRUE(
test_prf(0xc0, a_sk, rest) == PRF_addr_a_pk(a_sk)
);
}
for (size_t i = 0; i < 100; i++) {
uint252 a_sk = libzcash::random_uint252();
uint256 rest;
*rest.begin() = 0x01;
ASSERT_TRUE(
test_prf(0xc0, a_sk, rest) == PRF_addr_sk_enc(a_sk)
);
}
}
TEST(noteencryption, prf_nf)
{
for (size_t i = 0; i < 100; i++) {
uint252 a_sk = libzcash::random_uint252();
uint256 rho = libzcash::random_uint256();
ASSERT_TRUE(
test_prf(0xe0, a_sk, rho) == PRF_nf(a_sk, rho)
);
}
}
TEST(noteencryption, prf_pk)
{
for (size_t i = 0; i < 100; i++) {
uint252 a_sk = libzcash::random_uint252();
uint256 h_sig = libzcash::random_uint256();
ASSERT_TRUE(
test_prf(0x00, a_sk, h_sig) == PRF_pk(a_sk, 0, h_sig)
);
}
for (size_t i = 0; i < 100; i++) {
uint252 a_sk = libzcash::random_uint252();
uint256 h_sig = libzcash::random_uint256();
ASSERT_TRUE(
test_prf(0x40, a_sk, h_sig) == PRF_pk(a_sk, 1, h_sig)
);
}
uint252 dummy_a;
uint256 dummy_b;
ASSERT_THROW(PRF_pk(dummy_a, 2, dummy_b), std::domain_error);
}
TEST(noteencryption, prf_rho)
{
for (size_t i = 0; i < 100; i++) {
uint252 phi = libzcash::random_uint252();
uint256 h_sig = libzcash::random_uint256();
ASSERT_TRUE(
test_prf(0x20, phi, h_sig) == PRF_rho(phi, 0, h_sig)
);
}
for (size_t i = 0; i < 100; i++) {
uint252 phi = libzcash::random_uint252();
uint256 h_sig = libzcash::random_uint256();
ASSERT_TRUE(
test_prf(0x60, phi, h_sig) == PRF_rho(phi, 1, h_sig)
);
}
uint252 dummy_a;
uint256 dummy_b;
ASSERT_THROW(PRF_rho(dummy_a, 2, dummy_b), std::domain_error);
}
TEST(noteencryption, uint252)
{
ASSERT_THROW(uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516847e")), std::domain_error);
}

View File

@ -0,0 +1,212 @@
#include <gtest/gtest.h>
#include "main.h"
#include "utilmoneystr.h"
#include "chainparams.h"
#include "utilstrencodings.h"
#include "zcash/Address.hpp"
#include "wallet/wallet.h"
#include "amount.h"
#include <memory>
#include <string>
#include <set>
#include <vector>
#include <boost/filesystem.hpp>
#include <iostream>
#include "util.h"
#include "paymentdisclosure.h"
#include "paymentdisclosuredb.h"
#include "sodium.h"
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
using namespace std;
/*
To run tests:
./zcash-gtest --gtest_filter="paymentdisclosure.*"
Note: As an experimental feature, writing your own tests may require option flags to be set.
mapArgs["-experimentalfeatures"] = true;
mapArgs["-paymentdisclosure"] = true;
*/
#define NUM_TRIES 10000
#define DUMP_DATABASE_TO_STDOUT false
static boost::uuids::random_generator uuidgen;
static uint256 random_uint256()
{
uint256 ret;
randombytes_buf(ret.begin(), 32);
return ret;
}
// Subclass of PaymentDisclosureDB to add debugging methods
class PaymentDisclosureDBTest : public PaymentDisclosureDB {
public:
PaymentDisclosureDBTest(const boost::filesystem::path& dbPath) : PaymentDisclosureDB(dbPath) {}
void DebugDumpAllStdout() {
ASSERT_NE(db, nullptr);
std::lock_guard<std::mutex> guard(lock_);
// Iterate over each item in the database and print them
leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
cout << it->key().ToString() << " : ";
// << it->value().ToString() << endl;
try {
std::string strValue = it->value().ToString();
PaymentDisclosureInfo info;
CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
ssValue >> info;
cout << info.ToString() << std::endl;
} catch (const std::exception& e) {
cout << e.what() << std::endl;
}
}
if (false == it->status().ok()) {
cerr << "An error was found iterating over the database" << endl;
cerr << it->status().ToString() << endl;
}
delete it;
}
};
// This test creates random payment disclosure blobs and checks that they can be
// 1. inserted and retrieved from a database
// 2. serialized and deserialized without corruption
// Note that the zpd: prefix is not part of the payment disclosure blob itself. It is only
// used as convention to improve the user experience when sharing payment disclosure blobs.
TEST(paymentdisclosure, mainnet) {
ECC_Start();
SelectParams(CBaseChainParams::MAIN);
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
boost::filesystem::create_directories(pathTemp);
mapArgs["-datadir"] = pathTemp.string();
std::cout << "Test payment disclosure database created in folder: " << pathTemp.native() << std::endl;
PaymentDisclosureDBTest mydb(pathTemp);
for (int i=0; i<NUM_TRIES; i++) {
// Generate an ephemeral keypair for joinsplit sig.
uint256 joinSplitPubKey;
unsigned char buffer[crypto_sign_SECRETKEYBYTES] = {0};
crypto_sign_keypair(joinSplitPubKey.begin(), &buffer[0]);
// First 32 bytes contain private key, second 32 bytes contain public key.
ASSERT_EQ(0, memcmp(joinSplitPubKey.begin(), &buffer[0]+32, 32));
std::vector<unsigned char> vch(&buffer[0], &buffer[0] + 32);
uint256 joinSplitPrivKey = uint256(vch);
// Create payment disclosure key and info data to store in test database
size_t js = random_uint256().GetCheapHash() % std::numeric_limits<size_t>::max();
uint8_t n = random_uint256().GetCheapHash() % std::numeric_limits<uint8_t>::max();
PaymentDisclosureKey key { random_uint256(), js, n};
PaymentDisclosureInfo info;
info.esk = random_uint256();
info.joinSplitPrivKey = joinSplitPrivKey;
info.zaddr = libzcash::SpendingKey::random().address();
ASSERT_TRUE(mydb.Put(key, info));
// Retrieve info from test database into new local variable and test it matches
PaymentDisclosureInfo info2;
ASSERT_TRUE(mydb.Get(key, info2));
ASSERT_EQ(info, info2);
// Modify this local variable and confirm it no longer matches
info2.esk = random_uint256();
info2.joinSplitPrivKey = random_uint256();
info2.zaddr = libzcash::SpendingKey::random().address();
ASSERT_NE(info, info2);
// Using the payment info object, let's create a dummy payload
PaymentDisclosurePayload payload;
payload.version = PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL;
payload.esk = info.esk;
payload.txid = key.hash;
payload.js = key.js;
payload.n = key.n;
payload.message = "random-" + boost::uuids::to_string(uuidgen()); // random message
payload.zaddr = info.zaddr;
// Serialize and hash the payload to generate a signature
uint256 dataToBeSigned = SerializeHash(payload, SER_GETHASH, 0);
// Compute the payload signature
unsigned char payloadSig[64];
if (!(crypto_sign_detached(&payloadSig[0], NULL,
dataToBeSigned.begin(), 32,
&buffer[0] // buffer containing both private and public key required
) == 0))
{
throw std::runtime_error("crypto_sign_detached failed");
}
// Sanity check
if (!(crypto_sign_verify_detached(&payloadSig[0],
dataToBeSigned.begin(), 32,
joinSplitPubKey.begin()
) == 0))
{
throw std::runtime_error("crypto_sign_verify_detached failed");
}
// Convert signature buffer to boost array
boost::array<unsigned char, 64> arrayPayloadSig;
memcpy(arrayPayloadSig.data(), &payloadSig[0], 64);
// Payment disclosure blob to pass around
PaymentDisclosure pd = {payload, arrayPayloadSig};
// Test payment disclosure constructors
PaymentDisclosure pd2(payload, arrayPayloadSig);
ASSERT_EQ(pd, pd2);
PaymentDisclosure pd3(joinSplitPubKey, key, info, payload.message);
ASSERT_EQ(pd, pd3);
// Verify serialization and deserialization works
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << pd;
std::string ssHexString = HexStr(ss.begin(), ss.end());
PaymentDisclosure pdTmp;
CDataStream ssTmp(ParseHex(ssHexString), SER_NETWORK, PROTOCOL_VERSION);
ssTmp >> pdTmp;
ASSERT_EQ(pd, pdTmp);
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
ss2 << pdTmp;
std::string ss2HexString = HexStr(ss2.begin(), ss2.end());
ASSERT_EQ(ssHexString, ss2HexString);
// Verify marker
ASSERT_EQ(pd.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES);
ASSERT_EQ(pdTmp.payload.marker, PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES);
ASSERT_EQ(0, ssHexString.find("706462ff")); // Little endian encoding of PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES value
// Sanity check
PaymentDisclosure pdDummy;
ASSERT_NE(pd, pdDummy);
}
#if DUMP_DATABASE_TO_STDOUT == true
mydb.DebugDumpAllStdout();
#endif
ECC_Stop();
}

70
src/gtest/test_pow.cpp Normal file
View File

@ -0,0 +1,70 @@
#include <gtest/gtest.h>
#include "chain.h"
#include "chainparams.h"
#include "pow.h"
#include "random.h"
TEST(PoW, DifficultyAveraging) {
SelectParams(CBaseChainParams::MAIN);
const Consensus::Params& params = Params().GetConsensus();
size_t lastBlk = 2*params.nPowAveragingWindow;
size_t firstBlk = lastBlk - params.nPowAveragingWindow;
// Start with blocks evenly-spaced and equal difficulty
std::vector<CBlockIndex> blocks(lastBlk+1);
for (int i = 0; i <= lastBlk; i++) {
blocks[i].pprev = i ? &blocks[i - 1] : nullptr;
blocks[i].nHeight = i;
blocks[i].nTime = 1269211443 + i * params.nPowTargetSpacing;
blocks[i].nBits = 0x1e7fffff; /* target 0x007fffff000... */
blocks[i].nChainWork = i ? blocks[i - 1].nChainWork + GetBlockProof(blocks[i - 1]) : arith_uint256(0);
}
// Result should be the same as if last difficulty was used
arith_uint256 bnAvg;
bnAvg.SetCompact(blocks[lastBlk].nBits);
EXPECT_EQ(CalculateNextWorkRequired(bnAvg,
blocks[lastBlk].GetMedianTimePast(),
blocks[firstBlk].GetMedianTimePast(),
params),
GetNextWorkRequired(&blocks[lastBlk], nullptr, params));
// Result should be unchanged, modulo integer division precision loss
arith_uint256 bnRes;
bnRes.SetCompact(0x1e7fffff);
bnRes /= params.AveragingWindowTimespan();
bnRes *= params.AveragingWindowTimespan();
EXPECT_EQ(bnRes.GetCompact(), GetNextWorkRequired(&blocks[lastBlk], nullptr, params));
// Randomise the final block time (plus 1 to ensure it is always different)
blocks[lastBlk].nTime += GetRand(params.nPowTargetSpacing/2) + 1;
// Result should be the same as if last difficulty was used
bnAvg.SetCompact(blocks[lastBlk].nBits);
EXPECT_EQ(CalculateNextWorkRequired(bnAvg,
blocks[lastBlk].GetMedianTimePast(),
blocks[firstBlk].GetMedianTimePast(),
params),
GetNextWorkRequired(&blocks[lastBlk], nullptr, params));
// Result should not be unchanged
EXPECT_NE(0x1e7fffff, GetNextWorkRequired(&blocks[lastBlk], nullptr, params));
// Change the final block difficulty
blocks[lastBlk].nBits = 0x1e0fffff;
// Result should not be the same as if last difficulty was used
bnAvg.SetCompact(blocks[lastBlk].nBits);
EXPECT_NE(CalculateNextWorkRequired(bnAvg,
blocks[lastBlk].GetMedianTimePast(),
blocks[firstBlk].GetMedianTimePast(),
params),
GetNextWorkRequired(&blocks[lastBlk], nullptr, params));
// Result should be the same as if the average difficulty was used
arith_uint256 average = UintToArith256(uint256S("0000796968696969696969696969696969696969696969696969696969696969"));
EXPECT_EQ(CalculateNextWorkRequired(average,
blocks[lastBlk].GetMedianTimePast(),
blocks[firstBlk].GetMedianTimePast(),
params),
GetNextWorkRequired(&blocks[lastBlk], nullptr, params));
}

702
src/gtest/test_proofs.cpp Normal file
View File

@ -0,0 +1,702 @@
#include <gtest/gtest.h>
#include "zcash/Proof.hpp"
#include <iostream>
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp>
#include <libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>
using namespace libzcash;
typedef libsnark::default_r1cs_ppzksnark_pp curve_pp;
typedef libsnark::default_r1cs_ppzksnark_pp::G1_type curve_G1;
typedef libsnark::default_r1cs_ppzksnark_pp::G2_type curve_G2;
typedef libsnark::default_r1cs_ppzksnark_pp::GT_type curve_GT;
typedef libsnark::default_r1cs_ppzksnark_pp::Fp_type curve_Fr;
typedef libsnark::default_r1cs_ppzksnark_pp::Fq_type curve_Fq;
typedef libsnark::default_r1cs_ppzksnark_pp::Fqe_type curve_Fq2;
#include "streams.h"
#include "version.h"
#include "utilstrencodings.h"
TEST(proofs, g1_pairing_at_infinity)
{
for (size_t i = 0; i < 100; i++) {
auto r1 = curve_G1::random_element();
auto r2 = curve_G2::random_element();
ASSERT_TRUE(
curve_pp::reduced_pairing(curve_G1::zero(), r2) ==
curve_GT::one()
);
ASSERT_TRUE(
curve_pp::final_exponentiation(
curve_pp::double_miller_loop(
curve_pp::precompute_G1(curve_G1::zero()),
curve_pp::precompute_G2(r2),
curve_pp::precompute_G1(curve_G1::zero()),
curve_pp::precompute_G2(r2)
)
) ==
curve_GT::one()
);
ASSERT_TRUE(
curve_pp::final_exponentiation(
curve_pp::double_miller_loop(
curve_pp::precompute_G1(r1),
curve_pp::precompute_G2(r2),
curve_pp::precompute_G1(curve_G1::zero()),
curve_pp::precompute_G2(r2)
)
) ==
curve_pp::reduced_pairing(r1, r2)
);
ASSERT_TRUE(
curve_pp::final_exponentiation(
curve_pp::double_miller_loop(
curve_pp::precompute_G1(curve_G1::zero()),
curve_pp::precompute_G2(r2),
curve_pp::precompute_G1(r1),
curve_pp::precompute_G2(r2)
)
) ==
curve_pp::reduced_pairing(r1, r2)
);
}
}
TEST(proofs, g2_subgroup_check)
{
// all G2 elements are order r
ASSERT_TRUE(libsnark::alt_bn128_modulus_r * curve_G2::random_element() == curve_G2::zero());
// but that doesn't mean all elements that satisfy the curve equation are in G2...
curve_G2 p = curve_G2::one();
while (1) {
// This will construct an order r(2q-r) point with high probability
p.X = curve_Fq2::random_element();
try {
p.Y = ((p.X.squared() * p.X) + libsnark::alt_bn128_twist_coeff_b).sqrt();
break;
} catch(...) {}
}
ASSERT_TRUE(p.is_well_formed()); // it's on the curve
ASSERT_TRUE(libsnark::alt_bn128_modulus_r * p != curve_G2::zero()); // but not the order r subgroup..
{
// libsnark unfortunately doesn't check, and the pairing will complete
auto e = curve_Fr("149");
auto a = curve_pp::reduced_pairing(curve_G1::one(), p);
auto b = curve_pp::reduced_pairing(e * curve_G1::one(), p);
// though it will not preserve bilinearity
ASSERT_TRUE((a^e) != b);
}
{
// so, our decompression API should not allow you to decompress G2 elements of that form!
CompressedG2 badp(p);
try {
auto newp = badp.to_libsnark_g2<curve_G2>();
FAIL() << "Expected std::runtime_error";
} catch (std::runtime_error const & err) {
EXPECT_EQ(err.what(), std::string("point is not in G2"));
} catch(...) {
FAIL() << "Expected std::runtime_error";
}
}
// educational purposes: showing that E'(Fp2) is of order r(2q-r),
// by multiplying our random point in E' by (2q-r) = (q + q - r) to
// get an element in G2
{
auto p1 = libsnark::alt_bn128_modulus_q * p;
p1 = p1 + p1;
p1 = p1 - (libsnark::alt_bn128_modulus_r * p);
ASSERT_TRUE(p1.is_well_formed());
ASSERT_TRUE(libsnark::alt_bn128_modulus_r * p1 == curve_G2::zero());
CompressedG2 goodp(p1);
auto newp = goodp.to_libsnark_g2<curve_G2>();
ASSERT_TRUE(newp == p1);
}
}
TEST(proofs, sqrt_zero)
{
ASSERT_TRUE(curve_Fq::zero() == curve_Fq::zero().sqrt());
ASSERT_TRUE(curve_Fq2::zero() == curve_Fq2::zero().sqrt());
}
TEST(proofs, sqrt_fq)
{
// Poor man's PRNG
curve_Fq acc = curve_Fq("348957923485290374852379485") ^ 1000;
size_t quadratic_residues = 0;
size_t quadratic_nonresidues = 0;
for (size_t i = 1; i < 1000; i++) {
try {
acc += curve_Fq("45634563456") ^ i;
curve_Fq x = acc.sqrt();
ASSERT_TRUE((x*x) == acc);
quadratic_residues += 1;
} catch (std::runtime_error &e) {
quadratic_nonresidues += 1;
}
}
// Half of all nonzero elements in Fp are quadratic residues
ASSERT_TRUE(quadratic_residues == 511);
ASSERT_TRUE(quadratic_nonresidues == 488);
for (size_t i = 0; i < 1000; i++) {
curve_Fq x = curve_Fq::random_element();
curve_Fq x2 = x * x;
ASSERT_TRUE((x2.sqrt() == x) || (x2.sqrt() == -x));
}
// Test vectors
ASSERT_TRUE(
curve_Fq("5204065062716160319596273903996315000119019512886596366359652578430118331601")
==
curve_Fq("348579348568").sqrt()
);
ASSERT_THROW(curve_Fq("348579348569").sqrt(), std::runtime_error);
}
TEST(proofs, sqrt_fq2)
{
curve_Fq2 acc = curve_Fq2(
curve_Fq("3456293840592348059238409578239048769348760238476029347885092384059238459834") ^ 1000,
curve_Fq("2394578084760439457823945729347502374590283479582739485723945729384759823745") ^ 1000
);
size_t quadratic_residues = 0;
size_t quadratic_nonresidues = 0;
for (size_t i = 1; i < 1000; i++) {
try {
acc = acc + curve_Fq2(
curve_Fq("5204065062716160319596273903996315000119019512886596366359652578430118331601") ^ i,
curve_Fq("348957923485290374852379485348957923485290374852379485348957923485290374852") ^ i
);
curve_Fq2 x = acc.sqrt();
ASSERT_TRUE((x*x) == acc);
quadratic_residues += 1;
} catch (std::runtime_error &e) {
quadratic_nonresidues += 1;
}
}
// Half of all nonzero elements in Fp^k are quadratic residues as long
// as p != 2
ASSERT_TRUE(quadratic_residues == 505);
ASSERT_TRUE(quadratic_nonresidues == 494);
for (size_t i = 0; i < 1000; i++) {
curve_Fq2 x = curve_Fq2::random_element();
curve_Fq2 x2 = x * x;
ASSERT_TRUE((x2.sqrt() == x) || (x2.sqrt() == -x));
}
// Test vectors
ASSERT_THROW(curve_Fq2(
curve_Fq("2"),
curve_Fq("1")
).sqrt(), std::runtime_error);
ASSERT_THROW(curve_Fq2(
curve_Fq("3345897230485723946872934576923485762803457692345760237495682347502347589473"),
curve_Fq("1234912378405347958234756902345768290345762348957605678245967234857634857676")
).sqrt(), std::runtime_error);
curve_Fq2 x = curve_Fq2(
curve_Fq("12844195307879678418043983815760255909500142247603239203345049921980497041944"),
curve_Fq("7476417578426924565731404322659619974551724117137577781074613937423560117731")
);
curve_Fq2 nx = -x;
curve_Fq2 x2 = curve_Fq2(
curve_Fq("3345897230485723946872934576923485762803457692345760237495682347502347589474"),
curve_Fq("1234912378405347958234756902345768290345762348957605678245967234857634857676")
);
ASSERT_TRUE(x == x2.sqrt());
ASSERT_TRUE(nx == -x2.sqrt());
ASSERT_TRUE(x*x == x2);
ASSERT_TRUE(nx*nx == x2);
}
TEST(proofs, size_is_expected)
{
ZCProof p;
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << p;
ASSERT_EQ(ss.size(), 296);
}
TEST(proofs, fq_serializes_properly)
{
for (size_t i = 0; i < 1000; i++) {
curve_Fq e = curve_Fq::random_element();
Fq e2(e);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << e2;
Fq e3;
ss >> e3;
curve_Fq e4 = e3.to_libsnark_fq<curve_Fq>();
ASSERT_TRUE(e == e4);
}
}
TEST(proofs, fq2_serializes_properly)
{
for (size_t i = 0; i < 1000; i++) {
curve_Fq2 e = curve_Fq2::random_element();
Fq2 e2(e);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << e2;
Fq2 e3;
ss >> e3;
curve_Fq2 e4 = e3.to_libsnark_fq2<curve_Fq2>();
ASSERT_TRUE(e == e4);
}
}
template<typename T>
T deserialize_tv(std::string s)
{
T e;
CDataStream ss(ParseHex(s), SER_NETWORK, PROTOCOL_VERSION);
ss >> e;
return e;
}
curve_Fq deserialize_fq(std::string s)
{
return deserialize_tv<Fq>(s).to_libsnark_fq<curve_Fq>();
}
curve_Fq2 deserialize_fq2(std::string s)
{
return deserialize_tv<Fq2>(s).to_libsnark_fq2<curve_Fq2>();
}
TEST(proofs, fq_valid)
{
curve_Fq e = deserialize_fq("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46");
ASSERT_TRUE(e == curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"));
ASSERT_TRUE(e != curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208581"));
curve_Fq e2 = deserialize_fq("30644e72e131a029b75045b68181585d97816a916871ca8d3c208c16d87cfd46");
ASSERT_TRUE(e2 == curve_Fq("21888242871839275222221885816603420866962577604863418715751138068690288573766"));
}
TEST(proofs, fq_invalid)
{
// Should not be able to deserialize the modulus
ASSERT_THROW(
deserialize_fq("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"),
std::logic_error
);
// Should not be able to deserialize the modulus plus one
ASSERT_THROW(
deserialize_fq("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd48"),
std::logic_error
);
// Should not be able to deserialize a ridiculously out of bound int
ASSERT_THROW(
deserialize_fq("ff644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"),
std::logic_error
);
}
TEST(proofs, fq2_valid)
{
// (q - 1) * q + q
curve_Fq2 e = deserialize_fq2("0925c4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b0");
ASSERT_TRUE(e.c0 == curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"));
ASSERT_TRUE(e.c1 == curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"));
curve_Fq2 e2 = deserialize_fq2("000000000000000000000000000000000000000000000000010245be1c91e3186bbbe1c430a93fcfc5aada4ab10c3492f70eea97a91c7b29554db55acffa34d2");
ASSERT_TRUE(e2.c0 == curve_Fq("238769481237490823"));
ASSERT_TRUE(e2.c1 == curve_Fq("384579238459723485"));
curve_Fq2 e3 = deserialize_fq2("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
ASSERT_TRUE(e3.c0 == curve_Fq("0"));
ASSERT_TRUE(e3.c1 == curve_Fq("0"));
curve_Fq2 e4 = deserialize_fq2("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
ASSERT_TRUE(e4.c0 == curve_Fq("1"));
ASSERT_TRUE(e4.c1 == curve_Fq("0"));
}
TEST(proofs, fq2_invalid)
{
// (q - 1) * q + q is invalid
ASSERT_THROW(
deserialize_fq2("0925c4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b1"),
std::logic_error
);
// q * q + (q - 1) is invalid
ASSERT_THROW(
deserialize_fq2("0925c4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d34cced085b43e2f202a05e52ef18233a3d8371be725c8b8e7774e4b8ffda66f7"),
std::logic_error
);
// Ridiculously out of bounds
ASSERT_THROW(
deserialize_fq2("0fffc4b8763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b0"),
std::logic_error
);
ASSERT_THROW(
deserialize_fq2("ffffffff763cbf9c599a6f7c0348d21cb00b85511637560626edfa5c34c6b38d04689e957a1242c84a50189c6d96cadca602072d09eac1013b5458a2275d69b0"),
std::logic_error
);
}
TEST(proofs, g1_serializes_properly)
{
// Cannot serialize zero
{
ASSERT_THROW({CompressedG1 g = CompressedG1(curve_G1::zero());}, std::domain_error);
}
for (size_t i = 0; i < 1000; i++) {
curve_G1 e = curve_G1::random_element();
CompressedG1 e2(e);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << e2;
CompressedG1 e3;
ss >> e3;
ASSERT_TRUE(e2 == e3);
curve_G1 e4 = e3.to_libsnark_g1<curve_G1>();
ASSERT_TRUE(e == e4);
}
}
TEST(proofs, g2_serializes_properly)
{
// Cannot serialize zero
{
ASSERT_THROW({CompressedG2 g = CompressedG2(curve_G2::zero());}, std::domain_error);
}
for (size_t i = 0; i < 1000; i++) {
curve_G2 e = curve_G2::random_element();
CompressedG2 e2(e);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << e2;
CompressedG2 e3;
ss >> e3;
ASSERT_TRUE(e2 == e3);
curve_G2 e4 = e3.to_libsnark_g2<curve_G2>();
ASSERT_TRUE(e == e4);
}
}
TEST(proofs, zksnark_serializes_properly)
{
auto example = libsnark::generate_r1cs_example_with_field_input<curve_Fr>(250, 4);
example.constraint_system.swap_AB_if_beneficial();
auto kp = libsnark::r1cs_ppzksnark_generator<curve_pp>(example.constraint_system);
auto vkprecomp = libsnark::r1cs_ppzksnark_verifier_process_vk(kp.vk);
for (size_t i = 0; i < 20; i++) {
auto badproof = ZCProof::random_invalid();
auto proof = badproof.to_libsnark_proof<libsnark::r1cs_ppzksnark_proof<curve_pp>>();
auto verifierEnabled = ProofVerifier::Strict();
auto verifierDisabled = ProofVerifier::Disabled();
// This verifier should catch the bad proof
ASSERT_FALSE(verifierEnabled.check(
kp.vk,
vkprecomp,
example.primary_input,
proof
));
// This verifier won't!
ASSERT_TRUE(verifierDisabled.check(
kp.vk,
vkprecomp,
example.primary_input,
proof
));
}
for (size_t i = 0; i < 20; i++) {
auto proof = libsnark::r1cs_ppzksnark_prover<curve_pp>(
kp.pk,
example.primary_input,
example.auxiliary_input,
example.constraint_system
);
{
auto verifierEnabled = ProofVerifier::Strict();
auto verifierDisabled = ProofVerifier::Disabled();
ASSERT_TRUE(verifierEnabled.check(
kp.vk,
vkprecomp,
example.primary_input,
proof
));
ASSERT_TRUE(verifierDisabled.check(
kp.vk,
vkprecomp,
example.primary_input,
proof
));
}
ASSERT_TRUE(libsnark::r1cs_ppzksnark_verifier_strong_IC<curve_pp>(
kp.vk,
example.primary_input,
proof
));
ZCProof compressed_proof_0(proof);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << compressed_proof_0;
ZCProof compressed_proof_1;
ss >> compressed_proof_1;
ASSERT_TRUE(compressed_proof_0 == compressed_proof_1);
auto newproof = compressed_proof_1.to_libsnark_proof<libsnark::r1cs_ppzksnark_proof<curve_pp>>();
ASSERT_TRUE(proof == newproof);
ASSERT_TRUE(libsnark::r1cs_ppzksnark_verifier_strong_IC<curve_pp>(
kp.vk,
example.primary_input,
newproof
));
}
}
TEST(proofs, g1_deserialization)
{
CompressedG1 g;
curve_G1 expected;
// Valid G1 element.
{
CDataStream ss(ParseHex("0230644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), SER_NETWORK, PROTOCOL_VERSION);
ss >> g;
expected.X = curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582");
expected.Y = curve_Fq("3969792565221544645472939191694882283483352126195956956354061729942568608776");
expected.Z = curve_Fq::one();
ASSERT_TRUE(g.to_libsnark_g1<curve_G1>() == expected);
}
// Its negation.
{
CDataStream ss(ParseHex("0330644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), SER_NETWORK, PROTOCOL_VERSION);
ss >> g;
expected.X = curve_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582");
expected.Y = curve_Fq("3969792565221544645472939191694882283483352126195956956354061729942568608776");
expected.Z = curve_Fq::one();
ASSERT_TRUE(g.to_libsnark_g1<curve_G1>() == -expected);
}
// Invalid leading bytes
{
CDataStream ss(ParseHex("ff30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46"), SER_NETWORK, PROTOCOL_VERSION);
ASSERT_THROW(ss >> g, std::ios_base::failure);
}
// Invalid point
{
CDataStream ss(ParseHex("0208c6d2adffacbc8438f09f321874ea66e2fcc29f8dcfec2caefa21ec8c96a77c"), SER_NETWORK, PROTOCOL_VERSION);
ss >> g;
ASSERT_THROW(g.to_libsnark_g1<curve_G1>(), std::runtime_error);
}
// Point with out of bounds Fq
{
CDataStream ss(ParseHex("02ffc6d2adffacbc8438f09f321874ea66e2fcc29f8dcfec2caefa21ec8c96a77c"), SER_NETWORK, PROTOCOL_VERSION);
ss >> g;
ASSERT_THROW(g.to_libsnark_g1<curve_G1>(), std::logic_error);
}
// Randomly produce valid G1 representations and fail/succeed to
// turn them into G1 points based on whether they are valid.
for (size_t i = 0; i < 5000; i++) {
curve_Fq e = curve_Fq::random_element();
CDataStream ss(ParseHex("02"), SER_NETWORK, PROTOCOL_VERSION);
ss << Fq(e);
CompressedG1 g;
ss >> g;
try {
curve_G1 g_real = g.to_libsnark_g1<curve_G1>();
} catch(...) {
}
}
}
TEST(proofs, g2_deserialization)
{
CompressedG2 g;
curve_G2 expected = curve_G2::random_element();
// Valid G2 point
{
CDataStream ss(ParseHex("0a023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION);
ss >> g;
expected.X = curve_Fq2(
curve_Fq("5923585509243758863255447226263146374209884951848029582715967108651637186684"),
curve_Fq("5336385337059958111259504403491065820971993066694750945459110579338490853570")
);
expected.Y = curve_Fq2(
curve_Fq("10374495865873200088116930399159835104695426846400310764827677226300185211748"),
curve_Fq("5256529835065685814318509161957442385362539991735248614869838648137856366932")
);
expected.Z = curve_Fq2::one();
ASSERT_TRUE(g.to_libsnark_g2<curve_G2>() == expected);
}
// Its negation
{
CDataStream ss(ParseHex("0b023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION);
ss >> g;
expected.X = curve_Fq2(
curve_Fq("5923585509243758863255447226263146374209884951848029582715967108651637186684"),
curve_Fq("5336385337059958111259504403491065820971993066694750945459110579338490853570")
);
expected.Y = curve_Fq2(
curve_Fq("10374495865873200088116930399159835104695426846400310764827677226300185211748"),
curve_Fq("5256529835065685814318509161957442385362539991735248614869838648137856366932")
);
expected.Z = curve_Fq2::one();
ASSERT_TRUE(g.to_libsnark_g2<curve_G2>() == -expected);
}
// Invalid leading bytes
{
CDataStream ss(ParseHex("ff023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION);
ASSERT_THROW(ss >> g, std::ios_base::failure);
}
// Invalid point
{
CDataStream ss(ParseHex("0b023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984b"), SER_NETWORK, PROTOCOL_VERSION);
ss >> g;
ASSERT_THROW(g.to_libsnark_g2<curve_G2>(), std::runtime_error);
}
// Point with out of bounds Fq2
{
CDataStream ss(ParseHex("0a0f3aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a"), SER_NETWORK, PROTOCOL_VERSION);
ss >> g;
ASSERT_THROW(g.to_libsnark_g2<curve_G2>(), std::logic_error);
}
// Randomly produce valid G2 representations and fail/succeed to
// turn them into G2 points based on whether they are valid.
for (size_t i = 0; i < 5000; i++) {
curve_Fq2 e = curve_Fq2::random_element();
CDataStream ss(ParseHex("0a"), SER_NETWORK, PROTOCOL_VERSION);
ss << Fq2(e);
CompressedG2 g;
ss >> g;
try {
curve_G2 g_real = g.to_libsnark_g2<curve_G2>();
} catch(...) {
}
}
}
#include "json_test_vectors.h"
#include "test/data/g1_compressed.json.h"
TEST(proofs, g1_test_vectors)
{
UniValue v = read_json(std::string(json_tests::g1_compressed, json_tests::g1_compressed + sizeof(json_tests::g1_compressed)));
curve_G1 e = curve_Fr("34958239045823") * curve_G1::one();
for (size_t i = 0; i < 10000; i++) {
e = (curve_Fr("34958239045823") ^ i) * e;
auto expected = CompressedG1(e);
expect_test_vector(v[i], expected);
ASSERT_TRUE(expected.to_libsnark_g1<curve_G1>() == e);
}
}
#include "test/data/g2_compressed.json.h"
TEST(proofs, g2_test_vectors)
{
UniValue v = read_json(std::string(json_tests::g2_compressed, json_tests::g2_compressed + sizeof(json_tests::g2_compressed)));
curve_G2 e = curve_Fr("34958239045823") * curve_G2::one();
for (size_t i = 0; i < 10000; i++) {
e = (curve_Fr("34958239045823") ^ i) * e;
auto expected = CompressedG2(e);
expect_test_vector(v[i], expected);
ASSERT_TRUE(expected.to_libsnark_g2<curve_G2>() == e);
}
}

35
src/gtest/test_random.cpp Normal file
View File

@ -0,0 +1,35 @@
#include <gtest/gtest.h>
#include "random.h"
extern int GenZero(int n);
extern int GenMax(int n);
TEST(Random, MappedShuffle) {
std::vector<int> a {8, 4, 6, 3, 5};
std::vector<int> m {0, 1, 2, 3, 4};
auto a1 = a;
auto m1 = m;
MappedShuffle(a1.begin(), m1.begin(), a1.size(), GenZero);
std::vector<int> ea1 {4, 6, 3, 5, 8};
std::vector<int> em1 {1, 2, 3, 4, 0};
EXPECT_EQ(ea1, a1);
EXPECT_EQ(em1, m1);
auto a2 = a;
auto m2 = m;
MappedShuffle(a2.begin(), m2.begin(), a2.size(), GenMax);
std::vector<int> ea2 {8, 4, 6, 3, 5};
std::vector<int> em2 {0, 1, 2, 3, 4};
EXPECT_EQ(ea2, a2);
EXPECT_EQ(em2, m2);
auto a3 = a;
auto m3 = m;
MappedShuffle(a3.begin(), m3.begin(), a3.size(), GenIdentity);
std::vector<int> ea3 {8, 4, 6, 3, 5};
std::vector<int> em3 {0, 1, 2, 3, 4};
EXPECT_EQ(ea3, a3);
EXPECT_EQ(em3, m3);
}

28
src/gtest/test_rpc.cpp Normal file
View File

@ -0,0 +1,28 @@
#include <gtest/gtest.h>
#include <univalue.h>
#include "chain.h"
#include "chainparams.h"
#include "clientversion.h"
#include "primitives/block.h"
#include "rpcserver.h"
#include "streams.h"
#include "utilstrencodings.h"
extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
TEST(rpc, check_blockToJSON_returns_minified_solution) {
SelectParams(CBaseChainParams::TESTNET);
// Testnet block 006a87f9f91c1f51c7549e2c8965c0fd4fe8c212798f932efc54dc7bccbec780
// Height 1391
CDataStream ss(ParseHex("0400000077be515306e347c6856686d83a229169140a2f7e17281c8319ecf00c49bb6f00994ca400914d6733295faf4e0063998e75a18aae7d39b5244d88d082c13145070000000000000000000000000000000000000000000000000000000000000000ae71c25700737b1f010090f8a62f53105d6b6f173d242fbbf54b0c1024a64520f0020e47fe710000fd4005009f44ff7505d789b964d6817734b8ce1377d456255994370d06e59ac99bd5791b6ad174a66fd71c70e60cfc7fd88243ffe06f80b1ad181625f210779c745524629448e25348a5fce4f346a1735e60fdf53e144c0157dbc47c700a21a236f1efb7ee75f65b8d9d9e29026cfd09048233175202b211b9a49de4ab46f1cac71b6ea57a686377bd612378746e70c61a659c9cd683269e9c2a5cbc1d19f1149345302bbd0a1e62bf4bab01e9caeea789a1519441a61b146de35a4cc75dbdf01029127e311ad5073e7e96397f47226a7df9df66b2086b70756db013bbaeb068260157014b2602fc7dc71336e1439c887d2742d9730b4e79b08ec7839c3e2a037ae1565d04e05e351bb3531e5ef42cf7b71ca1482a9205245dd41f4db0f71644f8bdb88e845558537c03834c06ac83f336651e54e2edfc12e15ea9b7ea2c074e6155654d44c4d3bd90d9511050e9ad87d170db01448e5be6f45419cd86008978db5e3ceab79890234f992648d69bf1053855387db646ccdee5575c65f81dd0f670b016d9f9a84707d91f77b862f697b8bb08365ba71fbe6bfa47af39155a75ebdcb1e5d69f59c40c9e3a64988c1ec26f7f5159eef5c244d504a9e46125948ecc389c2ec3028ac4ff39ffd66e7743970819272b21e0c2df75b308bc62896873952147e57ed79446db4cdb5a563e76ec4c25899d41128afb9a5f8fc8063621efb7a58b9dd666d30c73e318cdcf3393bfec200e160f500e645f7baac263db99fa4a7c1cb4fea219fc512193102034d379f244c21a81821301b8d47c90247713a3e902c762d7bafa6cdb744eeb6d3b50dd175599d02b6e9f5bbda59366e04862aa765135968426e7ac0116de7351940dc57c0ae451d63f667e39891bc81e09e6c76f6f8a7582f7447c6f5945f717b0e52a7e3dd0c6db4061362123cc53fd8ede4abed4865201dc4d8eb4e5d48baa565183b69a5304a44c0600bb24dcaeee9d95ceebd27c1b0a33e0b46f23797d7d7907300b2bb7d62ef2fc5aa139250c73930c621bb5f41fc235534ee8014dfaddd5245aeb01198420ba7b5c076545329c94d54fa725a8e807579f5f0cc9d98170598023268f5930893620190275e6b3c6f5181e36310a9a475208316911d78f917d724c5946c553b7ec042c563c540114b6b78bd4c6e808ee391a4a9d93e127032983c5b3708037b14aa604cfb034e7c8b0ffdd6936446fe80216178506a87402653a373926eeff66e704daf992a0a9a5c3ad80566c0339be9e5b8e35b3b3226b2f7767e20d992ea6c3d6e322eca37b0c7f7e60060802f5abcc1975841365cadbdc3867063addfc803766ae525375ecddee61f9df9ffcd20343c83ab82b0e91de039c59cb435c8d3159cc338b4901f40c9b5c27043bcf2bd5fa9b685b65c9ba5a1e11a51dd3f773051560341f9ec81d05bf259e2d4b7161f896fbb6812cfc924a32120b7367d5e40439e267adda6a1315bb0d6200ce6a503174c8d2a638ea6fd6b1f486d68db11bdca63c4f4a725d1ab6231ea875484e70b27d293c05803386924f283d4c12bb953474d92b7dd43d2d97193bd96281ebb63fa075d2f9ecd310c70ee1d97b5330bd8fb5791c5943ecf084e5f2c83915acac57519c46b166136068d6f9ec0dd598616e32c591128ce13705a283ca39d5b211409600e07b3713113374d9700207a45394eac5b3b7afc9b1b2bad7d89fd3f35f6b2413ce615ee7869b3569009403b96fdacdb32ef0a7e5229e2b666d51e95bdfb009b892e88bde70621a9b6509f068781392df4bdbc5723bb15071993f0d9a11575af5ff6ef85eaea39bc86805b35d8beee91b779354147f2d85304b8b49d053e7444fdd3deb9d16de331f2552af5b3be7766bb8f3f6a78c62148efb231f22680101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff05026f050101ffffffff02b03f250400000000232103885e6a80a5702046eb76c4702921b75858fc633df3cddff827cf7b3602e45cbdacec4f09010000000017a9146708e6670db0b950dac68031025cc5b63213a4918700000000"), SER_DISK, CLIENT_VERSION);
CBlock block;
ss >> block;
CBlockIndex index {block};
index.nHeight = 1391;
UniValue obj = blockToJSON(block, &index);
EXPECT_EQ("009f44ff7505d789b964d6817734b8ce1377d456255994370d06e59ac99bd5791b6ad174a66fd71c70e60cfc7fd88243ffe06f80b1ad181625f210779c745524629448e25348a5fce4f346a1735e60fdf53e144c0157dbc47c700a21a236f1efb7ee75f65b8d9d9e29026cfd09048233175202b211b9a49de4ab46f1cac71b6ea57a686377bd612378746e70c61a659c9cd683269e9c2a5cbc1d19f1149345302bbd0a1e62bf4bab01e9caeea789a1519441a61b146de35a4cc75dbdf01029127e311ad5073e7e96397f47226a7df9df66b2086b70756db013bbaeb068260157014b2602fc7dc71336e1439c887d2742d9730b4e79b08ec7839c3e2a037ae1565d04e05e351bb3531e5ef42cf7b71ca1482a9205245dd41f4db0f71644f8bdb88e845558537c03834c06ac83f336651e54e2edfc12e15ea9b7ea2c074e6155654d44c4d3bd90d9511050e9ad87d170db01448e5be6f45419cd86008978db5e3ceab79890234f992648d69bf1053855387db646ccdee5575c65f81dd0f670b016d9f9a84707d91f77b862f697b8bb08365ba71fbe6bfa47af39155a75ebdcb1e5d69f59c40c9e3a64988c1ec26f7f5159eef5c244d504a9e46125948ecc389c2ec3028ac4ff39ffd66e7743970819272b21e0c2df75b308bc62896873952147e57ed79446db4cdb5a563e76ec4c25899d41128afb9a5f8fc8063621efb7a58b9dd666d30c73e318cdcf3393bfec200e160f500e645f7baac263db99fa4a7c1cb4fea219fc512193102034d379f244c21a81821301b8d47c90247713a3e902c762d7bafa6cdb744eeb6d3b50dd175599d02b6e9f5bbda59366e04862aa765135968426e7ac0116de7351940dc57c0ae451d63f667e39891bc81e09e6c76f6f8a7582f7447c6f5945f717b0e52a7e3dd0c6db4061362123cc53fd8ede4abed4865201dc4d8eb4e5d48baa565183b69a5304a44c0600bb24dcaeee9d95ceebd27c1b0a33e0b46f23797d7d7907300b2bb7d62ef2fc5aa139250c73930c621bb5f41fc235534ee8014dfaddd5245aeb01198420ba7b5c076545329c94d54fa725a8e807579f5f0cc9d98170598023268f5930893620190275e6b3c6f5181e36310a9a475208316911d78f917d724c5946c553b7ec042c563c540114b6b78bd4c6e808ee391a4a9d93e127032983c5b3708037b14aa604cfb034e7c8b0ffdd6936446fe80216178506a87402653a373926eeff66e704daf992a0a9a5c3ad80566c0339be9e5b8e35b3b3226b2f7767e20d992ea6c3d6e322eca37b0c7f7e60060802f5abcc1975841365cadbdc3867063addfc803766ae525375ecddee61f9df9ffcd20343c83ab82b0e91de039c59cb435c8d3159cc338b4901f40c9b5c27043bcf2bd5fa9b685b65c9ba5a1e11a51dd3f773051560341f9ec81d05bf259e2d4b7161f896fbb6812cfc924a32120b7367d5e40439e267adda6a1315bb0d6200ce6a503174c8d2a638ea6fd6b1f486d68db11bdca63c4f4a725d1ab6231ea875484e70b27d293c05803386924f283d4c12bb953474d92b7dd43d2d97193bd96281ebb63fa075d2f9ecd310c70ee1d97b5330bd8fb5791c5943ecf084e5f2c83915acac57519c46b166136068d6f9ec0dd598616e32c591128ce13705a283ca39d5b211409600e07b3713113374d9700207a45394eac5b3b7afc9b1b2bad7d89fd3f35f6b2413ce615ee7869b3569009403b96fdacdb32ef0a7e5229e2b666d51e95bdfb009b892e88bde70621a9b6509f068781392df4bdbc5723bb15071993f0d9a11575af5ff6ef85eaea39bc86805b35d8beee91b779354147f2d85304b8b49d053e7444fdd3deb9d16de331f2552af5b3be7766bb8f3f6a78c62148efb231f2268", find_value(obj, "solution").get_str());
}

View File

@ -0,0 +1,10 @@
#include <gtest/gtest.h>
TEST(tautologies, seven_eq_seven) {
ASSERT_EQ(7, 7);
}
TEST(tautologies, DISABLED_ObviousFailure)
{
FAIL() << "This is expected";
}

View File

@ -0,0 +1,85 @@
#include <gtest/gtest.h>
#include "primitives/transaction.h"
#include "zcash/Note.hpp"
#include "zcash/Address.hpp"
extern ZCJoinSplit* params;
extern int GenZero(int n);
extern int GenMax(int n);
TEST(Transaction, JSDescriptionRandomized) {
// construct a merkle tree
ZCIncrementalMerkleTree merkleTree;
libzcash::SpendingKey k = libzcash::SpendingKey::random();
libzcash::PaymentAddress addr = k.address();
libzcash::Note note(addr.a_pk, 100, uint256(), uint256());
// commitment from coin
uint256 commitment = note.cm();
// insert commitment into the merkle tree
merkleTree.append(commitment);
// compute the merkle root we will be working with
uint256 rt = merkleTree.root();
auto witness = merkleTree.witness();
// create JSDescription
uint256 pubKeyHash;
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
libzcash::JSInput(witness, note, k),
libzcash::JSInput() // dummy input of zero value
};
boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
libzcash::JSOutput(addr, 50),
libzcash::JSOutput(addr, 50)
};
boost::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
boost::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
{
auto jsdesc = JSDescription::Randomized(
*params, pubKeyHash, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false);
std::set<size_t> inputSet(inputMap.begin(), inputMap.end());
std::set<size_t> expectedInputSet {0, 1};
EXPECT_EQ(expectedInputSet, inputSet);
std::set<size_t> outputSet(outputMap.begin(), outputMap.end());
std::set<size_t> expectedOutputSet {0, 1};
EXPECT_EQ(expectedOutputSet, outputSet);
}
{
auto jsdesc = JSDescription::Randomized(
*params, pubKeyHash, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false, nullptr, GenZero);
boost::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {1, 0};
boost::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {1, 0};
EXPECT_EQ(expectedInputMap, inputMap);
EXPECT_EQ(expectedOutputMap, outputMap);
}
{
auto jsdesc = JSDescription::Randomized(
*params, pubKeyHash, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false, nullptr, GenMax);
boost::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {0, 1};
boost::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {0, 1};
EXPECT_EQ(expectedInputMap, inputMap);
EXPECT_EQ(expectedOutputMap, outputMap);
}
}

34
src/gtest/test_txid.cpp Normal file
View File

@ -0,0 +1,34 @@
#include <gtest/gtest.h>
#include "primitives/transaction.h"
#include "clientversion.h"
#include "serialize.h"
#include "streams.h"
#include "uint256.h"
#include "util.h"
#include "utilstrencodings.h"
/*
Test that removing #1144 succeeded by verifying the hash of a transaction is over the entire serialized form.
*/
TEST(txid_tests, check_txid_and_hash_are_same) {
// Random zcash transaction aacaa62d40fcdd9192ed35ea9df31660ccf7f6c60566530faaa444fb5d0d410e
CTransaction tx;
CDataStream stream(ParseHex("01000000015ad78be5497476bbf84869d8156761ca850b6e82e48ad1315069a3726516a3d1010000006b483045022100ba5e90204e83c5f961b67c6232c1cc6c360afd36d43fcfae0de7af2e75f4cda7022012fec415a12048dbb70511fda6195b090b56735232281dc1144409833a092edc012102c322382e17c9ed4f47183f219cc5dd7853f939fb8eebae3c943622e0abf8d5e5feffffff0280969800000000001976a91430271a250e92135ce0db0783ebb63aaeb58e47f988acd694693a000000001976a9145f0d00adba6489150808feb4108d7be582cbb2e188ac0a000000"), SER_DISK, CLIENT_VERSION);
stream >> tx;
uint256 hash = uint256S("aacaa62d40fcdd9192ed35ea9df31660ccf7f6c60566530faaa444fb5d0d410e");
ASSERT_TRUE(hash == tx.GetHash());
}
TEST(txid_tests, check_txid_and_hash_are_same_coinbase) {
// Random zcash coinbase transaction 6041357de59ba64959d1b60f93de24dfe5ea1e26ed9e8a73d35b225a1845bad5
CTransaction tx;
CDataStream stream(ParseHex("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03530101ffffffff02f049020000000000232102f2df427fd4f76552ed0e86b17da8cf9b07754ea23ddc05799410a4a3a5d806ccac7c9200000000000017a9146708e6670db0b950dac68031025cc5b63213a4918700000000"), SER_DISK, CLIENT_VERSION);
stream >> tx;
ASSERT_TRUE(tx.IsCoinBase());
uint256 hash = uint256S("6041357de59ba64959d1b60f93de24dfe5ea1e26ed9e8a73d35b225a1845bad5");
ASSERT_TRUE(hash == tx.GetHash());
}

173
src/gtest/test_upgrades.cpp Normal file
View File

@ -0,0 +1,173 @@
#include <gtest/gtest.h>
#include "chainparams.h"
#include "consensus/upgrades.h"
#include <boost/optional.hpp>
class UpgradesTest : public ::testing::Test {
protected:
virtual void SetUp() {
}
virtual void TearDown() {
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
};
TEST_F(UpgradesTest, NetworkUpgradeState) {
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus();
// Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT
EXPECT_EQ(
NetworkUpgradeState(0, params, Consensus::UPGRADE_TESTDUMMY),
UPGRADE_DISABLED);
EXPECT_EQ(
NetworkUpgradeState(1000000, params, Consensus::UPGRADE_TESTDUMMY),
UPGRADE_DISABLED);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
EXPECT_EQ(
NetworkUpgradeState(0, params, Consensus::UPGRADE_TESTDUMMY),
UPGRADE_ACTIVE);
EXPECT_EQ(
NetworkUpgradeState(1000000, params, Consensus::UPGRADE_TESTDUMMY),
UPGRADE_ACTIVE);
int nActivationHeight = 100;
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight);
EXPECT_EQ(
NetworkUpgradeState(0, params, Consensus::UPGRADE_TESTDUMMY),
UPGRADE_PENDING);
EXPECT_EQ(
NetworkUpgradeState(nActivationHeight - 1, params, Consensus::UPGRADE_TESTDUMMY),
UPGRADE_PENDING);
EXPECT_EQ(
NetworkUpgradeState(nActivationHeight, params, Consensus::UPGRADE_TESTDUMMY),
UPGRADE_ACTIVE);
EXPECT_EQ(
NetworkUpgradeState(1000000, params, Consensus::UPGRADE_TESTDUMMY),
UPGRADE_ACTIVE);
}
TEST_F(UpgradesTest, CurrentEpoch) {
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus();
auto nBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_TESTDUMMY].nBranchId;
// Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT
EXPECT_EQ(CurrentEpoch(0, params), Consensus::BASE_SPROUT);
EXPECT_EQ(CurrentEpochBranchId(0, params), 0);
EXPECT_EQ(CurrentEpoch(1000000, params), Consensus::BASE_SPROUT);
EXPECT_EQ(CurrentEpochBranchId(1000000, params), 0);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
EXPECT_EQ(CurrentEpoch(0, params), Consensus::UPGRADE_TESTDUMMY);
EXPECT_EQ(CurrentEpochBranchId(0, params), nBranchId);
EXPECT_EQ(CurrentEpoch(1000000, params), Consensus::UPGRADE_TESTDUMMY);
EXPECT_EQ(CurrentEpochBranchId(1000000, params), nBranchId);
int nActivationHeight = 100;
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight);
EXPECT_EQ(CurrentEpoch(0, params), Consensus::BASE_SPROUT);
EXPECT_EQ(CurrentEpochBranchId(0, params), 0);
EXPECT_EQ(CurrentEpoch(nActivationHeight - 1, params), Consensus::BASE_SPROUT);
EXPECT_EQ(CurrentEpochBranchId(nActivationHeight - 1, params), 0);
EXPECT_EQ(CurrentEpoch(nActivationHeight, params), Consensus::UPGRADE_TESTDUMMY);
EXPECT_EQ(CurrentEpochBranchId(nActivationHeight, params), nBranchId);
EXPECT_EQ(CurrentEpoch(1000000, params), Consensus::UPGRADE_TESTDUMMY);
EXPECT_EQ(CurrentEpochBranchId(1000000, params), nBranchId);
}
TEST_F(UpgradesTest, IsActivationHeight) {
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus();
// Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT
EXPECT_FALSE(IsActivationHeight(-1, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(0, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(1, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(1000000, params, Consensus::UPGRADE_TESTDUMMY));
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
EXPECT_FALSE(IsActivationHeight(-1, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_TRUE(IsActivationHeight(0, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(1, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(1000000, params, Consensus::UPGRADE_TESTDUMMY));
int nActivationHeight = 100;
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight);
EXPECT_FALSE(IsActivationHeight(-1, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(0, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(1, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(nActivationHeight - 1, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_TRUE(IsActivationHeight(nActivationHeight, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(nActivationHeight + 1, params, Consensus::UPGRADE_TESTDUMMY));
EXPECT_FALSE(IsActivationHeight(1000000, params, Consensus::UPGRADE_TESTDUMMY));
}
TEST_F(UpgradesTest, IsActivationHeightForAnyUpgrade) {
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus();
// Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(-1, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(0, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1000000, params));
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(-1, params));
EXPECT_TRUE(IsActivationHeightForAnyUpgrade(0, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1000000, params));
int nActivationHeight = 100;
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight);
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(-1, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(0, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(nActivationHeight - 1, params));
EXPECT_TRUE(IsActivationHeightForAnyUpgrade(nActivationHeight, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(nActivationHeight + 1, params));
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1000000, params));
}
TEST_F(UpgradesTest, NextActivationHeight) {
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus();
// Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT
EXPECT_EQ(NextActivationHeight(-1, params), boost::none);
EXPECT_EQ(NextActivationHeight(0, params), boost::none);
EXPECT_EQ(NextActivationHeight(1, params), boost::none);
EXPECT_EQ(NextActivationHeight(1000000, params), boost::none);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
EXPECT_EQ(NextActivationHeight(-1, params), boost::none);
EXPECT_EQ(NextActivationHeight(0, params), boost::none);
EXPECT_EQ(NextActivationHeight(1, params), boost::none);
EXPECT_EQ(NextActivationHeight(1000000, params), boost::none);
int nActivationHeight = 100;
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight);
EXPECT_EQ(NextActivationHeight(-1, params), boost::none);
EXPECT_EQ(NextActivationHeight(0, params), nActivationHeight);
EXPECT_EQ(NextActivationHeight(1, params), nActivationHeight);
EXPECT_EQ(NextActivationHeight(nActivationHeight - 1, params), nActivationHeight);
EXPECT_EQ(NextActivationHeight(nActivationHeight, params), boost::none);
EXPECT_EQ(NextActivationHeight(nActivationHeight + 1, params), boost::none);
EXPECT_EQ(NextActivationHeight(1000000, params), boost::none);
}

View File

@ -0,0 +1,152 @@
#include <gtest/gtest.h>
#include "consensus/upgrades.h"
#include "consensus/validation.h"
#include "main.h"
#include "utiltest.h"
extern ZCJoinSplit* params;
extern bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos);
void ExpectOptionalAmount(CAmount expected, boost::optional<CAmount> actual) {
EXPECT_TRUE((bool)actual);
if (actual) {
EXPECT_EQ(expected, *actual);
}
}
// Fake an empty view
class FakeCoinsViewDB : public CCoinsView {
public:
FakeCoinsViewDB() {}
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
return false;
}
bool GetNullifier(const uint256 &nf) const {
return false;
}
bool GetCoins(const uint256 &txid, CCoins &coins) const {
return false;
}
bool HaveCoins(const uint256 &txid) const {
return false;
}
uint256 GetBestBlock() const {
uint256 a;
return a;
}
uint256 GetBestAnchor() const {
uint256 a;
return a;
}
bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors,
CNullifiersMap &mapNullifiers) {
return false;
}
bool GetStats(CCoinsStats &stats) const {
return false;
}
};
TEST(Validation, ContextualCheckInputsPassesWithCoinbase) {
// Create fake coinbase transaction
CMutableTransaction mtx;
mtx.vin.resize(1);
CTransaction tx(mtx);
ASSERT_TRUE(tx.IsCoinBase());
// Fake an empty view
FakeCoinsViewDB fakeDB;
CCoinsViewCache view(&fakeDB);
auto consensusBranchId = SPROUT_BRANCH_ID;
CValidationState state;
PrecomputedTransactionData txdata(tx);
EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, txdata, Params(CBaseChainParams::MAIN).GetConsensus(), consensusBranchId));
}
TEST(Validation, ReceivedBlockTransactions) {
auto sk = libzcash::SpendingKey::random();
// Create a fake genesis block
CBlock block1;
block1.vtx.push_back(GetValidReceive(*params, sk, 5, true));
block1.hashMerkleRoot = block1.BuildMerkleTree();
CBlockIndex fakeIndex1 {block1};
// Create a fake child block
CBlock block2;
block2.hashPrevBlock = block1.GetHash();
block2.vtx.push_back(GetValidReceive(*params, sk, 10, true));
block2.hashMerkleRoot = block2.BuildMerkleTree();
CBlockIndex fakeIndex2 {block2};
fakeIndex2.pprev = &fakeIndex1;
CDiskBlockPos pos1;
CDiskBlockPos pos2;
// Set initial state of indices
ASSERT_TRUE(fakeIndex1.RaiseValidity(BLOCK_VALID_TREE));
ASSERT_TRUE(fakeIndex2.RaiseValidity(BLOCK_VALID_TREE));
EXPECT_TRUE(fakeIndex1.IsValid(BLOCK_VALID_TREE));
EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TREE));
EXPECT_FALSE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS));
EXPECT_FALSE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS));
// Sprout pool values should not be set
EXPECT_FALSE((bool)fakeIndex1.nSproutValue);
EXPECT_FALSE((bool)fakeIndex1.nChainSproutValue);
EXPECT_FALSE((bool)fakeIndex2.nSproutValue);
EXPECT_FALSE((bool)fakeIndex2.nChainSproutValue);
// Mark the second block's transactions as received first
CValidationState state;
EXPECT_TRUE(ReceivedBlockTransactions(block2, state, &fakeIndex2, pos2));
EXPECT_FALSE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS));
EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS));
// Sprout pool value delta should now be set for the second block,
// but not any chain totals
EXPECT_FALSE((bool)fakeIndex1.nSproutValue);
EXPECT_FALSE((bool)fakeIndex1.nChainSproutValue);
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(20, fakeIndex2.nSproutValue);
}
EXPECT_FALSE((bool)fakeIndex2.nChainSproutValue);
// Now mark the first block's transactions as received
EXPECT_TRUE(ReceivedBlockTransactions(block1, state, &fakeIndex1, pos1));
EXPECT_TRUE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS));
EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS));
// Sprout pool values should now be set for both blocks
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(10, fakeIndex1.nSproutValue);
}
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(10, fakeIndex1.nChainSproutValue);
}
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(20, fakeIndex2.nSproutValue);
}
{
SCOPED_TRACE("ExpectOptionalAmount call");
ExpectOptionalAmount(30, fakeIndex2.nChainSproutValue);
}
}

9
src/gtest/utils.cpp Normal file
View File

@ -0,0 +1,9 @@
int GenZero(int n)
{
return 0;
}
int GenMax(int n)
{
return n-1;
}

282
src/snark/Makefile Normal file
View File

@ -0,0 +1,282 @@
#********************************************************************************
# Makefile for the libsnark library.
#********************************************************************************
#* @author This file is part of libsnark, developed by SCIPR Lab
#* and contributors (see AUTHORS).
#* @copyright MIT license (see LICENSE file)
#*******************************************************************************/
# To override these, use "make OPTFLAGS=..." etc.
CURVE = BN128
OPTFLAGS = -O2 -march=native -mtune=native
FEATUREFLAGS = -DUSE_ASM -DMONTGOMERY_OUTPUT
# Initialize this using "CXXFLAGS=... make". The makefile appends to that.
CXXFLAGS += -std=c++11 -Wall -Wextra -Wno-unused-parameter -Wno-comment -Wfatal-errors $(OPTFLAGS) $(FEATUREFLAGS) -DCURVE_$(CURVE)
DEPSRC = depsrc
DEPINST = depinst
CXXFLAGS += -I$(DEPINST)/include -Ilibsnark
LDFLAGS += -L$(DEPINST)/lib -Wl,-rpath,$(DEPINST)/lib
LDLIBS += -lgmpxx -lgmp -lboost_program_options -lsodium
# List of .a files to include within libsnark.a and libsnark.so:
AR_LIBS =
# List of library files to install:
INSTALL_LIBS = $(LIB_FILE)
# Sentinel file to check existence of this directory (since directories don't work as a Make dependency):
DEPINST_EXISTS = $(DEPINST)/.exists
ifneq ($(NO_GTEST),1)
# Compile GTest from sourcecode if we can (e.g., Ubuntu). Otherwise use precompiled one (e.g., Fedora).
# See https://github.com/google/googletest/blob/master/googletest/docs/FAQ.md#why-is-it-not-recommended-to-install-a-pre-compiled-copy-of-google-test-for-example-into-usrlocal
ifneq ($(NO_COMPILE_LIBGTEST),1)
GTESTDIR=/usr/src/gtest
COMPILE_LIBGTEST = $(shell test -d $(GTESTDIR) && echo -n 1)
endif
GTEST_LDLIBS += -lgtest -lpthread
endif
ifneq ($(NO_SUPERCOP),1)
SUPERCOP_LDLIBS += -lsupercop
INSTALL_LIBS += depinst/lib/libsupercop.a
# Would have been nicer to roll supercop into libsnark.a ("AR_LIBS += $(DEPINST)/lib/libsupercop.a"), but it doesn't support position-independent code (libsnark issue #20).
endif
LIB_SRCS = \
libsnark/algebra/curves/alt_bn128/alt_bn128_g1.cpp \
libsnark/algebra/curves/alt_bn128/alt_bn128_g2.cpp \
libsnark/algebra/curves/alt_bn128/alt_bn128_init.cpp \
libsnark/algebra/curves/alt_bn128/alt_bn128_pairing.cpp \
libsnark/algebra/curves/alt_bn128/alt_bn128_pp.cpp \
libsnark/common/profiling.cpp \
libsnark/common/utils.cpp \
libsnark/gadgetlib1/constraint_profiling.cpp \
ifeq ($(CURVE),BN128)
LIB_SRCS += \
libsnark/algebra/curves/bn128/bn128_g1.cpp \
libsnark/algebra/curves/bn128/bn128_g2.cpp \
libsnark/algebra/curves/bn128/bn128_gt.cpp \
libsnark/algebra/curves/bn128/bn128_init.cpp \
libsnark/algebra/curves/bn128/bn128_pairing.cpp \
libsnark/algebra/curves/bn128/bn128_pp.cpp
CXXFLAGS += -DBN_SUPPORT_SNARK
AR_LIBS += $(DEPINST)/lib/libzm.a
endif
# FIXME: most of these are broken due to removed code.
DISABLED_EXECUTABLES = \
libsnark/common/routing_algorithms/profiling/profile_routing_algorithms \
libsnark/common/routing_algorithms/tests/test_routing_algorithms \
libsnark/gadgetlib1/gadgets/cpu_checkers/fooram/examples/test_fooram \
libsnark/gadgetlib1/gadgets/hashes/knapsack/tests/test_knapsack_gadget \
libsnark/gadgetlib1/gadgets/routing/profiling/profile_routing_gadgets \
libsnark/gadgetlib1/gadgets/set_commitment/tests/test_set_commitment_gadget \
libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget \
libsnark/reductions/ram_to_r1cs/examples/demo_arithmetization \
libsnark/relations/arithmetic_programs/ssp/tests/test_ssp \
libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_mp_ppzkpcd/profiling/profile_r1cs_mp_ppzkpcd \
libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_mp_ppzkpcd/tests/test_r1cs_mp_ppzkpcd \
libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_sp_ppzkpcd/profiling/profile_r1cs_sp_ppzkpcd \
libsnark/zk_proof_systems/pcd/r1cs_pcd/r1cs_sp_ppzkpcd/tests/test_r1cs_sp_ppzkpcd \
libsnark/zk_proof_systems/ppzksnark/bacs_ppzksnark/profiling/profile_bacs_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/bacs_ppzksnark/tests/test_bacs_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/profiling/profile_r1cs_gg_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/tests/test_r1cs_gg_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/profiling/profile_r1cs_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark_generator \
libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark_prover \
libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/examples/demo_ram_ppzksnark_verifier \
libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/profiling/profile_ram_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/ram_ppzksnark/tests/test_ram_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/tbcs_ppzksnark/profiling/profile_tbcs_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/tbcs_ppzksnark/tests/test_tbcs_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/uscs_ppzksnark/profiling/profile_uscs_ppzksnark \
libsnark/zk_proof_systems/ppzksnark/uscs_ppzksnark/tests/test_uscs_ppzksnark \
libsnark/zk_proof_systems/zksnark/ram_zksnark/profiling/profile_ram_zksnark \
libsnark/zk_proof_systems/zksnark/ram_zksnark/tests/test_ram_zksnark
EXECUTABLES =
EXECUTABLES_WITH_GTEST =
EXECUTABLES_WITH_SUPERCOP = \
libsnark/zk_proof_systems/ppzkadsnark/r1cs_ppzkadsnark/examples/demo_r1cs_ppzkadsnark
GTEST_TESTS = libsnark/gtests
GTEST_SRCS = \
libsnark/algebra/curves/tests/test_bilinearity.cpp \
libsnark/algebra/curves/tests/test_groups.cpp \
libsnark/algebra/fields/tests/test_bigint.cpp \
libsnark/algebra/fields/tests/test_fields.cpp \
libsnark/gadgetlib1/gadgets/hashes/sha256/tests/test_sha256_gadget.cpp \
libsnark/gadgetlib1/gadgets/merkle_tree/tests/test_merkle_tree_gadgets.cpp \
libsnark/relations/arithmetic_programs/qap/tests/test_qap.cpp \
libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp \
libsnark/gtests.cpp
DOCS = README.html
LIBSNARK_A = libsnark.a
# For documentation of the following options, see README.md .
ifeq ($(NO_PROCPS),1)
CXXFLAGS += -DNO_PROCPS
else
LDLIBS += -lprocps
endif
ifeq ($(LOWMEM),1)
CXXFLAGS += -DLOWMEM
endif
ifeq ($(PROFILE_OP_COUNTS),1)
STATIC = 1
CXXFLAGS += -DPROFILE_OP_COUNTS
endif
ifeq ($(STATIC),1)
CXXFLAGS += -static -DSTATIC
else
CXXFLAGS += -fPIC
endif
ifeq ($(MULTICORE),1)
CXXFLAGS += -DMULTICORE -fopenmp
endif
ifeq ($(CPPDEBUG),1)
CXXFLAGS += -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
DEBUG = 1
endif
ifeq ($(DEBUG),1)
CXXFLAGS += -DDEBUG -ggdb3
endif
ifeq ($(PERFORMANCE),1)
OPTFLAGS = -O3 -march=native -mtune=native
CXXFLAGS += -DNDEBUG
# Enable link-time optimization:
CXXFLAGS += -flto -fuse-linker-plugin
LDFLAGS += -flto
endif
LIB_OBJS =$(patsubst %.cpp,%.o,$(LIB_SRCS))
EXEC_OBJS =$(patsubst %,%.o,$(EXECUTABLES) $(EXECUTABLES_WITH_GTEST) $(EXECUTABLES_WITH_SUPERCOP))
GTEST_OBJS =$(patsubst %.cpp,%.o,$(GTEST_SRCS))
all: \
$(if $(NO_GTEST),,$(EXECUTABLES_WITH_GTEST) $(GTEST_TESTS)) \
$(if $(NO_SUPERCOP),,$(EXECUTABLES_WITH_SUPERCOP)) \
$(EXECUTABLES) \
$(if $(NO_DOCS),,doc)
doc: $(DOCS)
$(DEPINST_EXISTS):
# Create placeholder directories for installed dependencies. Some make settings (including the default) require actually running ./prepare-depends.sh to populate this directory.
mkdir -p $(DEPINST)/lib $(DEPINST)/include
touch $@
# In order to detect changes to #include dependencies. -MMD below generates a .d file for each .o file. Include the .d file.
-include $(patsubst %.o,%.d, $(LIB_OBJS) $(GTEST_OBJS) $(EXEC_OBJS) )
$(LIB_OBJS) $(if $(NO_GTEST),,$(GTEST_OBJS)) $(EXEC_OBJS): %.o: %.cpp
$(CXX) -o $@ $< -c -MMD $(CXXFLAGS)
LIBGTEST_A = $(DEPINST)/lib/libgtest.a
$(LIBGTEST_A): $(GTESTDIR)/libsnark/gtest-all.cc $(DEPINST_EXISTS)
$(CXX) -o $(DEPINST)/lib/gtest-all.o -I $(GTESTDIR) -c -isystem $(GTESTDIR)/include $< $(CXXFLAGS)
$(AR) -rv $(LIBGTEST_A) $(DEPINST)/lib/gtest-all.o
# libsnark.a will contains all of our relevant object files, and we also mash in the .a files of relevant dependencies built by ./prepare-depends.sh
$(LIBSNARK_A): $(LIB_OBJS) $(AR_LIBS)
$(AR) q $(LIBSNARK_A) $(LIB_OBJS)
if [ -n "$(AR_LIBS)" ]; then mkdir -p tmp-ar; cd tmp-ar; for AR_LIB in $(AR_LIBS); do $(AR) x $$AR_LIB; done; $(AR) qc $(LIBSNARK_A) tmp-ar/*; cd ..; rm -r tmp-ar; fi;
$(AR) s $(LIBSNARK_A)
libsnark.so: $(LIBSNARK_A) $(DEPINST_EXISTS)
$(CXX) -o $@ --shared -Wl,--whole-archive $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) -Wl,--no-whole-archive $(LDLIBS)
libsnark/gadgetlib2/tests/gadgetlib2_test: \
libsnark/gadgetlib2/tests/adapters_UTEST.cpp \
libsnark/gadgetlib2/tests/constraint_UTEST.cpp \
libsnark/gadgetlib2/tests/gadget_UTEST.cpp \
libsnark/gadgetlib2/tests/integration_UTEST.cpp \
libsnark/gadgetlib2/tests/protoboard_UTEST.cpp \
libsnark/gadgetlib2/tests/variable_UTEST.cpp
$(EXECUTABLES): %: %.o $(LIBSNARK_A) $(DEPINST_EXISTS)
$(CXX) -o $@ $@.o $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS)
$(EXECUTABLES_WITH_GTEST): %: %.o $(LIBSNARK_A) $(if $(COMPILE_LIBGTEST),$(LIBGTEST_A)) $(DEPINST_EXISTS)
$(CXX) -o $@ $@.o $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) $(GTEST_LDLIBS) $(LDLIBS)
$(EXECUTABLES_WITH_SUPERCOP): %: %.o $(LIBSNARK_A) $(DEPINST_EXISTS)
$(CXX) -o $@ $@.o $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) $(SUPERCOP_LDLIBS) $(LDLIBS)
$(GTEST_TESTS): %: $(GTEST_OBJS) $(LIBSNARK_A) $(if $(COMPILE_LIBGTEST),$(LIBGTEST_A)) $(DEPINST_EXISTS)
$(CXX) -o $@ $(GTEST_OBJS) $(LIBSNARK_A) $(CXXFLAGS) $(LDFLAGS) $(GTEST_LDLIBS) $(LDLIBS)
ifeq ($(STATIC),1)
LIB_FILE = $(LIBSNARK_A)
else
LIB_FILE = libsnark.so
endif
lib: $(LIB_FILE)
$(DOCS): %.html: %.md
markdown_py -f $@ $^ -x toc -x extra --noisy
# TODO: Would be nice to enable "-x smartypants" but Ubuntu 12.04 doesn't support that.
# TODO: switch to redcarpet, to produce same output as GitHub's processing of README.md. But what about TOC?
ifeq ($(PREFIX),)
install:
$(error Please provide PREFIX. E.g. make install PREFIX=/usr)
else
HEADERS_SRC=$(shell find libsnark -name '*.hpp' -o -name '*.tcc')
HEADERS_DEST=$(patsubst libsnark/%,$(PREFIX)/include/libsnark/%,$(HEADERS_SRC))
$(HEADERS_DEST): $(PREFIX)/include/libsnark/%: libsnark/%
mkdir -p $(shell dirname $@)
cp $< $@
install: $(INSTALL_LIBS) $(HEADERS_DEST) $(DEPINST_EXISTS)
mkdir -p $(PREFIX)/lib
cp -v $(INSTALL_LIBS) $(PREFIX)/lib/
ifneq ($(NO_COPY_DEPINST),1)
cp -rv $(DEPINST)/include $(PREFIX)
endif
endif
check: $(GTEST_TESTS)
$(GTEST_TESTS)
doxy:
doxygen doxygen.conf
# Clean generated files, except locally-compiled dependencies
clean:
$(RM) \
$(LIB_OBJS) $(GTEST_OBJS) $(EXEC_OBJS) \
$(EXECUTABLES) $(EXECUTABLES_WITH_GTEST) $(EXECUTABLES_WITH_SUPERCOP) $(GTEST_TESTS) \
$(DOCS) \
${patsubst %.o,%.d,${LIB_OBJS} ${GTEST_OBJS} ${EXEC_OBJS}} \
libsnark.so $(LIBSNARK_A) \
$(RM) -fr doxygen/ \
$(RM) $(LIBGTEST_A) $(DEPINST)/lib/gtest-all.o
# Clean all, including locally-compiled dependencies
clean-all: clean
$(RM) -fr $(DEPSRC) $(DEPINST)
.PHONY: all clean clean-all doc doxy lib install