diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index a025e66aca0..a1592004001 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -31,7 +31,9 @@ Run `zcashd --version` to find out - Disk size: - Disk Type (HD/SDD): - Linux kernel version (uname -a): -- Compiler version (gcc -version): +- Compiler version (gcc --version): +- Linker version (ld -v): +- Assembler version (as --version): ### Any extra information that might be useful in the debugging process. This includes the relevant contents of `~/.zcash/debug.log`. You can paste raw text, attach the file directly in the issue or link to the text via a pastebin type site. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..18694855b37 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ +Please ensure this checklist is followed for any pull requests for this repo. This checklist must be checked by both the PR creator and by anyone who reviews the PR. +* [ ] Relevant documentation for this PR has to be completed and reviewed by the documentation team (@ioptio, @mdr0id, or @Eirik0) before the PR can be merged +* [ ] A test plan for the PR must be documented in the PR notes and included in the test plan for the next regular release + +As a note, all buildbot tests need to be passing and all appropriate code reviews need to be done before this PR can be merged diff --git a/README.md b/README.md index 89abbf07212..c18da9a0918 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -VoteCoin 2.0.4 +VoteCoin 2.0.6 ============== What is VoteCoin? diff --git a/build-aux/m4/ax_compiler_vendor.m4 b/build-aux/m4/ax_compiler_vendor.m4 new file mode 100644 index 00000000000..f06e865405c --- /dev/null +++ b/build-aux/m4/ax_compiler_vendor.m4 @@ -0,0 +1,117 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_VENDOR +# +# DESCRIPTION +# +# Determine the vendor of the C, C++ or Fortran compiler. The vendor is +# returned in the cache variable $ax_cv_c_compiler_vendor for C, +# $ax_cv_cxx_compiler_vendor for C++ or $ax_cv_fc_compiler_vendor for +# (modern) Fortran. The value is one of "intel", "ibm", "pathscale", +# "clang" (LLVM), "cray", "fujitsu", "sdcc", "sx", "portland" (PGI), "gnu" +# (GCC), "sun" (Oracle Developer Studio), "hp", "dec", "borland", +# "comeau", "kai", "lcc", "sgi", "microsoft", "metrowerks", "watcom", +# "tcc" (Tiny CC) or "unknown" (if the compiler cannot be determined). +# +# To check for a Fortran compiler, you must first call AC_FC_PP_SRCEXT +# with an appropriate preprocessor-enabled extension. For example: +# +# AC_LANG_PUSH([Fortran]) +# AC_PROG_FC +# AC_FC_PP_SRCEXT([F]) +# AX_COMPILER_VENDOR +# AC_LANG_POP([Fortran]) +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# Copyright (c) 2018-19 John Zaitseff +# +# 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 . +# +# 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 30 + +AC_DEFUN([AX_COMPILER_VENDOR], [dnl + AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, [dnl + dnl If you modify this list of vendors, please add similar support + dnl to ax_compiler_version.m4 if at all possible. + dnl + dnl Note: Do NOT check for GCC first since some other compilers + dnl define __GNUC__ to remain compatible with it. Compilers that + dnl are very slow to start (such as Intel) are listed first. + + vendors=" + intel: __ICC,__ECC,__INTEL_COMPILER + ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__,__ibmxl__ + pathscale: __PATHCC__,__PATHSCALE__ + clang: __clang__ + cray: _CRAYC + fujitsu: __FUJITSU + sdcc: SDCC,__SDCC + sx: _SX + portland: __PGI + gnu: __GNUC__ + sun: __SUNPRO_C,__SUNPRO_CC,__SUNPRO_F90,__SUNPRO_F95 + hp: __HP_cc,__HP_aCC + dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER + borland: __BORLANDC__,__CODEGEARC__,__TURBOC__ + comeau: __COMO__ + kai: __KCC + lcc: __LCC__ + sgi: __sgi,sgi + microsoft: _MSC_VER + metrowerks: __MWERKS__ + watcom: __WATCOMC__ + tcc: __TINYC__ + unknown: UNKNOWN + " + for ventest in $vendors; do + case $ventest in + *:) + vendor=$ventest + continue + ;; + *) + vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")" + ;; + esac + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[ +#if !($vencpp) + thisisanerror; +#endif + ]])], [break]) + done + + ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1` + ]) +])dnl diff --git a/build-aux/m4/ax_gcc_archflag.m4 b/build-aux/m4/ax_gcc_archflag.m4 new file mode 100644 index 00000000000..c52b9b296e9 --- /dev/null +++ b/build-aux/m4/ax_gcc_archflag.m4 @@ -0,0 +1,267 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_gcc_archflag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GCC_ARCHFLAG([PORTABLE?], [ACTION-SUCCESS], [ACTION-FAILURE]) +# +# DESCRIPTION +# +# This macro tries to guess the "native" arch corresponding to the target +# architecture for use with gcc's -march=arch or -mtune=arch flags. If +# found, the cache variable $ax_cv_gcc_archflag is set to this flag and +# ACTION-SUCCESS is executed; otherwise $ax_cv_gcc_archflag is set to +# "unknown" and ACTION-FAILURE is executed. The default ACTION-SUCCESS is +# to add $ax_cv_gcc_archflag to the end of $CFLAGS. +# +# PORTABLE? should be either [yes] (default) or [no]. In the former case, +# the flag is set to -mtune (or equivalent) so that the architecture is +# only used for tuning, but the instruction set used is still portable. In +# the latter case, the flag is set to -march (or equivalent) so that +# architecture-specific instructions are enabled. +# +# The user can specify --with-gcc-arch= in order to override the +# macro's choice of architecture, or --without-gcc-arch to disable this. +# +# When cross-compiling, or if $CC is not gcc, then ACTION-FAILURE is +# called unless the user specified --with-gcc-arch manually. +# +# Requires macros: AX_CHECK_COMPILE_FLAG, AX_GCC_X86_CPUID +# +# (The main emphasis here is on recent CPUs, on the principle that doing +# high-performance computing on old hardware is uncommon.) +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# Copyright (c) 2014 Tsukasa Oi +# Copyright (c) 2017-2018 Alexey Kopytov +# +# 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 . +# +# 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 22 + +AC_DEFUN([AX_GCC_ARCHFLAG], +[AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_SED]) +AC_REQUIRE([AX_COMPILER_VENDOR]) + +AC_ARG_WITH(gcc-arch, [AS_HELP_STRING([--with-gcc-arch=], [use architecture for gcc -march/-mtune, instead of guessing])], + ax_gcc_arch=$withval, ax_gcc_arch=yes) + +AC_MSG_CHECKING([for gcc architecture flag]) +AC_MSG_RESULT([]) +AC_CACHE_VAL(ax_cv_gcc_archflag, +[ +ax_cv_gcc_archflag="unknown" + +if test "$GCC" = yes; then + +if test "x$ax_gcc_arch" = xyes; then +ax_gcc_arch="" +if test "$cross_compiling" = no; then +case $host_cpu in + i[[3456]]86*|x86_64*|amd64*) # use cpuid codes + AX_GCC_X86_CPUID(0) + AX_GCC_X86_CPUID(1) + case $ax_cv_gcc_x86_cpuid_0 in + *:756e6547:6c65746e:49656e69) # Intel + case $ax_cv_gcc_x86_cpuid_1 in + *5[[4578]]?:*:*:*) ax_gcc_arch="pentium-mmx pentium" ;; + *5[[123]]?:*:*:*) ax_gcc_arch=pentium ;; + *0?61?:*:*:*|?61?:*:*:*|61?:*:*:*) ax_gcc_arch=pentiumpro ;; + *0?6[[356]]?:*:*:*|?6[[356]]?:*:*:*|6[[356]]?:*:*:*) ax_gcc_arch="pentium2 pentiumpro" ;; + *0?6[[78ab]]?:*:*:*|?6[[78ab]]?:*:*:*|6[[78ab]]?:*:*:*) ax_gcc_arch="pentium3 pentiumpro" ;; + *0?6[[9d]]?:*:*:*|?6[[9d]]?:*:*:*|6[[9d]]?:*:*:*|*1?65?:*:*:*) ax_gcc_arch="pentium-m pentium3 pentiumpro" ;; + *0?6e?:*:*:*|?6e?:*:*:*|6e?:*:*:*) ax_gcc_arch="yonah pentium-m pentium3 pentiumpro" ;; + *0?6f?:*:*:*|?6f?:*:*:*|6f?:*:*:*|*1?66?:*:*:*) ax_gcc_arch="core2 pentium-m pentium3 pentiumpro" ;; + *1?6[[7d]]?:*:*:*) ax_gcc_arch="penryn core2 pentium-m pentium3 pentiumpro" ;; + *1?6[[aef]]?:*:*:*|*2?6e?:*:*:*) ax_gcc_arch="nehalem corei7 core2 pentium-m pentium3 pentiumpro" ;; + *2?6[[5cf]]?:*:*:*) ax_gcc_arch="westmere corei7 core2 pentium-m pentium3 pentiumpro" ;; + *2?6[[ad]]?:*:*:*) ax_gcc_arch="sandybridge corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; + *3?6[[ae]]?:*:*:*) ax_gcc_arch="ivybridge core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; + *3?6[[cf]]?:*:*:*|*4?6[[56]]?:*:*:*) ax_gcc_arch="haswell core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; + *3?6d?:*:*:*|*4?6[[7f]]?:*:*:*|*5?66?:*:*:*) ax_gcc_arch="broadwell core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; + *1?6c?:*:*:*|*2?6[[67]]?:*:*:*|*3?6[[56]]?:*:*:*) ax_gcc_arch="bonnell atom core2 pentium-m pentium3 pentiumpro" ;; + *3?67?:*:*:*|*[[45]]?6[[ad]]?:*:*:*) ax_gcc_arch="silvermont atom core2 pentium-m pentium3 pentiumpro" ;; + *000?f[[012]]?:*:*:*|?f[[012]]?:*:*:*|f[[012]]?:*:*:*) ax_gcc_arch="pentium4 pentiumpro" ;; + *000?f[[346]]?:*:*:*|?f[[346]]?:*:*:*|f[[346]]?:*:*:*) ax_gcc_arch="nocona prescott pentium4 pentiumpro" ;; + # fallback + *5??:*:*:*) ax_gcc_arch=pentium ;; + *??6??:*:*:*) ax_gcc_arch="core2 pentiumpro" ;; + *6??:*:*:*) ax_gcc_arch=pentiumpro ;; + *00??f??:*:*:*|??f??:*:*:*|?f??:*:*:*|f??:*:*:*) ax_gcc_arch="pentium4 pentiumpro" ;; + esac ;; + *:68747541:444d4163:69746e65) # AMD + case $ax_cv_gcc_x86_cpuid_1 in + *5[[67]]?:*:*:*) ax_gcc_arch=k6 ;; + *5[[8]]?:*:*:*) ax_gcc_arch="k6-2 k6" ;; + *5[[9d]]?:*:*:*) ax_gcc_arch="k6-3 k6" ;; + *6[[12]]?:*:*:*) ax_gcc_arch="athlon k7" ;; + *6[[34]]?:*:*:*) ax_gcc_arch="athlon-tbird k7" ;; + *6[[678a]]?:*:*:*) ax_gcc_arch="athlon-xp athlon-4 athlon k7" ;; + *000?f[[4578bcef]]?:*:*:*|?f[[4578bcef]]?:*:*:*|f[[4578bcef]]?:*:*:*|*001?f[[4578bcf]]?:*:*:*|1?f[[4578bcf]]?:*:*:*) ax_gcc_arch="athlon64 k8" ;; + *002?f[[13457bcf]]?:*:*:*|2?f[[13457bcf]]?:*:*:*|*004?f[[138bcf]]?:*:*:*|4?f[[138bcf]]?:*:*:*|*005?f[[df]]?:*:*:*|5?f[[df]]?:*:*:*|*006?f[[8bcf]]?:*:*:*|6?f[[8bcf]]?:*:*:*|*007?f[[cf]]?:*:*:*|7?f[[cf]]?:*:*:*|*00c?f1?:*:*:*|c?f1?:*:*:*|*020?f3?:*:*:*|20?f3?:*:*:*) ax_gcc_arch="athlon64-sse3 k8-sse3 athlon64 k8" ;; + *010?f[[245689a]]?:*:*:*|10?f[[245689a]]?:*:*:*|*030?f1?:*:*:*|30?f1?:*:*:*) ax_gcc_arch="barcelona amdfam10 k8" ;; + *050?f[[12]]?:*:*:*|50?f[[12]]?:*:*:*) ax_gcc_arch="btver1 amdfam10 k8" ;; + *060?f1?:*:*:*|60?f1?:*:*:*) ax_gcc_arch="bdver1 amdfam10 k8" ;; + *060?f2?:*:*:*|60?f2?:*:*:*|*061?f[[03]]?:*:*:*|61?f[[03]]?:*:*:*) ax_gcc_arch="bdver2 bdver1 amdfam10 k8" ;; + *063?f0?:*:*:*|63?f0?:*:*:*) ax_gcc_arch="bdver3 bdver2 bdver1 amdfam10 k8" ;; + *07[[03]]?f0?:*:*:*|7[[03]]?f0?:*:*:*) ax_gcc_arch="btver2 btver1 amdfam10 k8" ;; + # fallback + *0[[13]]??f??:*:*:*|[[13]]??f??:*:*:*) ax_gcc_arch="barcelona amdfam10 k8" ;; + *020?f??:*:*:*|20?f??:*:*:*) ax_gcc_arch="athlon64-sse3 k8-sse3 athlon64 k8" ;; + *05??f??:*:*:*|5??f??:*:*:*) ax_gcc_arch="btver1 amdfam10 k8" ;; + *060?f??:*:*:*|60?f??:*:*:*) ax_gcc_arch="bdver1 amdfam10 k8" ;; + *061?f??:*:*:*|61?f??:*:*:*) ax_gcc_arch="bdver2 bdver1 amdfam10 k8" ;; + *06??f??:*:*:*|6??f??:*:*:*) ax_gcc_arch="bdver3 bdver2 bdver1 amdfam10 k8" ;; + *070?f??:*:*:*|70?f??:*:*:*) ax_gcc_arch="btver2 btver1 amdfam10 k8" ;; + *???f??:*:*:*) ax_gcc_arch="amdfam10 k8" ;; + esac ;; + *:746e6543:736c7561:48727561) # IDT / VIA (Centaur) + case $ax_cv_gcc_x86_cpuid_1 in + *54?:*:*:*) ax_gcc_arch=winchip-c6 ;; + *5[[89]]?:*:*:*) ax_gcc_arch=winchip2 ;; + *66?:*:*:*) ax_gcc_arch=winchip2 ;; + *6[[78]]?:*:*:*) ax_gcc_arch=c3 ;; + *6[[9adf]]?:*:*:*) ax_gcc_arch="c3-2 c3" ;; + esac ;; + esac + if test x"$ax_gcc_arch" = x; then # fallback + case $host_cpu in + i586*) ax_gcc_arch=pentium ;; + i686*) ax_gcc_arch=pentiumpro ;; + esac + fi + ;; + + sparc*) + AC_PATH_PROG([PRTDIAG], [prtdiag], [prtdiag], [$PATH:/usr/platform/`uname -i`/sbin/:/usr/platform/`uname -m`/sbin/]) + cputype=`(((grep cpu /proc/cpuinfo | cut -d: -f2) ; ($PRTDIAG -v |grep -i sparc) ; grep -i cpu /var/run/dmesg.boot ) | head -n 1) 2> /dev/null` + cputype=`echo "$cputype" | tr -d ' -' | $SED 's/SPARCIIi/SPARCII/' |tr $as_cr_LETTERS $as_cr_letters` + case $cputype in + *ultrasparciv*) ax_gcc_arch="ultrasparc4 ultrasparc3 ultrasparc v9" ;; + *ultrasparciii*) ax_gcc_arch="ultrasparc3 ultrasparc v9" ;; + *ultrasparc*) ax_gcc_arch="ultrasparc v9" ;; + *supersparc*|*tms390z5[[05]]*) ax_gcc_arch="supersparc v8" ;; + *hypersparc*|*rt62[[056]]*) ax_gcc_arch="hypersparc v8" ;; + *cypress*) ax_gcc_arch=cypress ;; + esac ;; + + alphaev5) ax_gcc_arch=ev5 ;; + alphaev56) ax_gcc_arch=ev56 ;; + alphapca56) ax_gcc_arch="pca56 ev56" ;; + alphapca57) ax_gcc_arch="pca57 pca56 ev56" ;; + alphaev6) ax_gcc_arch=ev6 ;; + alphaev67) ax_gcc_arch=ev67 ;; + alphaev68) ax_gcc_arch="ev68 ev67" ;; + alphaev69) ax_gcc_arch="ev69 ev68 ev67" ;; + alphaev7) ax_gcc_arch="ev7 ev69 ev68 ev67" ;; + alphaev79) ax_gcc_arch="ev79 ev7 ev69 ev68 ev67" ;; + + powerpc*) + cputype=`((grep cpu /proc/cpuinfo | head -n 1 | cut -d: -f2 | cut -d, -f1 | $SED 's/ //g') ; /usr/bin/machine ; /bin/machine; grep CPU /var/run/dmesg.boot | head -n 1 | cut -d" " -f2) 2> /dev/null` + cputype=`echo $cputype | $SED -e 's/ppc//g;s/ *//g'` + case $cputype in + *750*) ax_gcc_arch="750 G3" ;; + *740[[0-9]]*) ax_gcc_arch="$cputype 7400 G4" ;; + *74[[4-5]][[0-9]]*) ax_gcc_arch="$cputype 7450 G4" ;; + *74[[0-9]][[0-9]]*) ax_gcc_arch="$cputype G4" ;; + *970*) ax_gcc_arch="970 G5 power4";; + *POWER4*|*power4*|*gq*) ax_gcc_arch="power4 970";; + *POWER5*|*power5*|*gr*|*gs*) ax_gcc_arch="power5 power4 970";; + 603ev|8240) ax_gcc_arch="$cputype 603e 603";; + *POWER7*) ax_gcc_arch="power7";; + *POWER8*) ax_gcc_arch="power8";; + *POWER9*) ax_gcc_arch="power9";; + *POWER10*) ax_gcc_arch="power10";; + *) ax_gcc_arch=$cputype ;; + esac + ax_gcc_arch="$ax_gcc_arch powerpc" + ;; + aarch64) + cpuimpl=`grep 'CPU implementer' /proc/cpuinfo 2> /dev/null | cut -d: -f2 | tr -d " " | head -n 1` + cpuarch=`grep 'CPU architecture' /proc/cpuinfo 2> /dev/null | cut -d: -f2 | tr -d " " | head -n 1` + cpuvar=`grep 'CPU variant' /proc/cpuinfo 2> /dev/null | cut -d: -f2 | tr -d " " | head -n 1` + case $cpuimpl in + 0x42) case $cpuarch in + 8) case $cpuvar in + 0x0) ax_gcc_arch="thunderx2t99 vulcan armv8.1-a armv8-a+lse armv8-a native" ;; + esac + ;; + esac + ;; + 0x43) case $cpuarch in + 8) case $cpuvar in + 0x0) ax_gcc_arch="thunderx armv8-a native" ;; + 0x1) ax_gcc_arch="thunderx+lse armv8.1-a armv8-a+lse armv8-a native" ;; + esac + ;; + esac + ;; + esac + ;; +esac +fi # not cross-compiling +fi # guess arch + +if test "x$ax_gcc_arch" != x -a "x$ax_gcc_arch" != xno; then +if test "x[]m4_default([$1],yes)" = xyes; then # if we require portable code + flag_prefixes="-mtune=" + if test "x$ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor" = xclang; then flag_prefixes="-march="; fi + # -mcpu=$arch and m$arch generate nonportable code on every arch except + # x86. And some other arches (e.g. Alpha) don't accept -mtune. Grrr. + case $host_cpu in i*86|x86_64*|amd64*) flag_prefixes="$flag_prefixes -mcpu= -m";; esac +else + flag_prefixes="-march= -mcpu= -m" +fi +for flag_prefix in $flag_prefixes; do + for arch in $ax_gcc_arch; do + flag="$flag_prefix$arch" + AX_CHECK_COMPILE_FLAG($flag, [if test "x$ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor" = xclang; then + if test "x[]m4_default([$1],yes)" = xyes; then + if test "x$flag" = "x-march=$arch"; then flag=-mtune=$arch; fi + fi + fi; ax_cv_gcc_archflag=$flag; break]) + done + test "x$ax_cv_gcc_archflag" = xunknown || break +done +fi + +fi # $GCC=yes +]) +AC_MSG_CHECKING([for gcc architecture flag]) +AC_MSG_RESULT($ax_cv_gcc_archflag) +if test "x$ax_cv_gcc_archflag" = xunknown; then + m4_default([$3],:) +else + m4_default([$2], [CFLAGS="$CFLAGS $ax_cv_gcc_archflag"]) +fi +]) diff --git a/build-aux/m4/ax_gcc_x86_cpuid.m4 b/build-aux/m4/ax_gcc_x86_cpuid.m4 new file mode 100644 index 00000000000..df954658ee1 --- /dev/null +++ b/build-aux/m4/ax_gcc_x86_cpuid.m4 @@ -0,0 +1,89 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GCC_X86_CPUID(OP) +# AX_GCC_X86_CPUID_COUNT(OP, COUNT) +# +# DESCRIPTION +# +# On Pentium and later x86 processors, with gcc or a compiler that has a +# compatible syntax for inline assembly instructions, run a small program +# that executes the cpuid instruction with input OP. This can be used to +# detect the CPU type. AX_GCC_X86_CPUID_COUNT takes an additional COUNT +# parameter that gets passed into register ECX before calling cpuid. +# +# On output, the values of the eax, ebx, ecx, and edx registers are stored +# as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable +# ax_cv_gcc_x86_cpuid_OP. +# +# If the cpuid instruction fails (because you are running a +# cross-compiler, or because you are not using gcc, or because you are on +# a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP +# is set to the string "unknown". +# +# This macro mainly exists to be used in AX_GCC_ARCHFLAG. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# Copyright (c) 2015 Michael Petch +# +# 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 . +# +# 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 10 + +AC_DEFUN([AX_GCC_X86_CPUID], +[AX_GCC_X86_CPUID_COUNT($1, 0) +]) + +AC_DEFUN([AX_GCC_X86_CPUID_COUNT], +[AC_REQUIRE([AC_PROG_CC]) +AC_LANG_PUSH([C]) +AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1, + [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ], [ + int op = $1, level = $2, eax, ebx, ecx, edx; + FILE *f; + __asm__ __volatile__ ("xchg %%ebx, %1\n" + "cpuid\n" + "xchg %%ebx, %1\n" + : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx) + : "a" (op), "2" (level)); + + f = fopen("conftest_cpuid", "w"); if (!f) return 1; + fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); + fclose(f); + return 0; +])], + [ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid], + [ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid], + [ax_cv_gcc_x86_cpuid_$1=unknown])]) +AC_LANG_POP([C]) +]) diff --git a/code_of_conduct.md b/code_of_conduct.md index 82446d3831d..ceb5f506d62 100644 --- a/code_of_conduct.md +++ b/code_of_conduct.md @@ -25,7 +25,7 @@ reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. Note that contributors may be volunteers -who do not represent Zcash Company. They are free to express their own +who do not represent Electric Coin Company. They are free to express their own opinions so long as they adhere to these guidelines. By adopting this Code of Conduct, project maintainers commit themselves to diff --git a/configure.ac b/configure.ac index 0346ea44bd1..05b4667e822 100644 --- a/configure.ac +++ b/configure.ac @@ -2,12 +2,12 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 2) define(_CLIENT_VERSION_MINOR, 0) -define(_CLIENT_VERSION_REVISION, 4) +define(_CLIENT_VERSION_REVISION, 6) define(_CLIENT_VERSION_BUILD, 50) 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))) define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1))) define(_CLIENT_VERSION_IS_RELEASE, true) -define(_COPYRIGHT_YEAR, 2018) +define(_COPYRIGHT_YEAR, 2019) AC_INIT([Zcash],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_SUFFIX(_ZC_BUILD_VAL)],[https://github.com/zcash/zcash/issues],[zcash]) AC_CONFIG_SRCDIR([src/main.cpp]) AC_CONFIG_HEADERS([src/config/bitcoin-config.h]) @@ -768,6 +768,9 @@ else LIBSNARK_DEPINST="$prefix" fi +# Set optimization flags for libsnark +AX_GCC_ARCHFLAG([no], [LIBSNARK_OPTFLAGS="-O2 $ax_cv_gcc_archflag"], [LIBSNARK_OPTFLAGS="-O2"]) + # Additional Zcash flags AX_CHECK_COMPILE_FLAG([-fwrapv],[CXXFLAGS="$CXXFLAGS -fwrapv"]) AX_CHECK_COMPILE_FLAG([-fno-strict-aliasing],[CXXFLAGS="$CXXFLAGS -fno-strict-aliasing"]) @@ -906,6 +909,7 @@ AC_SUBST(ZMQ_LIBS) AC_SUBST(GMP_LIBS) AC_SUBST(GMPXX_LIBS) AC_SUBST(LIBSNARK_DEPINST) +AC_SUBST(LIBSNARK_OPTFLAGS) AC_SUBST(LIBZCASH_LIBS) AC_SUBST(PROTON_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile src/test/buildenv.py]) diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 896237ef4b2..ed12564cf6c 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,39 @@ +zcash (2.0.6) stable; urgency=medium + + * 2.0.6 release. + + -- Electric Coin Company Wed, 19 Jun 2019 00:28:06 +0100 + +zcash (2.0.6~rc1) stable; urgency=medium + + * 2.0.6-rc1 release. + + -- Electric Coin Company Wed, 12 Jun 2019 16:36:57 +0100 + +zcash (2.0.5+2) stable; urgency=medium + + * 2.0.5-2 release. + + -- Electric Coin Company Wed, 15 May 2019 09:56:01 -0600 + +zcash (2.0.5+1) stable; urgency=medium + + * 2.0.5-1 release. + + -- Electric Coin Company Wed, 08 May 2019 06:57:28 -0600 + +zcash (2.0.5) stable; urgency=medium + + * 2.0.5 release. + + -- Electric Coin Company Fri, 03 May 2019 16:35:30 -0600 + +zcash (2.0.5~rc1) stable; urgency=medium + + * 2.0.5-rc1 release. + + -- Electric Coin Company Wed, 01 May 2019 14:16:08 -0600 + zcash (2.0.4) stable; urgency=medium * 2.0.4 release. diff --git a/contrib/debian/control b/contrib/debian/control index b0c220cf01c..c2c9d17d555 100644 --- a/contrib/debian/control +++ b/contrib/debian/control @@ -1,7 +1,7 @@ Source: zcash Section: utils Priority: optional -Maintainer: Zcash Company +Maintainer: Electric Coin Company Homepage: https://z.cash Build-Depends: autoconf, automake, bsdmainutils, build-essential, git, g++-multilib, libc6-dev, libtool, diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 14cbe9e9aef..de1fdf97d19 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -1,6 +1,6 @@ Format: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?rev=174 Upstream-Name: Zcash -Upstream-Contact: Zcash Company +Upstream-Contact: Electric Coin Company Source: https://github.com/zcash/zcash Files: * diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 1d946ab8f6f..d770c29fc31 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,12 +1,14 @@ --- -name: "zcash-2.0.4" +name: "zcash-2.0.6" enable_cache: true distro: "debian" suites: - "jessie" +- "stretch" architectures: - "amd64" packages: +- "curl" - "autoconf" - "automake" - "bsdmainutils" diff --git a/depends/.gitignore b/depends/.gitignore index 1f163897b9e..3cb4b9ac155 100644 --- a/depends/.gitignore +++ b/depends/.gitignore @@ -7,3 +7,4 @@ x86_64* i686* mips* arm* +aarch64* diff --git a/depends/funcs.mk b/depends/funcs.mk index 3d89de8a703..f17b246a4c8 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -59,8 +59,8 @@ $(eval $(1)_build_id:=$(shell echo -n "$($(1)_build_id_long)" | $(build_SHA256SU 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))) +$(eval $(1)_file_name=$(if $($(1)_exact_file_name),$($(1)_exact_file_name),$(if $($(1)_file_name_$(host_os)),$($(1)_file_name_$(host_os)),$($(1)_file_name)))) +$(eval $(1)_sha256_hash=$(if $($(1)_exact_sha256_hash),$($(1)_exact_sha256_hash),$(if $($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash)))) #compute package-specific paths $(1)_build_subdir?=. diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index 6b385f2ab59..21019aeb71f 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -9,6 +9,7 @@ define $(package)_set_vars $(package)_config_opts=--disable-shared --enable-cxx --disable-replication $(package)_config_opts_mingw32=--enable-mingw $(package)_config_opts_linux=--with-pic +$(package)_config_opts_aarch64=--disable-atomicsupport $(package)_cxxflags=-std=c++11 endef diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 3e274c7e48d..a5c90cece97 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,8 +1,8 @@ package=boost -$(package)_version=1_69_0 -$(package)_download_path=https://dl.bintray.com/boostorg/release/1.69.0/source +$(package)_version=1_70_0 +$(package)_download_path=https://dl.bintray.com/boostorg/release/1.70.0/source $(package)_file_name=$(package)_$($(package)_version).tar.bz2 -$(package)_sha256_hash=8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406 +$(package)_sha256_hash=430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778 define $(package)_set_vars $(package)_config_opts_release=variant=release diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index a47c757de80..9bc8ee543ec 100644 --- a/depends/packages/librustzcash.mk +++ b/depends/packages/librustzcash.mk @@ -8,15 +8,21 @@ $(package)_git_commit=06da3b9ac8f278e5d4ae13088cf0a4c03d2c13f5 $(package)_dependencies=rust $(rust_crates) $(package)_patches=cargo.config 0001-Start-using-cargo-clippy-for-CI.patch remove-dev-dependencies.diff +$(package)_rust_target=$(if $(rust_rust_target_$(canonical_host)),$(rust_rust_target_$(canonical_host)),$(canonical_host)) + ifeq ($(host_os),mingw32) $(package)_library_file=target/x86_64-pc-windows-gnu/release/rustzcash.lib +else ifneq ($(canonical_host),$(build)) +$(package)_library_file=target/$($(package)_rust_target)/release/librustzcash.a else $(package)_library_file=target/release/librustzcash.a endif define $(package)_set_vars $(package)_build_opts=--frozen --release -$(package)_build_opts_mingw32=--target=x86_64-pc-windows-gnu +ifneq ($(canonical_host),$(build)) +$(package)_build_opts+=--target=$($(package)_rust_target) +endif endef define $(package)_preprocess_cmds @@ -27,7 +33,7 @@ define $(package)_preprocess_cmds endef define $(package)_build_cmds - cargo build --package librustzcash $($(package)_build_opts) + $(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts) endef define $(package)_stage_cmds diff --git a/depends/packages/rust.mk b/depends/packages/rust.mk index 18d5b131647..b892d8b7265 100644 --- a/depends/packages/rust.mk +++ b/depends/packages/rust.mk @@ -5,12 +5,21 @@ $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.t $(package)_sha256_hash_linux=e024698320d76b74daf0e6e71be3681a1e7923122e3ebd03673fcac3ecc23810 $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz $(package)_sha256_hash_darwin=f0dfba507192f9b5c330b5984ba71d57d434475f3d62bd44a39201e36fa76304 -$(package)_file_name_mingw32=rust-$($(package)_version)-x86_64-pc-windows-gnu.tar.gz -$(package)_sha256_hash_mingw32=358e1435347c67dbf33aa9cad6fe501a833d6633ed5d5aa1863d5dffa0349be9 -ifeq ($(host_os),mingw32) +# Mapping from GCC canonical hosts to Rust targets +# If a mapping is not present, we assume they are identical +$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu + +# Mapping from Rust targets to SHA-256 hashes +$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=346efe3aef2aff7b71a611bf7661bcec5f9bc4025a599c2866ec5fd330247cb9 +$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=cad5f1454d591c13eeb3657f1c9dbfeb30e648f59680bd0765b94c63e7afc49e + +ifneq ($(canonical_host),$(build)) +$(package)_rust_target=$(if $($(package)_rust_target_$(canonical_host)),$($(package)_rust_target_$(canonical_host)),$(canonical_host)) +$(package)_exact_file_name=rust-std-$($(package)_version)-$($(package)_rust_target).tar.gz +$(package)_exact_sha256_hash=$($(package)_rust_std_sha256_hash_$($(package)_rust_target)) $(package)_build_subdir=buildos -$(package)_extra_sources = $($(package)_file_name_$(build_os)) +$(package)_extra_sources=$($(package)_file_name_$(build_os)) define $(package)_fetch_cmds $(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ @@ -22,15 +31,15 @@ define $(package)_extract_cmds echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ echo "$($(package)_sha256_hash_$(build_os)) $($(package)_source_dir)/$($(package)_file_name_$(build_os))" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - mkdir mingw32 && \ - tar --strip-components=1 -xf $($(package)_source) -C mingw32 && \ + mkdir $(canonical_host) && \ + tar --strip-components=1 -xf $($(package)_source) -C $(canonical_host) && \ mkdir buildos && \ tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_file_name_$(build_os)) -C buildos endef define $(package)_stage_cmds ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig && \ - cp -r ../mingw32/rust-std-x86_64-pc-windows-gnu/lib/rustlib/x86_64-pc-windows-gnu $($(package)_staging_dir)$(host_prefix)/native/lib/rustlib + ../$(canonical_host)/install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig endef else diff --git a/depends/patches/librustzcash/cargo.config b/depends/patches/librustzcash/cargo.config index a0252d1c681..84d447afb62 100644 --- a/depends/patches/librustzcash/cargo.config +++ b/depends/patches/librustzcash/cargo.config @@ -18,6 +18,3 @@ replace-with = "vendored-sources" [source.vendored-sources] directory = "CRATE_REGISTRY" - -[target.x86_64-pc-windows-gnu] -linker = "x86_64-w64-mingw32-gcc" diff --git a/doc/authors.md b/doc/authors.md index e82945efc15..6afdb557cdf 100644 --- a/doc/authors.md +++ b/doc/authors.md @@ -1,63 +1,64 @@ Zcash Contributors ================== -Jack Grigg (862) -Simon Liu (443) +Jack Grigg (873) +Simon Liu (451) Sean Bowe (278) -Daira Hopwood (110) -Eirik Ogilvie-Wigley (102) +Eirik Ogilvie-Wigley (152) +Daira Hopwood (124) Jay Graber (89) -Wladimir J. van der Laan (81) +Wladimir J. van der Laan (82) Taylor Hornby (73) Jonas Schnelli (62) Nathan Wilcox (56) Pieter Wuille (54) Kevin Gallagher (38) Cory Fields (35) -mdr0id (22) +Larry Ruane (23) +Marshall Gaucher (22) Jonathan "Duke" Leto (17) -Larry Ruane (16) syd (15) Matt Corallo (13) Paige Peterson (11) +Marco Falke (11) Ariel Gabizon (11) -MarcoFalke (10) +Jorge Timón (10) nomnombtc (9) kozyilmaz (8) fanquake (8) Jeff Garzik (7) Gregory Maxwell (7) +Marius Kjærstad (6) Luke Dashjr (6) David Mercer (6) Daniel Cousens (6) +Charlie O'Keefe (6) Suhas Daftuar (5) -Pavel Janík (5) -Marius Kjærstad (5) +Peter Todd (5) Karl-Johan Alm (5) Johnathan Corgan (5) -Charlie O'Keefe (5) Alex Morcos (5) WO (4) Philip Kaufmann (4) -Peter Todd (4) +Pavel Janík (4) Patrick Strateman (4) João Barbosa (4) -Jorge Timón (4) George Tankersley (4) +Gareth Davies (4) +Daniel Kraft (4) +Benjamin Winston (4) lpescher (3) ca333 (3) Per Grön (3) Patick Strateman (3) Jason Davies (3) James O'Beirne (3) -Gareth Davies (3) -Daniel Kraft (3) +Dimitris Apostolou (3) Alfie John (3) -zebambam (2) rofl0r (2) -paveljanik (2) mruddy (2) kpcyrd (2) +face (2) aniemerg (2) UdjinM6 (2) Scott (2) @@ -66,12 +67,12 @@ Pejvan (2) Pavol Rusnak (2) Pavel Vasin (2) Matthew King (2) +Mary Moore-Simmons (2) Kaz Wesley (2) Joe Turgeon (2) Jack Gavigan (2) ITH4Coinomia (2) Gavin Andresen (2) -Dimitris Apostolou (2) Brad Miller (2) Bjorn Hjortsberg (2) Amgad Abdelhafez (2) @@ -118,6 +119,7 @@ Kevin Pan (1) Jonas Nick (1) Jeremy Rubin (1) Jeffrey Walton (1) +Ian Munoz (1) Ian Kelling (1) Gaurav Rana (1) Forrest Voight (1) @@ -131,6 +133,7 @@ Casey Rodarmor (1) Cameron Boehmer (1) Bryan Stitt (1) Bruno Arueira (1) +Braydon Fuller (1) Boris Hajduk (1) Bob McElrath (1) Bitcoin Error Log (1) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 311dbcc46c6..ddbd7df1889 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -71,6 +71,12 @@ To describe a member or variable use: int var; //!< Detailed description after the member ``` +or +```cpp +//! Description before the member +int var; +``` + Also OK: ```c++ /// diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index 3bb439b340e..741de3dba72 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH ZCASH-CLI "1" "March 2019" "zcash-cli v2.0.4" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. +.TH ZCASH-CLI "1" "June 2019" "zcash-cli v2.0.6" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v2.0.4 +zcash-cli \- manual page for zcash-cli v2.0.6 .SH DESCRIPTION -Zcash RPC client version v2.0.4 +Zcash RPC client version v2.0.6 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . @@ -75,8 +75,8 @@ Read extra arguments from standard input, one per line until EOF/Ctrl\-D In order to ensure you are adequately protecting your privacy when using Zcash, please see . -Copyright (C) 2009-2018 The Bitcoin Core Developers -Copyright (C) 2015-2018 The Zcash Developers +Copyright (C) 2009-2019 The Bitcoin Core Developers +Copyright (C) 2015-2019 The Zcash Developers This is experimental software. diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index 20466cb239d..1353e34a7f4 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH ZCASH-TX "1" "March 2019" "zcash-tx v2.0.4" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. +.TH ZCASH-TX "1" "June 2019" "zcash-tx v2.0.6" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v2.0.4 +zcash-tx \- manual page for zcash-tx v2.0.6 .SH DESCRIPTION -Zcash zcash\-tx utility version v2.0.4 +Zcash zcash\-tx utility version v2.0.6 .SS "Usage:" .TP zcash\-tx [options] [commands] @@ -88,8 +88,8 @@ Set register NAME to given JSON\-STRING In order to ensure you are adequately protecting your privacy when using Zcash, please see . -Copyright (C) 2009-2018 The Bitcoin Core Developers -Copyright (C) 2015-2018 The Zcash Developers +Copyright (C) 2009-2019 The Bitcoin Core Developers +Copyright (C) 2015-2019 The Zcash Developers This is experimental software. diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index f993c77a7a8..491550c9768 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH ZCASHD "1" "March 2019" "zcashd v2.0.4" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. +.TH ZCASHD "1" "June 2019" "zcashd v2.0.6" "User Commands" .SH NAME -zcashd \- manual page for zcashd v2.0.4 +zcashd \- manual page for zcashd v2.0.6 .SH DESCRIPTION -Zcash Daemon version v2.0.4 +Zcash Daemon version v2.0.6 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . @@ -75,7 +75,7 @@ limit applied) .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-4\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-6\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP \fB\-pid=\fR @@ -240,6 +240,14 @@ Do not load the wallet and disable wallet RPC calls .IP Set key pool size to (default: 100) .HP +\fB\-migration\fR +.IP +Enable the Sprout to Sapling migration +.HP +\fB\-migrationdestaddress=\fR +.IP +Set the Sapling migration address +.HP \fB\-paytxfee=\fR .IP Fee (in ZEC/kB) to add to transactions you send (default: 0.00) @@ -470,8 +478,8 @@ console, 600 otherwise) In order to ensure you are adequately protecting your privacy when using Zcash, please see . -Copyright (C) 2009-2018 The Bitcoin Core Developers -Copyright (C) 2015-2018 The Zcash Developers +Copyright (C) 2009-2019 The Bitcoin Core Developers +Copyright (C) 2015-2019 The Zcash Developers This is experimental software. diff --git a/doc/release-notes/release-notes-2.0.5-1.md b/doc/release-notes/release-notes-2.0.5-1.md new file mode 100644 index 00000000000..ef02d80b571 --- /dev/null +++ b/doc/release-notes/release-notes-2.0.5-1.md @@ -0,0 +1,68 @@ +Notable changes +=============== + +Sprout to Sapling Migration Tool +-------------------------------- +This release includes the addition of a tool that will enable users to migrate +shielded funds from the Sprout pool to the Sapling pool while minimizing +information leakage. + +The migration can be enabled using the RPC `z_setmigration` or by including +`-migration` in the `zcash.conf` file. Unless otherwise specified funds will be +migrated to the wallet's default Sapling address; it is also possible to set the +receiving Sapling address using the `-migrationdestaddress` option in `zcash.conf`. + +See [ZIP308](https://github.com/zcash/zips/blob/master/zip-0308.rst) for full details. + +Sprout to Sapling Migration Tool Fixes +-------------------------------------- +The 2.0.5-1 release includes fixes to the Sprout to Sapling Migration Tool +found in testing. We resolved an issue which would cause the zcash daemon to +crash if calling the RPC `z_getmigrationstatus` while a wallet's migration +transactions are in the mempool. + +New consensus rule: Reject blocks that violate turnstile +-------------------------------------------------------- +In the 2.0.4 release the consensus rules were changed on testnet to enforce a +consensus rule which marks blocks as invalid if they would lead to a turnstile +violation in the Sprout or Shielded value pools. +**This release enforces the consensus rule change on mainnet** + +The motivations and deployment details can be found in the accompanying +[ZIP draft](https://github.com/zcash/zips/pull/210) and +[PR 3968](https://github.com/zcash/zcash/pull/3968). + +Developers can use a new experimental feature `-developersetpoolsizezero` to test +Sprout and Sapling turnstile violations. See [PR 3964](https://github.com/zcash/zcash/pull/3964) for more details. + + +64-bit ARMv8 support +-------------------- +Added ARMv8 (AArch64) support. This enables users to build zcash on even more +devices. + +For information on how to build see the [User Guide](https://zcash.readthedocs.io/en/latest/rtd_pages/user_guide.html#build) + +Users on the Zcash forum have reported successes with both the Pine64 Rock64Pro +and Odroid C2 which contain 4GB and 2GB of RAM respectively. + +Just released, the Odroid N2 looks like a great solution with 4GB of RAM. The +newly released Jetson Nano Developer Kit from Nvidia (also 4GB of RAM) is also +worth a look. The NanoPC-T3 Plus is another option but for the simplest/best +experience choose a board with 4GB of RAM. Just make sure before purchase that +the CPU supports the 64-bit ARMv8 architecture. + +Changelog +========= + +Eirik0 (9): + Correctly account for migration transactions in the mempool + Store the migration operation id rather than the operation iteslf + Rename variable and add comment + Notable changes for v2.0.5-1 + Fix summing available funds + Add the amount migrated to the operation's result + coinsView is required when using TransactionBuilder if there may be Sprout change + make-release.py: Versioning changes for 2.0.5-1. + make-release.py: Updated manpages for 2.0.5-1. + diff --git a/doc/release-notes/release-notes-2.0.5-2.md b/doc/release-notes/release-notes-2.0.5-2.md new file mode 100644 index 00000000000..8d7dc947afd --- /dev/null +++ b/doc/release-notes/release-notes-2.0.5-2.md @@ -0,0 +1,77 @@ +Notable changes +=============== + +Sprout to Sapling Migration Tool +-------------------------------- +This release includes the addition of a tool that will enable users to migrate +shielded funds from the Sprout pool to the Sapling pool while minimizing +information leakage. + +The migration can be enabled using the RPC `z_setmigration` or by including +`migration=1` in the `zcash.conf` file. Unless otherwise specified funds will be +migrated to the wallet's default Sapling address; it is also possible to set the +receiving Sapling address using the `migrationdestaddress=` option in +`zcash.conf`. + +See [ZIP308](https://github.com/zcash/zips/blob/master/zip-0308.rst) for full details. + +Sprout to Sapling Migration Tool Fixes +-------------------------------------- +The 2.0.5-1 and 2.0.5-2 releases include fixes to the Sprout to Sapling +Migration Tool found in testing. + +For a complete list of changes in 2.0.5, 2.0.5-1 and 2.0.5-2, see the [2.0.5 milestone](https://github.com/zcash/zcash/milestone/79?closed=1). + +New consensus rule: Reject blocks that violate turnstile +-------------------------------------------------------- +In the 2.0.4 release the consensus rules were changed on testnet to enforce a +consensus rule which marks blocks as invalid if they would lead to a turnstile +violation in the Sprout or Shielded value pools. +**This release enforces the consensus rule change on mainnet.** + +The motivations and deployment details can be found in +[ZIP209](https://github.com/zcash/zips/blob/master/zip-0209.rst) and +[PR 3968](https://github.com/zcash/zcash/pull/3968). + +Developers can use a new experimental feature `-developersetpoolsizezero` to test +Sprout and Sapling turnstile violations. See [PR 3964](https://github.com/zcash/zcash/pull/3964) for more details. + +64-bit ARMv8 support +-------------------- +Added ARMv8 (AArch64) support. This enables users to build zcash on even more +devices. + +For information on how to build see the [User Guide](https://zcash.readthedocs.io/en/latest/rtd_pages/user_guide.html#build). + +Users on the Zcash forum have reported successes with both the Pine64 Rock64Pro +and Odroid C2 which contain 4GB and 2GB of RAM respectively. + +Just released, the Odroid N2 looks like a great solution with 4GB of RAM. The +newly released Jetson Nano Developer Kit from Nvidia (also 4GB of RAM) is also +worth a look. The NanoPC-T3 Plus is another option but for the simplest/best +experience choose a board with 4GB of RAM. Just make sure before purchase that +the CPU supports the 64-bit ARMv8 architecture. + +Changelog +========= + +Daira Hopwood (5): + Generalize TransactionBuilder and CreateNewContextualCMutableTransaction to allow choosing the expiry delta. + Repair calls to TransactionBuilder from tests. + Change expiry delta for migration transactions to 450 blocks. + Test the expiry height of migration transactions. + Fix cosmetic spacing issue in z_setmigration help. + +Eirik0 (7): + Do not automatically remove async migration operations and return txids + Add logging for Sprout to Sapling migration transaction generation + Fix LogPrint statements + Notable changes for v2.0.5-2 + Release notes wording and punctuation + make-release.py: Versioning changes for 2.0.5-2. + make-release.py: Updated manpages for 2.0.5-2. + +Simon Liu (2): + Remove unused specifier from format string. + Don't allow migration when node is syncing at launch or after waking up. + diff --git a/doc/release-notes/release-notes-2.0.5-rc1.md b/doc/release-notes/release-notes-2.0.5-rc1.md new file mode 100644 index 00000000000..08640b1f443 --- /dev/null +++ b/doc/release-notes/release-notes-2.0.5-rc1.md @@ -0,0 +1,73 @@ +Changelog +========= + +Braydon Fuller (1): + tests: adds unit test for IsPayToPublicKeyHash method + +Dimitris Apostolou (1): + Electric Coin Company + +Eirik0 (22): + Split test in to multiple parts + Use a custom error type if creating joinsplit descriptions fails + Rename and update comment + Add rpc to enable and disable Sprout to Sapling migration + Move migration logic to ChainTip + Documentation cleanup + Additional locking and race condition prevention + Refactor wait_and_assert_operationid_status to allow returning the result + Set min depth when selecting notes to migrate + Check for full failure message in test case + Add migration options to conf file + Create method for getting HD seed in RPCs + Add rpc to get Sprout to Sapling migration status + Fix help message + Test migration using both the parameter and the default Sapling address + Fix typos and update documentation + use -valueBalance rather than vpub_new to calculate migrated amount + Do not look at vin/vout when determining migration txs and other cleanup + Calculate the number of confimations in the canonical way + Do not throw an exception if HD Seed is not found when exporting wallet + make-release.py: Versioning changes for 2.0.5-rc1. + make-release.py: Updated manpages for 2.0.5-rc1. + +Gareth Davies (1): + Adding addressindex.h to Makefile.am + +Jack Grigg (9): + Add Sprout support to TransactionBuilder + depends: Use full path to cargo binary + depends: Generalise the rust package cross-compilation functions + depends: Add rust-std hash for aarch64-unknown-linux-gnu + depends: Compile bdb with --disable-atomics on aarch64 + depends: Update .gitignore + configure: Guess -march for libsnark OPTFLAGS instead of hard-coding + Add Blossom to upgrade list + init: Fix new HD seed generation for previously-encrypted wallets + +Larry Ruane (6): + fix enable-debug build DB_COINS undefined + add -addressindex changes for bitcore insight block explorer + add -spentindex changes for bitcore insight block explorer + Update boost from v1.69.0 to v1.70.0. #3947 + add -timestampindex for bitcore insight block explorer + 3873 z_setmigration cli bool enable arg conversion + +Marius Kjærstad (1): + Update _COPYRIGHT_YEAR in configure.ac to 2019 + +Mary Moore-Simmons (1): + Creates checklist template for new PRs being opened and addresses Str4d's suggestion for using GitHub handles + +Simon Liu (4): + Add testnet and regtest experimental feature: -developersetpoolsizezero + Add qa test for experimental feature: -developersetpoolsizezero + Enable ZIP209 on mainnet and set fallback Sprout pool balance. + Enable experimental feature -developersetpoolsizezero on mainnet. + +Jack Grigg (1): + remove extra hyphen + +zebambam (1): + Minor speling changes + diff --git a/doc/release-notes/release-notes-2.0.5.md b/doc/release-notes/release-notes-2.0.5.md new file mode 100644 index 00000000000..520060f1784 --- /dev/null +++ b/doc/release-notes/release-notes-2.0.5.md @@ -0,0 +1,130 @@ +Notable changes +=============== + +Sprout to Sapling Migration Tool +-------------------------------- +This release includes the addition of a tool that will enable users to migrate +shielded funds from the Sprout pool to the Sapling pool while minimizing +information leakage. + +The migration can be enabled using the RPC `z_setmigration` or by including +`-migration` in the `zcash.conf` file. Unless otherwise specified funds will be +migrated to the wallet's default Sapling address; it is also possible to set the +receiving Sapling address using the `-migrationdestaddress` option in `zcash.conf`. + +See [ZIP308](https://github.com/zcash/zips/blob/master/zip-0308.rst) for full details. + + +New consensus rule: Reject blocks that violate turnstile +-------------------------------------------------------- +In the 2.0.4 release the consensus rules were changed on testnet to enforce a +consensus rule which marks blocks as invalid if they would lead to a turnstile +violation in the Sprout or Shielded value pools. +**This release enforces the consensus rule change on mainnet** + +The motivations and deployment details can be found in the accompanying +[ZIP draft](https://github.com/zcash/zips/pull/210) and +[PR 3968](https://github.com/zcash/zcash/pull/3968). + +Developers can use a new experimental feature `-developersetpoolsizezero` to test +Sprout and Sapling turnstile violations. See [PR 3964](https://github.com/zcash/zcash/pull/3964) for more details. + + +64-bit ARMv8 support +-------------------- +Added ARMv8 (AArch64) support. This enables users to build zcash on even more +devices. + +For information on how to build see the [User Guide](https://zcash.readthedocs.io/en/latest/rtd_pages/user_guide.html#build) + +Users on the Zcash forum have reported successes with both the Pine64 Rock64Pro +and Odroid C2 which contain 4GB and 2GB of RAM respectively. + +Just released, the Odroid N2 looks like a great solution with 4GB of RAM. The +newly released Jetson Nano Developer Kit from Nvidia (also 4GB of RAM) is also +worth a look. The NanoPC-T3 Plus is another option but for the simplest/best +experience choose a board with 4GB of RAM. Just make sure before purchase that +the CPU supports the 64-bit ARMv8 architecture. + +Changelog +========= + +Braydon Fuller (1): + tests: adds unit test for IsPayToPublicKeyHash method + +Dimitris Apostolou (1): + Electric Coin Company + +Eirik0 (27): + Split test in to multiple parts + Use a custom error type if creating joinsplit descriptions fails + Rename and update comment + Add rpc to enable and disable Sprout to Sapling migration + Move migration logic to ChainTip + Documentation cleanup + Additional locking and race condition prevention + Refactor wait_and_assert_operationid_status to allow returning the result + Set min depth when selecting notes to migrate + Check for full failure message in test case + Add migration options to conf file + Create method for getting HD seed in RPCs + Add rpc to get Sprout to Sapling migration status + Fix help message + Test migration using both the parameter and the default Sapling address + Fix typos and update documentation + use -valueBalance rather than vpub_new to calculate migrated amount + Do not look at vin/vout when determining migration txs and other cleanup + Calculate the number of confimations in the canonical way + Do not throw an exception if HD Seed is not found when exporting wallet + make-release.py: Versioning changes for 2.0.5-rc1. + make-release.py: Updated manpages for 2.0.5-rc1. + make-release.py: Updated release notes and changelog for 2.0.5-rc1. + Notable changes for v2.0.5 + Add missing word to release notes + make-release.py: Versioning changes for 2.0.5. + make-release.py: Updated manpages for 2.0.5. + +Gareth Davies (1): + Adding addressindex.h to Makefile.am + +Ian Munoz (1): + add curl to package list for gitian lxc container + +Jack Grigg (9): + Add Sprout support to TransactionBuilder + depends: Use full path to cargo binary + depends: Generalise the rust package cross-compilation functions + depends: Add rust-std hash for aarch64-unknown-linux-gnu + depends: Compile bdb with --disable-atomics on aarch64 + depends: Update .gitignore + configure: Guess -march for libsnark OPTFLAGS instead of hard-coding + Add Blossom to upgrade list + init: Fix new HD seed generation for previously-encrypted wallets + +Larry Ruane (6): + fix enable-debug build DB_COINS undefined + add -addressindex changes for bitcore insight block explorer + add -spentindex changes for bitcore insight block explorer + Update boost from v1.69.0 to v1.70.0. #3947 + add -timestampindex for bitcore insight block explorer + 3873 z_setmigration cli bool enable arg conversion + +Marius Kjærstad (1): + Update _COPYRIGHT_YEAR in configure.ac to 2019 + +Mary Moore-Simmons (1): + Creates checklist template for new PRs being opened and addresses Str4d's suggestion for using GitHub handles + +Simon Liu (5): + Add testnet and regtest experimental feature: -developersetpoolsizezero + Add qa test for experimental feature: -developersetpoolsizezero + Enable ZIP209 on mainnet and set fallback Sprout pool balance. + Enable experimental feature -developersetpoolsizezero on mainnet. + Update chain work and checkpoint using block 525000. + +Jack Grigg (1): + remove extra hyphen + +zebambam (1): + Minor speling changes + diff --git a/doc/release-notes/release-notes-2.0.6-rc1.md b/doc/release-notes/release-notes-2.0.6-rc1.md new file mode 100644 index 00000000000..ce40147200a --- /dev/null +++ b/doc/release-notes/release-notes-2.0.6-rc1.md @@ -0,0 +1,62 @@ +Changelog +========= + +Daira Hopwood (3): + Closes #3992. Remove obsolete warning message. + make-release.py: Versioning changes for 2.0.6-rc1. + make-release.py: Updated manpages for 2.0.6-rc1. + +Daniel Kraft (1): + Add some const declarations where they are appropriate. + +Eirik Ogilvie-Wigley (7): + Fix tree depth in comment + Update author aliases + Remove old mergetoaddress RPC test + Replace CSproutNotePlaintextEntry with SproutNoteEntry to match Sapling + z_getmigrationstatus help message wording change + Fix z_mergetoaddress sending from ANY_SPROUT/ANY_SAPLING when the wallet contains both note types + Clarify what combinations of from addresses can be used in z_mergetoaddress + +Jack Grigg (10): + Move Equihash parameters into consensus params + Globals: Remove Zcash-specific Params() calls from main.cpp + Globals: Explicitly pass const CChainParams& to IsStandardTx() + Globals: Explicit const CChainParams& arg for main: + Globals: Explicitly pass const CChainParams& to ContextualCheckTransaction() + Globals: Explicit const CChainParams& arg for main: + Globals: Explicitly pass const CChainParams& to DisconnectBlock() + Consistently use chainparams and consensusParams + Globals: Explicitly pass const CChainParams& to IsInitialBlockDownload() + Globals: Explicitly pass const CChainParams& to ReceivedBlockTransactions() + +Jorge Timón (6): + Globals: Explicit Consensus::Params arg for main: + Globals: Make AcceptBlockHeader static (Fix #6163) + Chainparams: Explicit CChainParams arg for main (pre miner): + Chainparams: Explicit CChainParams arg for miner: + Globals: Remove a bunch of Params() calls from main.cpp: + Globals: Explicitly pass const CChainParams& to UpdateTip() + +Larry Ruane (1): + add spentindex to getrawtransaction RPC results + +MarcoFalke (1): + [doc] Fix doxygen comments for members + +Peter Todd (1): + Improve block validity/ConnectBlock() comments + +Simon Liu (1): + Fix typo and clean up help message for RPC z_getmigrationstatus. + +Wladimir J. van der Laan (1): + Break circular dependency main ↔ txdb + +face (2): + Pass CChainParams to DisconnectTip() + Explicitly pass CChainParams to ConnectBlock + +Benjamin Winston (1): + Fixes #4013, added BitcoinABC as a disclosure partner + diff --git a/doc/release-notes/release-notes-2.0.6.md b/doc/release-notes/release-notes-2.0.6.md new file mode 100644 index 00000000000..f1beec5117e --- /dev/null +++ b/doc/release-notes/release-notes-2.0.6.md @@ -0,0 +1,102 @@ +Notable changes +=============== + +Debian Stretch is now a Supported Platform +------------------------------------------ + +We now provide reproducible builds for Stretch as well as for Jessie. + + +Fixed a bug in ``z_mergetoaddress`` +----------------------------------- + +We fixed a bug which prevented users sending from ``ANY_SPROUT`` or ``ANY_SAPLING`` +with ``z_mergetoaddress`` when a wallet contained both Sprout and Sapling notes. + + +Insight Explorer +---------------- + +We have been incorporating changes to support the Insight explorer directly from +``zcashd``. v2.0.6 includes the first change to an RPC method. If ``zcashd`` is +run with the flag ``--insightexplorer``` (this requires an index rebuild), the +RPC method ``getrawtransaction`` will now return additional information about +spend indices. + +Changelog +========= + +Charlie O'Keefe (1): + Add stretch to list of suites in gitian linux descriptors + +Daira Hopwood (9): + Closes #3992. Remove obsolete warning message. + make-release.py: Versioning changes for 2.0.6-rc1. + make-release.py: Updated manpages for 2.0.6-rc1. + make-release.py: Updated release notes and changelog for 2.0.6-rc1. + ld --version doesn't work on macOS. + Tweak author aliases. + Add coding declaration to zcutil/release-notes.py + make-release.py: Versioning changes for 2.0.6. + make-release.py: Updated manpages for 2.0.6. + +Daniel Kraft (1): + Add some const declarations where they are appropriate. + +Eirik Ogilvie-Wigley (1): + Notable changes for 2.0.6 + +Eirik Ogilvie-Wigley (7): + Fix tree depth in comment + Update author aliases + Remove old mergetoaddress RPC test + Replace CSproutNotePlaintextEntry with SproutNoteEntry to match Sapling + z_getmigrationstatus help message wording change + Fix z_mergetoaddress sending from ANY_SPROUT/ANY_SAPLING when the wallet contains both note types + Clarify what combinations of from addresses can be used in z_mergetoaddress + +Jack Grigg (10): + Move Equihash parameters into consensus params + Globals: Remove Zcash-specific Params() calls from main.cpp + Globals: Explicitly pass const CChainParams& to IsStandardTx() + Globals: Explicit const CChainParams& arg for main: + Globals: Explicitly pass const CChainParams& to ContextualCheckTransaction() + Globals: Explicit const CChainParams& arg for main: + Globals: Explicitly pass const CChainParams& to DisconnectBlock() + Consistently use chainparams and consensusParams + Globals: Explicitly pass const CChainParams& to IsInitialBlockDownload() + Globals: Explicitly pass const CChainParams& to ReceivedBlockTransactions() + +Jorge Timón (6): + Globals: Explicit Consensus::Params arg for main: + Globals: Make AcceptBlockHeader static (Fix #6163) + Chainparams: Explicit CChainParams arg for main (pre miner): + Chainparams: Explicit CChainParams arg for miner: + Globals: Remove a bunch of Params() calls from main.cpp: + Globals: Explicitly pass const CChainParams& to UpdateTip() + +Larry Ruane (1): + add spentindex to getrawtransaction RPC results + +Marco Falke (1): + [doc] Fix doxygen comments for members + +Mary Moore-Simmons (1): + Fixes issue #3504: Changes to --version and adds a couple other useful commands. + +Peter Todd (1): + Improve block validity/ConnectBlock() comments + +Simon Liu (1): + Fix typo and clean up help message for RPC z_getmigrationstatus. + +Wladimir J. van der Laan (1): + Break circular dependency main ↔ txdb + +face (2): + Pass CChainParams to DisconnectTip() + Explicitly pass CChainParams to ConnectBlock + +Benjamin Winston (1): + Fixes #4013, added BitcoinABC as a disclosure partner + diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 3d6be26dd5c..aeda0da2cbf 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -31,12 +31,14 @@ testScripts=( 'wallet_listnotes.py' 'mergetoaddress_sprout.py' 'mergetoaddress_sapling.py' + 'mergetoaddress_mixednotes.py' 'listtransactions.py' 'mempool_resurrect_test.py' 'txn_doublespend.py' 'txn_doublespend.py --mineblock' 'getchaintips.py' 'rawtransactions.py' + 'getrawtransaction_insight.py' 'rest.py' 'mempool_spendcoinbase.py' 'mempool_reorg.py' @@ -71,6 +73,8 @@ testScripts=( 'p2p_node_bloom.py' 'regtest_signrawtransaction.py' 'finalsaplingroot.py' + 'sprout_sapling_migration.py' + 'turnstile.py' ); testScriptsExt=( 'getblocktemplate_longpoll.py' diff --git a/qa/rpc-tests/getrawtransaction_insight.py b/qa/rpc-tests/getrawtransaction_insight.py new file mode 100755 index 00000000000..f18eed5d465 --- /dev/null +++ b/qa/rpc-tests/getrawtransaction_insight.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python2 +# Copyright (c) 2019 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test the new fields added to the output of getrawtransaction +# RPC for the Insight Explorer by the new spentindex +# + +from test_framework.test_framework import BitcoinTestFramework + +from test_framework.util import assert_equal +from test_framework.util import initialize_chain_clean +from test_framework.util import start_nodes, stop_nodes, connect_nodes +from test_framework.util import wait_bitcoinds + +from test_framework.mininode import COIN + + +class GetrawtransactionTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def setup_network(self): + # -insightexplorer causes spentindex to be enabled (fSpentIndex = true) + + self.nodes = start_nodes(3, self.options.tmpdir, + [['-debug', '-txindex', '-experimentalfeatures', '-insightexplorer']]*3) + connect_nodes(self.nodes[0], 1) + connect_nodes(self.nodes[0], 2) + + self.is_network_split = False + self.sync_all() + + def run_test(self): + self.nodes[0].generate(105) + self.sync_all() + + chain_height = self.nodes[1].getblockcount() + assert_equal(chain_height, 105) + + # Test getrawtransaction changes and the getspentinfo RPC + + # send coinbase to address a + a = self.nodes[1].getnewaddress() + txid_a = self.nodes[0].sendtoaddress(a, 2) + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + # send from a to b + # (the only utxo on node 1 is from address a) + b = self.nodes[2].getnewaddress() + txid_b = self.nodes[1].sendtoaddress(b, 1) + self.sync_all() + + # a to b transaction is not confirmed, so it has no height + tx_b = self.nodes[2].getrawtransaction(txid_b, 1) + assert('height' not in tx_b) + + self.sync_all() + tx_a = self.nodes[2].getrawtransaction(txid_a, 1) + + # txid_b is not yet confirmed, so these should not be set + assert('spentTxId' not in tx_a['vout'][0]) + assert('spentIndex' not in tx_a['vout'][0]) + assert('spentHeight' not in tx_a['vout'][0]) + + # confirm txid_b (a to b transaction) + self.nodes[0].generate(1) + self.sync_all() + + # Restart all nodes to ensure index files are saved to disk and recovered + stop_nodes(self.nodes) + wait_bitcoinds() + self.setup_network() + + # Check new fields added to getrawtransaction + tx_a = self.nodes[2].getrawtransaction(txid_a, 1) + assert_equal(tx_a['vin'][0]['value'], 10) # coinbase + assert_equal(tx_a['vin'][0]['valueSat'], 10*COIN) + # we want the non-change (payment) output + vout = filter(lambda o: o['value'] == 2, tx_a['vout']) + assert_equal(vout[0]['spentTxId'], txid_b) + assert_equal(vout[0]['spentIndex'], 0) + assert_equal(vout[0]['spentHeight'], 107) + assert_equal(tx_a['height'], 106) + + tx_b = self.nodes[2].getrawtransaction(txid_b, 1) + assert_equal(tx_b['vin'][0]['address'], a) + assert_equal(tx_b['vin'][0]['value'], 2) + assert_equal(tx_b['vin'][0]['valueSat'], 2*COIN) + # since this transaction's outputs haven't yet been + # spent, these fields should not be present + assert('spentTxId' not in tx_b['vout'][0]) + assert('spentIndex' not in tx_b['vout'][0]) + assert('spentHeight' not in tx_b['vout'][0]) + assert_equal(tx_b['height'], 107) + +if __name__ == '__main__': + GetrawtransactionTest().main() diff --git a/qa/rpc-tests/mergetoaddress_helper.py b/qa/rpc-tests/mergetoaddress_helper.py index cc829f8b8aa..962c2b7ff49 100755 --- a/qa/rpc-tests/mergetoaddress_helper.py +++ b/qa/rpc-tests/mergetoaddress_helper.py @@ -17,11 +17,12 @@ def assert_mergetoaddress_exception(expected_error_msg, merge_to_address_lambda): try: merge_to_address_lambda() - fail("Expected exception: %s" % expected_error_msg) except JSONRPCException as e: assert_equal(expected_error_msg, e.error['message']) except Exception as e: fail("Expected JSONRPCException. Found %s" % repr(e)) + else: + fail("Expected exception: %s" % expected_error_msg) class MergeToAddressHelper: @@ -152,6 +153,11 @@ def run_test(self, test): "Destination address is also the only source address, and all its funds are already merged.", lambda: test.nodes[0].z_mergetoaddress([mytaddr], mytaddr)) + # Merging will fail for this specific case where it would spend a fee and do nothing + assert_mergetoaddress_exception( + "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress", + lambda: test.nodes[0].z_mergetoaddress(["ANY_SPROUT", "ANY_SAPLING"], mytaddr)) + # Merge UTXOs from node 0 of value 30, standard fee of 0.00010000 result = test.nodes[0].z_mergetoaddress([mytaddr, mytaddr2, mytaddr3], myzaddr) wait_and_assert_operationid_status(test.nodes[0], result['opid']) diff --git a/qa/rpc-tests/mergetoaddress_mixednotes.py b/qa/rpc-tests/mergetoaddress_mixednotes.py new file mode 100755 index 00000000000..b787429aa99 --- /dev/null +++ b/qa/rpc-tests/mergetoaddress_mixednotes.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# Copyright (c) 2019 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." + +from decimal import Decimal +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, get_coinbase_address, \ + initialize_chain_clean, start_nodes, wait_and_assert_operationid_status +from mergetoaddress_helper import assert_mergetoaddress_exception + + +class MergeToAddressMixedNotes(BitcoinTestFramework): + def setup_nodes(self): + return start_nodes(4, self.options.tmpdir, [[ + '-nuparams=5ba81b19:100', # Overwinter + '-nuparams=76b809bb:100', # Sapling + '-experimentalfeatures', '-zmergetoaddress' + ]] * 4) + + def setup_chain(self): + print("Initializing test directory " + self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def run_test(self): + print "Mining blocks..." + self.nodes[0].generate(102) + self.sync_all() + + # Send some ZEC to Sprout/Sapling addresses + coinbase_addr = get_coinbase_address(self.nodes[0]) + sproutAddr = self.nodes[0].z_getnewaddress('sprout') + saplingAddr = self.nodes[0].z_getnewaddress('sapling') + t_addr = self.nodes[1].getnewaddress() + + opid = self.nodes[0].z_sendmany(coinbase_addr, [{"address": sproutAddr, "amount": Decimal('10')}], 1, 0) + wait_and_assert_operationid_status(self.nodes[0], opid) + self.nodes[0].generate(1) + self.sync_all() + assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('10')) + assert_equal(self.nodes[0].z_getbalance(saplingAddr), Decimal('0')) + assert_equal(Decimal(self.nodes[1].z_gettotalbalance()["transparent"]), Decimal('0')) + # Make sure we cannot use "ANY_SPROUT" and "ANY_SAPLING" even if we only have Sprout Notes + assert_mergetoaddress_exception( + "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress", + lambda: self.nodes[0].z_mergetoaddress(["ANY_SPROUT", "ANY_SAPLING"], t_addr)) + opid = self.nodes[0].z_sendmany(coinbase_addr, [{"address": saplingAddr, "amount": Decimal('10')}], 1, 0) + wait_and_assert_operationid_status(self.nodes[0], opid) + self.nodes[0].generate(1) + self.sync_all() + + assert_equal(Decimal(self.nodes[1].z_gettotalbalance()["transparent"]), Decimal('0')) + + # Merge Sprout -> taddr + result = self.nodes[0].z_mergetoaddress(["ANY_SPROUT"], t_addr, 0) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + self.nodes[0].generate(1) + self.sync_all() + + assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('0')) + assert_equal(self.nodes[0].z_getbalance(saplingAddr), Decimal('10')) + assert_equal(Decimal(self.nodes[1].z_gettotalbalance()["transparent"]), Decimal('10')) + + # Make sure we cannot use "ANY_SPROUT" and "ANY_SAPLING" even if we only have Sapling Notes + assert_mergetoaddress_exception( + "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress", + lambda: self.nodes[0].z_mergetoaddress(["ANY_SPROUT", "ANY_SAPLING"], t_addr)) + # Merge Sapling -> taddr + result = self.nodes[0].z_mergetoaddress(["ANY_SAPLING"], t_addr, 0) + wait_and_assert_operationid_status(self.nodes[0], result['opid']) + self.nodes[0].generate(1) + self.sync_all() + + assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('0')) + assert_equal(self.nodes[0].z_getbalance(saplingAddr), Decimal('0')) + assert_equal(Decimal(self.nodes[1].z_gettotalbalance()["transparent"]), Decimal('20')) + + +if __name__ == '__main__': + MergeToAddressMixedNotes().main() diff --git a/qa/rpc-tests/sprout_sapling_migration.py b/qa/rpc-tests/sprout_sapling_migration.py new file mode 100755 index 00000000000..03e1ccde77c --- /dev/null +++ b/qa/rpc-tests/sprout_sapling_migration.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# Copyright (c) 2019 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." + +from decimal import Decimal +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, assert_true, get_coinbase_address, \ + initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, \ + wait_and_assert_operationid_status_result + +SAPLING_ADDR = 'zregtestsapling1ssqj3f3majnl270985gqcdqedd9t4nlttjqskccwevj2v20sc25deqspv3masufnwcdy67cydyy' +SAPLING_KEY = 'secret-extended-key-regtest1qv62zt2fqyqqpqrh2qzc08h7gncf4447jh9kvnnnhjg959fkwt7mhw9j8e9at7attx8z6u3953u86vcnsujdc2ckdlcmztjt44x3uxpah5mxtncxd0mqcnz9eq8rghh5m4j44ep5d9702sdvvwawqassulktfegrcp4twxgqdxx4eww3lau0mywuaeztpla2cmvagr5nj98elt45zh6fjznadl6wz52n2uyhdwcm2wlsu8fnxstrk6s4t55t8dy6jkgx5g0cwpchh5qffp8x5' + + +def check_migration_status( + node, + enabled, + destination_address, + non_zero_unmigrated_amount, + non_zero_unfinalized_migrated_amount, + non_zero_finalized_migrated_amount, + finalized_migration_transactions, + len_migration_txids +): + status = node.z_getmigrationstatus() + assert_equal(enabled, status['enabled']) + assert_equal(destination_address, status['destination_address']) + assert_equal(non_zero_unmigrated_amount, Decimal(status['unmigrated_amount']) > Decimal('0.00')) + assert_equal(non_zero_unfinalized_migrated_amount, Decimal(status['unfinalized_migrated_amount']) > Decimal('0')) + assert_equal(non_zero_finalized_migrated_amount, Decimal(status['finalized_migrated_amount']) > Decimal('0')) + assert_equal(finalized_migration_transactions, status['finalized_migration_transactions']) + assert_equal(len_migration_txids, len(status['migration_txids'])) + + +class SproutSaplingMigration(BitcoinTestFramework): + def setup_nodes(self): + # Activate overwinter/sapling on all nodes + extra_args = [[ + '-nuparams=5ba81b19:100', # Overwinter + '-nuparams=76b809bb:100', # Sapling + ]] * 4 + # Add migration parameters to nodes[0] + extra_args[0] = extra_args[0] + [ + '-migration', + '-migrationdestaddress=' + SAPLING_ADDR, + '-debug=zrpcunsafe' + ] + assert_equal(5, len(extra_args[0])) + assert_equal(2, len(extra_args[1])) + return start_nodes(4, self.options.tmpdir, extra_args) + + def setup_chain(self): + print("Initializing test directory " + self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + def run_migration_test(self, node, sproutAddr, saplingAddr, target_height): + # Make sure we are in a good state to run the test + assert_equal(102, node.getblockcount() % 500, "Should be at block 102 % 500") + assert_equal(node.z_getbalance(sproutAddr), Decimal('10')) + assert_equal(node.z_getbalance(saplingAddr), Decimal('0')) + check_migration_status(node, False, saplingAddr, True, False, False, 0, 0) + + # Migrate + node.z_setmigration(True) + print("Mining to block 494 % 500...") + node.generate(392) # 102 % 500 -> 494 % 500 + self.sync_all() + + # At 494 % 500 we should have no async operations + assert_equal(0, len(node.z_getoperationstatus()), "num async operations at 494 % 500") + check_migration_status(node, True, saplingAddr, True, False, False, 0, 0) + + node.generate(1) + self.sync_all() + + # At 495 % 500 we should have an async operation + operationstatus = node.z_getoperationstatus() + print("migration operation: {}".format(operationstatus)) + assert_equal(1, len(operationstatus), "num async operations at 495 % 500") + assert_equal('saplingmigration', operationstatus[0]['method']) + assert_equal(target_height, operationstatus[0]['target_height']) + + result = wait_and_assert_operationid_status_result(node, operationstatus[0]['id']) + print("result: {}".format(result)) + assert_equal('saplingmigration', result['method']) + assert_equal(target_height, result['target_height']) + assert_equal(1, result['result']['num_tx_created']) + assert_equal(1, len(result['result']['migration_txids'])) + assert_true(result['result']['amount_migrated'] > Decimal('0')) + + assert_equal(0, len(node.getrawmempool()), "mempool size at 495 % 500") + + node.generate(3) + self.sync_all() + + # At 498 % 500 the mempool will be empty and no funds will have moved + assert_equal(0, len(node.getrawmempool()), "mempool size at 498 % 500") + assert_equal(node.z_getbalance(sproutAddr), Decimal('10')) + assert_equal(node.z_getbalance(saplingAddr), Decimal('0')) + + node.generate(1) + self.sync_all() + + # At 499 % 500 there will be a transaction in the mempool and the note will be locked + mempool = node.getrawmempool() + print("mempool: {}".format(mempool)) + assert_equal(1, len(mempool), "mempool size at 499 % 500") + assert_equal(node.z_getbalance(sproutAddr), Decimal('0')) + assert_equal(node.z_getbalance(saplingAddr), Decimal('0')) + assert_true(node.z_getbalance(saplingAddr, 0) > Decimal('0'), "Unconfirmed sapling balance at 499 % 500") + # Check that unmigrated amount + unfinalized = starting balance - fee + status = node.z_getmigrationstatus() + print("status: {}".format(status)) + assert_equal(Decimal('9.9999'), Decimal(status['unmigrated_amount']) + Decimal(status['unfinalized_migrated_amount'])) + + # The transaction in the mempool should be the one listed in migration_txids, + # and it should expire at the next 450 % 500. + assert_equal(1, len(status['migration_txids'])) + txid = status['migration_txids'][0] + assert_equal(txid, mempool[0]) + tx = node.getrawtransaction(txid, 1) + assert_equal(target_height + 450, tx['expiryheight']) + + node.generate(1) + self.sync_all() + + # At 0 % 500 funds will have moved + sprout_balance = node.z_getbalance(sproutAddr) + sapling_balance = node.z_getbalance(saplingAddr) + print("sprout balance: {}, sapling balance: {}".format(sprout_balance, sapling_balance)) + assert_true(sprout_balance < Decimal('10'), "Should have less Sprout funds") + assert_true(sapling_balance > Decimal('0'), "Should have more Sapling funds") + assert_true(sprout_balance + sapling_balance, Decimal('9.9999')) + + check_migration_status(node, True, saplingAddr, True, True, False, 0, 1) + # At 10 % 500 the transactions will be considered 'finalized' + node.generate(10) + self.sync_all() + check_migration_status(node, True, saplingAddr, True, False, True, 1, 1) + # Check exact migration status amounts to make sure we account for fee + status = node.z_getmigrationstatus() + assert_equal(sprout_balance, Decimal(status['unmigrated_amount'])) + assert_equal(sapling_balance, Decimal(status['finalized_migrated_amount'])) + + def send_to_sprout_zaddr(self, tAddr, sproutAddr): + # Send some ZEC to a Sprout address + opid = self.nodes[0].z_sendmany(tAddr, [{"address": sproutAddr, "amount": Decimal('10')}], 1, 0) + wait_and_assert_operationid_status(self.nodes[0], opid) + self.nodes[0].generate(1) + self.sync_all() + + def run_test(self): + # Check enabling via '-migration' and disabling via rpc + check_migration_status(self.nodes[0], True, SAPLING_ADDR, False, False, False, 0, 0) + self.nodes[0].z_setmigration(False) + check_migration_status(self.nodes[0], False, SAPLING_ADDR, False, False, False, 0, 0) + + # 1. Test using self.nodes[0] which has the parameter + print("Running test using '-migrationdestaddress'...") + print("Mining blocks...") + self.nodes[0].generate(101) + self.sync_all() + tAddr = get_coinbase_address(self.nodes[0]) + + # Import a previously generated key to test '-migrationdestaddress' + self.nodes[0].z_importkey(SAPLING_KEY) + sproutAddr0 = self.nodes[0].z_getnewaddress('sprout') + + self.send_to_sprout_zaddr(tAddr, sproutAddr0) + self.run_migration_test(self.nodes[0], sproutAddr0, SAPLING_ADDR, 500) + # Disable migration so only self.nodes[1] has a transaction in the mempool at block 999 + self.nodes[0].z_setmigration(False) + + # 2. Test using self.nodes[1] which will use the default Sapling address + print("Running test using default Sapling address...") + # Mine more blocks so we start at 102 % 500 + print("Mining blocks...") + self.nodes[1].generate(91) # 511 -> 602 + self.sync_all() + + sproutAddr1 = self.nodes[1].z_getnewaddress('sprout') + saplingAddr1 = self.nodes[1].z_getnewaddress('sapling') + + self.send_to_sprout_zaddr(tAddr, sproutAddr1) + self.run_migration_test(self.nodes[1], sproutAddr1, saplingAddr1, 1000) + + +if __name__ == '__main__': + SproutSaplingMigration().main() diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 77abec55dcd..590cb2d48a1 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -385,8 +385,9 @@ def assert_raises(exc, fun, *args, **kwds): def fail(message=""): raise AssertionError(message) -# Returns txid if operation was a success or None -def wait_and_assert_operationid_status(node, myopid, in_status='success', in_errormsg=None, timeout=300): + +# Returns an async operation result +def wait_and_assert_operationid_status_result(node, myopid, in_status='success', in_errormsg=None, timeout=300): print('waiting for async operation {}'.format(myopid)) result = None for _ in xrange(1, timeout): @@ -399,26 +400,29 @@ def wait_and_assert_operationid_status(node, myopid, in_status='success', in_err assert_true(result is not None, "timeout occured") status = result['status'] - txid = None + debug = os.getenv("PYTHON_DEBUG", "") + if debug: + print('...returned status: {}'.format(status)) + errormsg = None if status == "failed": errormsg = result['error']['message'] - elif status == "success": - txid = result['result']['txid'] - - if os.getenv("PYTHON_DEBUG", ""): - print('...returned status: {}'.format(status)) - if errormsg is not None: + if debug: print('...returned error: {}'.format(errormsg)) - + assert_equal(in_errormsg, errormsg) + assert_equal(in_status, status, "Operation returned mismatched status. Error Message: {}".format(errormsg)) - if errormsg is not None: - assert_true(in_errormsg is not None, "No error retured. Expected: {}".format(errormsg)) - assert_true(in_errormsg in errormsg, "Error returned: {}. Error expected: {}".format(errormsg, in_errormsg)) - return result # if there was an error return the result + return result + + +# Returns txid if operation was a success or None +def wait_and_assert_operationid_status(node, myopid, in_status='success', in_errormsg=None, timeout=300): + result = wait_and_assert_operationid_status_result(node, myopid, in_status, in_errormsg, timeout) + if result['status'] == "success": + return result['result']['txid'] else: - return txid # otherwise return the txid + return None # Find a coinbase address on the node, filtering by the number of UTXOs it has. # If no filter is provided, returns the coinbase address on the node containing diff --git a/qa/rpc-tests/turnstile.py b/qa/rpc-tests/turnstile.py new file mode 100755 index 00000000000..89d680f0b13 --- /dev/null +++ b/qa/rpc-tests/turnstile.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# Copyright (c) 2019 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test Sprout and Sapling turnstile violations +# +# Experimental feature -developersetpoolsizezero will, upon node launch, +# set the in-memory size of shielded pools to zero. +# +# An unshielding operation can then be used to verify: +# 1. Turnstile violating transactions are excluded by the miner +# 2. Turnstile violating blocks are rejected by nodes +# +# By default, ZIP209 support is disabled in regtest mode, but gets enabled +# when experimental feature -developersetpoolsizezero is switched on. +# +# To perform a manual turnstile test on testnet: +# 1. Launch zcashd +# 2. Shield transparent funds +# 3. Wait for transaction to be mined +# 4. Restart zcashd, enabling experimental feature -developersetpoolsizezero +# 5. Unshield funds +# 6. Wait for transaction to be mined (using testnet explorer or another node) +# 7. Verify zcashd rejected the block +# + +import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + get_coinbase_address, + start_node, start_nodes, + sync_blocks, sync_mempools, + initialize_chain_clean, connect_nodes_bi, + wait_and_assert_operationid_status, + bitcoind_processes +) +from decimal import Decimal + +NUPARAMS_ARGS = ['-nuparams=5ba81b19:100', # Overwinter + '-nuparams=76b809bb:101'] # Sapling +TURNSTILE_ARGS = ['-experimentalfeatures', + '-developersetpoolsizezero'] + +class TurnstileTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory " + self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 3) + + def setup_network(self, split=False): + self.nodes = start_nodes(3, self.options.tmpdir, + extra_args=[NUPARAMS_ARGS] * 3) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + self.is_network_split=False + self.sync_all() + + # Helper method to verify the size of a shielded value pool for a given node + def assert_pool_balance(self, node, name, balance): + pools = node.getblockchaininfo()['valuePools'] + for pool in pools: + if pool['id'] == name: + assert_equal(pool['chainValue'], balance, message="for pool named %r" % (name,)) + return + assert False, "pool named %r not found" % (name,) + + # Helper method to start a single node with extra args and sync to the network + def start_and_sync_node(self, index, args=[]): + self.nodes[index] = start_node(index, self.options.tmpdir, extra_args=NUPARAMS_ARGS + args) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + self.sync_all() + + # Helper method to stop and restart a single node with extra args and sync to the network + def restart_and_sync_node(self, index, args=[]): + self.nodes[index].stop() + bitcoind_processes[index].wait() + self.start_and_sync_node(index, args) + + def run_test(self): + # Sanity-check the test harness + self.nodes[0].generate(101) + assert_equal(self.nodes[0].getblockcount(), 101) + self.sync_all() + + # Node 0 shields some funds + dest_addr = self.nodes[0].z_getnewaddress(POOL_NAME.lower()) + taddr0 = get_coinbase_address(self.nodes[0]) + recipients = [] + recipients.append({"address": dest_addr, "amount": Decimal('10')}) + myopid = self.nodes[0].z_sendmany(taddr0, recipients, 1, 0) + wait_and_assert_operationid_status(self.nodes[0], myopid) + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + assert_equal(self.nodes[0].z_getbalance(dest_addr), Decimal('10')) + + # Verify size of shielded pool + self.assert_pool_balance(self.nodes[0], POOL_NAME.lower(), Decimal('10')) + self.assert_pool_balance(self.nodes[1], POOL_NAME.lower(), Decimal('10')) + self.assert_pool_balance(self.nodes[2], POOL_NAME.lower(), Decimal('10')) + + # Relaunch node 0 with in-memory size of value pools set to zero. + self.restart_and_sync_node(0, TURNSTILE_ARGS) + + # Verify size of shielded pool + self.assert_pool_balance(self.nodes[0], POOL_NAME.lower(), Decimal('0')) + self.assert_pool_balance(self.nodes[1], POOL_NAME.lower(), Decimal('10')) + self.assert_pool_balance(self.nodes[2], POOL_NAME.lower(), Decimal('10')) + + # Node 0 creates an unshielding transaction + recipients = [] + recipients.append({"address": taddr0, "amount": Decimal('1')}) + myopid = self.nodes[0].z_sendmany(dest_addr, recipients, 1, 0) + mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) + + # Verify transaction appears in mempool of nodes + self.sync_all() + assert(mytxid in self.nodes[0].getrawmempool()) + assert(mytxid in self.nodes[1].getrawmempool()) + assert(mytxid in self.nodes[2].getrawmempool()) + + # Node 0 mines a block + count = self.nodes[0].getblockcount() + self.nodes[0].generate(1) + self.sync_all() + + # Verify the mined block does not contain the unshielding transaction + block = self.nodes[0].getblock(self.nodes[0].getbestblockhash()) + assert_equal(len(block["tx"]), 1) + assert_equal(block["height"], count + 1) + + # Stop node 0 and check logs to verify the miner excluded the transaction from the block + self.nodes[0].stop() + bitcoind_processes[0].wait() + logpath = self.options.tmpdir + "/node0/regtest/debug.log" + foundErrorMsg = False + with open(logpath, "r") as myfile: + logdata = myfile.readlines() + for logline in logdata: + if "CreateNewBlock(): tx " + mytxid + " appears to violate " + POOL_NAME.capitalize() + " turnstile" in logline: + foundErrorMsg = True + break + assert(foundErrorMsg) + + # Launch node 0 with in-memory size of value pools set to zero. + self.start_and_sync_node(0, TURNSTILE_ARGS) + + # Node 1 mines a block + oldhash = self.nodes[0].getbestblockhash() + self.nodes[1].generate(1) + newhash = self.nodes[1].getbestblockhash() + + # Verify block contains the unshielding transaction + assert(mytxid in self.nodes[1].getblock(newhash)["tx"]) + + # Verify nodes 1 and 2 have accepted the block as valid + sync_blocks(self.nodes[1:3]) + sync_mempools(self.nodes[1:3]) + assert_equal(len(self.nodes[1].getrawmempool()), 0) + assert_equal(len(self.nodes[2].getrawmempool()), 0) + + # Verify node 0 has not accepted the block + assert_equal(oldhash, self.nodes[0].getbestblockhash()) + assert(mytxid in self.nodes[0].getrawmempool()) + self.assert_pool_balance(self.nodes[0], POOL_NAME.lower(), Decimal('0')) + + # Verify size of shielded pool + self.assert_pool_balance(self.nodes[0], POOL_NAME.lower(), Decimal('0')) + self.assert_pool_balance(self.nodes[1], POOL_NAME.lower(), Decimal('9')) + self.assert_pool_balance(self.nodes[2], POOL_NAME.lower(), Decimal('9')) + + # Stop node 0 and check logs to verify the block was rejected as a turnstile violation + self.nodes[0].stop() + bitcoind_processes[0].wait() + logpath = self.options.tmpdir + "/node0/regtest/debug.log" + foundConnectBlockErrorMsg = False + foundInvalidBlockErrorMsg = False + foundConnectTipErrorMsg = False + with open(logpath, "r") as myfile: + logdata = myfile.readlines() + for logline in logdata: + if "ConnectBlock(): turnstile violation in " + POOL_NAME.capitalize() + " shielded value pool" in logline: + foundConnectBlockErrorMsg = True + elif "InvalidChainFound: invalid block=" + newhash in logline: + foundInvalidBlockErrorMsg = True + elif "ConnectTip(): ConnectBlock " + newhash + " failed" in logline: + foundConnectTipErrorMsg = True + assert(foundConnectBlockErrorMsg and foundInvalidBlockErrorMsg and foundConnectTipErrorMsg) + + # Launch node 0 without overriding the pool size, so the node can sync with rest of network. + self.start_and_sync_node(0) + assert_equal(newhash, self.nodes[0].getbestblockhash()) + +if __name__ == '__main__': + POOL_NAME = "SPROUT" + TurnstileTest().main() + POOL_NAME = "SAPLING" + TurnstileTest().main() diff --git a/qa/rpc-tests/wallet_mergetoaddress.py b/qa/rpc-tests/wallet_mergetoaddress.py deleted file mode 100755 index 3c2f84aa85f..00000000000 --- a/qa/rpc-tests/wallet_mergetoaddress.py +++ /dev/null @@ -1,368 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2017 The Zcash developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, initialize_chain_clean, \ - start_node, connect_nodes_bi, sync_blocks, sync_mempools, \ - wait_and_assert_operationid_status - -from decimal import Decimal - -class WalletMergeToAddressTest (BitcoinTestFramework): - - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) - - def setup_network(self, split=False): - args = ['-debug=zrpcunsafe', '-experimentalfeatures', '-zmergetoaddress'] - self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, args)) - self.nodes.append(start_node(1, self.options.tmpdir, args)) - args2 = ['-debug=zrpcunsafe', '-experimentalfeatures', '-zmergetoaddress', '-mempooltxinputlimit=7'] - self.nodes.append(start_node(2, self.options.tmpdir, args2)) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) - self.is_network_split=False - self.sync_all() - - def run_test (self): - print "Mining blocks..." - - self.nodes[0].generate(1) - do_not_shield_taddr = self.nodes[0].getnewaddress() - - self.nodes[0].generate(4) - walletinfo = self.nodes[0].getwalletinfo() - assert_equal(walletinfo['immature_balance'], 50) - assert_equal(walletinfo['balance'], 0) - self.sync_all() - self.nodes[2].generate(1) - self.nodes[2].getnewaddress() - self.nodes[2].generate(1) - self.nodes[2].getnewaddress() - self.nodes[2].generate(1) - self.sync_all() - self.nodes[1].generate(101) - self.sync_all() - assert_equal(self.nodes[0].getbalance(), 50) - assert_equal(self.nodes[1].getbalance(), 10) - assert_equal(self.nodes[2].getbalance(), 30) - - # Shield the coinbase - myzaddr = self.nodes[0].z_getnewaddress('sprout') - result = self.nodes[0].z_shieldcoinbase("*", myzaddr, 0) - wait_and_assert_operationid_status(self.nodes[0], result['opid']) - self.sync_all() - self.nodes[1].generate(1) - self.sync_all() - - # Prepare some UTXOs and notes for merging - mytaddr = self.nodes[0].getnewaddress() - mytaddr2 = self.nodes[0].getnewaddress() - mytaddr3 = self.nodes[0].getnewaddress() - result = self.nodes[0].z_sendmany(myzaddr, [ - {'address': do_not_shield_taddr, 'amount': 10}, - {'address': mytaddr, 'amount': 10}, - {'address': mytaddr2, 'amount': 10}, - {'address': mytaddr3, 'amount': 10}, - ], 1, 0) - wait_and_assert_operationid_status(self.nodes[0], result) - self.sync_all() - self.nodes[1].generate(1) - self.sync_all() - - # Merging will fail because from arguments need to be in an array - try: - self.nodes[0].z_mergetoaddress("*", myzaddr) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("JSON value is not an array as expected" in errorString, True) - - # Merging will fail when trying to spend from watch-only address - self.nodes[2].importaddress(mytaddr) - try: - self.nodes[2].z_mergetoaddress([mytaddr], myzaddr) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("Could not find any funds to merge" in errorString, True) - - # Merging will fail because fee is negative - try: - self.nodes[0].z_mergetoaddress(["*"], myzaddr, -1) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("Amount out of range" in errorString, True) - - # Merging will fail because fee is larger than MAX_MONEY - try: - self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('21000000.00000001')) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("Amount out of range" in errorString, True) - - # Merging will fail because fee is larger than sum of UTXOs - try: - self.nodes[0].z_mergetoaddress(["*"], myzaddr, 999) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("Insufficient funds" in errorString, True) - - # Merging will fail because transparent limit parameter must be at least 0 - try: - self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), -1) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("Limit on maximum number of UTXOs cannot be negative" in errorString, True) - - # Merging will fail because transparent limit parameter is absurdly large - try: - self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 99999999999999) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("JSON integer out of range" in errorString, True) - - # Merging will fail because shielded limit parameter must be at least 0 - try: - self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, -1) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("Limit on maximum number of notes cannot be negative" in errorString, True) - - # Merging will fail because shielded limit parameter is absurdly large - try: - self.nodes[0].z_mergetoaddress(["*"], myzaddr, Decimal('0.001'), 50, 99999999999999) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("JSON integer out of range" in errorString, True) - - # Merging will fail for this specific case where it would spend a fee and do nothing - try: - self.nodes[0].z_mergetoaddress([mytaddr], mytaddr) - assert(False) - except JSONRPCException,e: - errorString = e.error['message'] - assert_equal("Destination address is also the only source address, and all its funds are already merged" in errorString, True) - - # Merge UTXOs from node 0 of value 30, standard fee of 0.00010000 - result = self.nodes[0].z_mergetoaddress([mytaddr, mytaddr2, mytaddr3], myzaddr) - wait_and_assert_operationid_status(self.nodes[0], result['opid']) - self.sync_all() - self.nodes[1].generate(1) - self.sync_all() - - # Confirm balances and that do_not_shield_taddr containing funds of 10 was left alone - assert_equal(self.nodes[0].getbalance(), 10) - assert_equal(self.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) - assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('39.99990000')) - assert_equal(self.nodes[1].getbalance(), 40) - assert_equal(self.nodes[2].getbalance(), 30) - - # Shield all notes to another z-addr - myzaddr2 = self.nodes[0].z_getnewaddress('sprout') - result = self.nodes[0].z_mergetoaddress(["ANY_ZADDR"], myzaddr2, 0) - assert_equal(result["mergingUTXOs"], Decimal('0')) - assert_equal(result["remainingUTXOs"], Decimal('0')) - assert_equal(result["mergingNotes"], Decimal('2')) - assert_equal(result["remainingNotes"], Decimal('0')) - wait_and_assert_operationid_status(self.nodes[0], result['opid']) - self.sync_all() - blockhash = self.nodes[1].generate(1) - self.sync_all() - - assert_equal(len(self.nodes[0].getblock(blockhash[0])['tx']), 2) - assert_equal(self.nodes[0].z_getbalance(myzaddr), 0) - assert_equal(self.nodes[0].z_getbalance(myzaddr2), Decimal('39.99990000')) - - # Shield coinbase UTXOs from any node 2 taddr, and set fee to 0 - result = self.nodes[2].z_shieldcoinbase("*", myzaddr, 0) - wait_and_assert_operationid_status(self.nodes[2], result['opid']) - self.sync_all() - self.nodes[1].generate(1) - self.sync_all() - - assert_equal(self.nodes[0].getbalance(), 10) - assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('30')) - assert_equal(self.nodes[0].z_getbalance(myzaddr2), Decimal('39.99990000')) - assert_equal(self.nodes[1].getbalance(), 60) - assert_equal(self.nodes[2].getbalance(), 0) - - # Merge all notes from node 0 into a node 0 taddr, and set fee to 0 - result = self.nodes[0].z_mergetoaddress(["ANY_ZADDR"], mytaddr, 0) - wait_and_assert_operationid_status(self.nodes[0], result['opid']) - self.sync_all() - self.nodes[1].generate(1) - self.sync_all() - - assert_equal(self.nodes[0].getbalance(), Decimal('79.99990000')) - assert_equal(self.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) - assert_equal(self.nodes[0].z_getbalance(mytaddr), Decimal('69.99990000')) - assert_equal(self.nodes[0].z_getbalance(myzaddr), 0) - assert_equal(self.nodes[0].z_getbalance(myzaddr2), 0) - assert_equal(self.nodes[1].getbalance(), 70) - assert_equal(self.nodes[2].getbalance(), 0) - - # Merge all node 0 UTXOs together into a node 1 taddr, and set fee to 0 - self.nodes[1].getnewaddress() # Ensure we have an empty address - n1taddr = self.nodes[1].getnewaddress() - result = self.nodes[0].z_mergetoaddress(["ANY_TADDR"], n1taddr, 0) - wait_and_assert_operationid_status(self.nodes[0], result['opid']) - self.sync_all() - self.nodes[1].generate(1) - self.sync_all() - - assert_equal(self.nodes[0].getbalance(), 0) - assert_equal(self.nodes[0].z_getbalance(do_not_shield_taddr), 0) - assert_equal(self.nodes[0].z_getbalance(mytaddr), 0) - assert_equal(self.nodes[0].z_getbalance(myzaddr), 0) - assert_equal(self.nodes[1].getbalance(), Decimal('159.99990000')) - assert_equal(self.nodes[1].z_getbalance(n1taddr), Decimal('79.99990000')) - assert_equal(self.nodes[2].getbalance(), 0) - - # Generate 800 regular UTXOs on node 0, and 20 regular UTXOs on node 2 - mytaddr = self.nodes[0].getnewaddress() - n2taddr = self.nodes[2].getnewaddress() - self.nodes[1].generate(1000) - self.sync_all() - for i in range(800): - self.nodes[1].sendtoaddress(mytaddr, 1) - for i in range(20): - self.nodes[1].sendtoaddress(n2taddr, 1) - self.nodes[1].generate(1) - self.sync_all() - - # Merging the 800 UTXOs will occur over two transactions, since max tx size is 100,000 bytes. - # We don't verify mergingTransparentValue as UTXOs are not selected in any specific order, so value can change on each test run. - # We set an unrealistically high limit parameter of 99999, to verify that max tx size will constrain the number of UTXOs. - result = self.nodes[0].z_mergetoaddress([mytaddr], myzaddr, 0, 99999) - assert_equal(result["mergingUTXOs"], Decimal('662')) - assert_equal(result["remainingUTXOs"], Decimal('138')) - assert_equal(result["mergingNotes"], Decimal('0')) - assert_equal(result["mergingShieldedValue"], Decimal('0')) - assert_equal(result["remainingNotes"], Decimal('0')) - assert_equal(result["remainingShieldedValue"], Decimal('0')) - remainingTransparentValue = result["remainingTransparentValue"] - opid1 = result['opid'] - - # Verify that UTXOs are locked (not available for selection) by queuing up another merging operation - result = self.nodes[0].z_mergetoaddress([mytaddr], myzaddr, 0, 0) - assert_equal(result["mergingUTXOs"], Decimal('138')) - assert_equal(result["mergingTransparentValue"], Decimal(remainingTransparentValue)) - assert_equal(result["remainingUTXOs"], Decimal('0')) - assert_equal(result["remainingTransparentValue"], Decimal('0')) - assert_equal(result["mergingNotes"], Decimal('0')) - assert_equal(result["mergingShieldedValue"], Decimal('0')) - assert_equal(result["remainingNotes"], Decimal('0')) - assert_equal(result["remainingShieldedValue"], Decimal('0')) - opid2 = result['opid'] - - # wait for both aysnc operations to complete - wait_and_assert_operationid_status(self.nodes[0], opid1) - wait_and_assert_operationid_status(self.nodes[0], opid2) - - # sync_all() invokes sync_mempool() but node 2's mempool limit will cause tx1 and tx2 to be rejected. - # So instead, we sync on blocks and mempool for node 0 and node 1, and after a new block is generated - # which mines tx1 and tx2, all nodes will have an empty mempool which can then be synced. - sync_blocks(self.nodes[:2]) - sync_mempools(self.nodes[:2]) - # Generate enough blocks to ensure all transactions are mined - while self.nodes[1].getmempoolinfo()['size'] > 0: - self.nodes[1].generate(1) - self.sync_all() - - # Verify maximum number of UTXOs which node 2 can shield is limited by option -mempooltxinputlimit - # This option is used when the limit parameter is set to 0. - result = self.nodes[2].z_mergetoaddress([n2taddr], myzaddr, Decimal('0.0001'), 0) - assert_equal(result["mergingUTXOs"], Decimal('7')) - assert_equal(result["remainingUTXOs"], Decimal('13')) - assert_equal(result["mergingNotes"], Decimal('0')) - assert_equal(result["remainingNotes"], Decimal('0')) - wait_and_assert_operationid_status(self.nodes[2], result['opid']) - self.sync_all() - self.nodes[1].generate(1) - self.sync_all() - - # Verify maximum number of UTXOs which node 0 can shield is set by default limit parameter of 50 - mytaddr = self.nodes[0].getnewaddress() - for i in range(100): - self.nodes[1].sendtoaddress(mytaddr, 1) - self.nodes[1].generate(1) - self.sync_all() - result = self.nodes[0].z_mergetoaddress([mytaddr], myzaddr, Decimal('0.0001')) - assert_equal(result["mergingUTXOs"], Decimal('50')) - assert_equal(result["remainingUTXOs"], Decimal('50')) - assert_equal(result["mergingNotes"], Decimal('0')) - # Remaining notes are only counted if we are trying to merge any notes - assert_equal(result["remainingNotes"], Decimal('0')) - wait_and_assert_operationid_status(self.nodes[0], result['opid']) - - # Verify maximum number of UTXOs which node 0 can shield can be set by the limit parameter - result = self.nodes[0].z_mergetoaddress([mytaddr], myzaddr, Decimal('0.0001'), 33) - assert_equal(result["mergingUTXOs"], Decimal('33')) - assert_equal(result["remainingUTXOs"], Decimal('17')) - assert_equal(result["mergingNotes"], Decimal('0')) - # Remaining notes are only counted if we are trying to merge any notes - assert_equal(result["remainingNotes"], Decimal('0')) - wait_and_assert_operationid_status(self.nodes[0], result['opid']) - # Don't sync node 2 which rejects the tx due to its mempooltxinputlimit - sync_blocks(self.nodes[:2]) - sync_mempools(self.nodes[:2]) - self.nodes[1].generate(1) - self.sync_all() - - # Verify maximum number of notes which node 0 can shield can be set by the limit parameter - # Also check that we can set off a second merge before the first one is complete - - # myzaddr has 5 notes at this point - result1 = self.nodes[0].z_mergetoaddress([myzaddr], myzaddr, 0.0001, 50, 2) - result2 = self.nodes[0].z_mergetoaddress([myzaddr], myzaddr, 0.0001, 50, 2) - - # First merge should select from all notes - assert_equal(result1["mergingUTXOs"], Decimal('0')) - # Remaining UTXOs are only counted if we are trying to merge any UTXOs - assert_equal(result1["remainingUTXOs"], Decimal('0')) - assert_equal(result1["mergingNotes"], Decimal('2')) - assert_equal(result1["remainingNotes"], Decimal('3')) - - # Second merge should ignore locked notes - assert_equal(result2["mergingUTXOs"], Decimal('0')) - assert_equal(result2["remainingUTXOs"], Decimal('0')) - assert_equal(result2["mergingNotes"], Decimal('2')) - assert_equal(result2["remainingNotes"], Decimal('1')) - wait_and_assert_operationid_status(self.nodes[0], result1['opid']) - wait_and_assert_operationid_status(self.nodes[0], result2['opid']) - - self.sync_all() - self.nodes[1].generate(1) - self.sync_all() - - # Shield both UTXOs and notes to a z-addr - result = self.nodes[0].z_mergetoaddress(["*"], myzaddr, 0, 10, 2) - assert_equal(result["mergingUTXOs"], Decimal('10')) - assert_equal(result["remainingUTXOs"], Decimal('7')) - assert_equal(result["mergingNotes"], Decimal('2')) - assert_equal(result["remainingNotes"], Decimal('1')) - wait_and_assert_operationid_status(self.nodes[0], result['opid']) - # Don't sync node 2 which rejects the tx due to its mempooltxinputlimit - sync_blocks(self.nodes[:2]) - sync_mempools(self.nodes[:2]) - self.nodes[1].generate(1) - self.sync_all() - -if __name__ == '__main__': - WalletMergeToAddressTest().main() diff --git a/qa/rpc-tests/wallet_protectcoinbase.py b/qa/rpc-tests/wallet_protectcoinbase.py index 85109931163..e7a3b9e5a1c 100755 --- a/qa/rpc-tests/wallet_protectcoinbase.py +++ b/qa/rpc-tests/wallet_protectcoinbase.py @@ -10,7 +10,7 @@ from test_framework.mininode import COIN from test_framework.util import assert_equal, initialize_chain_clean, \ start_nodes, connect_nodes_bi, wait_and_assert_operationid_status, \ - get_coinbase_address + wait_and_assert_operationid_status_result, get_coinbase_address import sys import timeit @@ -84,7 +84,7 @@ def run_test (self): recipients= [{"address":myzaddr, "amount": Decimal('1')}] myopid = self.nodes[3].z_sendmany(mytaddr, recipients) - wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "no UTXOs found for taddr from address", 10) + wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient funds, no UTXOs found for taddr from address.", 10) # This send will fail because our wallet does not allow any change when protecting a coinbase utxo, # as it's currently not possible to specify a change address in z_sendmany. @@ -92,7 +92,9 @@ def run_test (self): recipients.append({"address":myzaddr, "amount":Decimal('1.23456789')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - error_result = wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "wallet does not allow any change", 10) + error_result = wait_and_assert_operationid_status_result(self.nodes[0], myopid, "failed", ("Change 8.76533211 not allowed. " + "When shielding coinbase funds, the wallet does not allow any change " + "as there is currently no way to specify a change address in z_sendmany."), 10) # Test that the returned status object contains a params field with the operation's input parameters assert_equal(error_result["method"], "z_sendmany") diff --git a/responsible_disclosure.md b/responsible_disclosure.md index ddd6ca93b9c..718cf42d9c0 100644 --- a/responsible_disclosure.md +++ b/responsible_disclosure.md @@ -43,16 +43,17 @@ CDHCrwSovQRMHtoOWZijBNobO2y1d0FkUpzNlNw44Ssw0Vo= In the case where we become aware of security issues affecting other projects that has never affected Zcash, our intention is to inform those projects of security issues on a best effort basis. -In the case where we fix a security issue in Zcash that also affects the following neighboring projects, our intention is to engage in responsible disclosures with them as described in https://github.com/RD-Crypto-Spec/Responsible-Disclosure, subject to the deviations described in the that section. +In the case where we fix a security issue in Zcash that also affects the following neighboring projects, our intention is to engage in responsible disclosures with them as described in https://github.com/RD-Crypto-Spec/Responsible-Disclosure, subject to the deviations described in the section at the bottom of this document. ## Bilateral Responsible Disclosure Agreements -We have set up agreements with the following neighboring projects to share vulnerability information, subject to the deviaions described in the next section. +We have set up agreements with the following neighboring projects to share vulnerability information, subject to the deviations described in the next section. Specifically, we have agreed to engage in responsible disclosures for security issues affecting Zcash technology with the following contacts: -- security@horizen.com via PGP -- ca333@komodoplatform.com via PGP +- Horizen security@horizen.com via PGP +- Komodo ca333@komodoplatform.com via PGP +- BitcoinABC https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/DISCLOSURE_POLICY.md ## Deviations from the Standard diff --git a/src/Makefile.am b/src/Makefile.am index 5733549ff91..a7c7b12bc5d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -64,10 +64,10 @@ LIBSNARK_CONFIG_FLAGS += PLATFORM=darwin endif $(LIBSNARK): $(wildcard snark/src/*) - $(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" + $(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="$(LIBSNARK_OPTFLAGS)" libsnark-tests: $(wildcard snark/src/*) - $(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" + $(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="$(LIBSNARK_OPTFLAGS)" $(LIBUNIVALUE): $(wildcard univalue/lib/*) $(wildcard univalue/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) @@ -114,6 +114,7 @@ LIBZCASH_H = \ .PHONY: FORCE collate-libsnark check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ + addressindex.h \ addrman.h \ alert.h \ amount.h \ @@ -188,6 +189,7 @@ BITCOIN_CORE_H = \ script/sign.h \ script/standard.h \ serialize.h \ + spentindex.h \ streams.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ @@ -197,6 +199,7 @@ BITCOIN_CORE_H = \ sync.h \ threadsafety.h \ timedata.h \ + timestampindex.h \ tinyformat.h \ torcontrol.h \ transaction_builder.h \ @@ -214,6 +217,7 @@ BITCOIN_CORE_H = \ validationinterface.h \ version.h \ wallet/asyncrpcoperation_mergetoaddress.h \ + wallet/asyncrpcoperation_saplingmigration.h \ wallet/asyncrpcoperation_sendmany.h \ wallet/asyncrpcoperation_shieldcoinbase.h \ wallet/crypter.h \ @@ -303,6 +307,7 @@ libbitcoin_wallet_a_SOURCES = \ zcbenchmarks.cpp \ zcbenchmarks.h \ wallet/asyncrpcoperation_mergetoaddress.cpp \ + wallet/asyncrpcoperation_saplingmigration.cpp \ wallet/asyncrpcoperation_sendmany.cpp \ wallet/asyncrpcoperation_shieldcoinbase.cpp \ wallet/crypter.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 91526ceb681..1eb6b262e91 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -86,6 +86,7 @@ BITCOIN_TESTS =\ test/sanity_tests.cpp \ test/scheduler_tests.cpp \ test/script_P2SH_tests.cpp \ + test/script_P2PKH_tests.cpp \ test/script_tests.cpp \ test/scriptnum_tests.cpp \ test/serialize_tests.cpp \ diff --git a/src/addressindex.h b/src/addressindex.h new file mode 100644 index 00000000000..e83c9fc2fdc --- /dev/null +++ b/src/addressindex.h @@ -0,0 +1,225 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_ADDRESSINDEX_H +#define BITCOIN_ADDRESSINDEX_H + +#include "uint256.h" +#include "amount.h" +#include "script/script.h" + +struct CAddressUnspentKey { + unsigned int type; + uint160 hashBytes; + uint256 txhash; + size_t index; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 57; + } + template + void Serialize(Stream& s) const { + ser_writedata8(s, type); + hashBytes.Serialize(s); + txhash.Serialize(s); + ser_writedata32(s, index); + } + template + void Unserialize(Stream& s) { + type = ser_readdata8(s); + hashBytes.Unserialize(s); + txhash.Unserialize(s); + index = ser_readdata32(s); + } + + CAddressUnspentKey(unsigned int addressType, uint160 addressHash, uint256 txid, size_t indexValue) { + type = addressType; + hashBytes = addressHash; + txhash = txid; + index = indexValue; + } + + CAddressUnspentKey() { + SetNull(); + } + + void SetNull() { + type = 0; + hashBytes.SetNull(); + txhash.SetNull(); + index = 0; + } +}; + +struct CAddressUnspentValue { + CAmount satoshis; + CScript script; + int blockHeight; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(satoshis); + READWRITE(*(CScriptBase*)(&script)); + READWRITE(blockHeight); + } + + CAddressUnspentValue(CAmount sats, CScript scriptPubKey, int height) { + satoshis = sats; + script = scriptPubKey; + blockHeight = height; + } + + CAddressUnspentValue() { + SetNull(); + } + + void SetNull() { + satoshis = -1; + script.clear(); + blockHeight = 0; + } + + bool IsNull() const { + return (satoshis == -1); + } +}; + +struct CAddressIndexKey { + unsigned int type; + uint160 hashBytes; + int blockHeight; + unsigned int txindex; + uint256 txhash; + size_t index; + bool spending; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 66; + } + template + void Serialize(Stream& s) const { + ser_writedata8(s, type); + hashBytes.Serialize(s); + // Heights are stored big-endian for key sorting in LevelDB + ser_writedata32be(s, blockHeight); + ser_writedata32be(s, txindex); + txhash.Serialize(s); + ser_writedata32(s, index); + char f = spending; + ser_writedata8(s, f); + } + template + void Unserialize(Stream& s) { + type = ser_readdata8(s); + hashBytes.Unserialize(s); + blockHeight = ser_readdata32be(s); + txindex = ser_readdata32be(s); + txhash.Unserialize(s); + index = ser_readdata32(s); + char f = ser_readdata8(s); + spending = f; + } + + CAddressIndexKey(unsigned int addressType, uint160 addressHash, int height, int blockindex, + uint256 txid, size_t indexValue, bool isSpending) { + type = addressType; + hashBytes = addressHash; + blockHeight = height; + txindex = blockindex; + txhash = txid; + index = indexValue; + spending = isSpending; + } + + CAddressIndexKey() { + SetNull(); + } + + void SetNull() { + type = 0; + hashBytes.SetNull(); + blockHeight = 0; + txindex = 0; + txhash.SetNull(); + index = 0; + spending = false; + } + +}; + +struct CAddressIndexIteratorKey { + unsigned int type; + uint160 hashBytes; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 21; + } + template + void Serialize(Stream& s) const { + ser_writedata8(s, type); + hashBytes.Serialize(s); + } + template + void Unserialize(Stream& s) { + type = ser_readdata8(s); + hashBytes.Unserialize(s); + } + + CAddressIndexIteratorKey(unsigned int addressType, uint160 addressHash) { + type = addressType; + hashBytes = addressHash; + } + + CAddressIndexIteratorKey() { + SetNull(); + } + + void SetNull() { + type = 0; + hashBytes.SetNull(); + } +}; + +struct CAddressIndexIteratorHeightKey { + unsigned int type; + uint160 hashBytes; + int blockHeight; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 25; + } + template + void Serialize(Stream& s) const { + ser_writedata8(s, type); + hashBytes.Serialize(s); + ser_writedata32be(s, blockHeight); + } + template + void Unserialize(Stream& s) { + type = ser_readdata8(s); + hashBytes.Unserialize(s); + blockHeight = ser_readdata32be(s); + } + + CAddressIndexIteratorHeightKey(unsigned int addressType, uint160 addressHash, int height) { + type = addressType; + hashBytes = addressHash; + blockHeight = height; + } + + CAddressIndexIteratorHeightKey() { + SetNull(); + } + + void SetNull() { + type = 0; + hashBytes.SetNull(); + blockHeight = 0; + } +}; + +#endif // BITCOIN_ADDRESSINDEX_H diff --git a/src/amqp/amqppublishnotifier.cpp b/src/amqp/amqppublishnotifier.cpp index 589eb151fb6..2704d94c2c5 100644 --- a/src/amqp/amqppublishnotifier.cpp +++ b/src/amqp/amqppublishnotifier.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "amqppublishnotifier.h" +#include "chainparams.h" #include "main.h" #include "util.h" @@ -152,11 +153,12 @@ bool AMQPPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex) { LogPrint("amqp", "amqp: Publish rawblock %s\n", pindex->GetBlockHash().GetHex()); + const Consensus::Params& consensusParams = Params().GetConsensus(); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); { LOCK(cs_main); CBlock block; - if(!ReadBlockFromDisk(block, pindex)) { + if(!ReadBlockFromDisk(block, pindex, consensusParams)) { LogPrint("amqp", "amqp: Can't read block from disk"); return false; } diff --git a/src/chain.h b/src/chain.h index a532c85642f..5a7b3ad9cf4 100644 --- a/src/chain.h +++ b/src/chain.h @@ -14,11 +14,63 @@ #include -#include - static const int SPROUT_VALUE_VERSION = 1001400; static const int SAPLING_VALUE_VERSION = 1010100; +class CBlockFileInfo +{ +public: + unsigned int nBlocks; //!< number of blocks stored in file + unsigned int nSize; //!< number of used bytes of block file + unsigned int nUndoSize; //!< number of used bytes in the undo file + unsigned int nHeightFirst; //!< lowest height of block in file + unsigned int nHeightLast; //!< highest height of block in file + uint64_t nTimeFirst; //!< earliest time of block in file + uint64_t nTimeLast; //!< latest time of block in file + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(VARINT(nBlocks)); + READWRITE(VARINT(nSize)); + READWRITE(VARINT(nUndoSize)); + READWRITE(VARINT(nHeightFirst)); + READWRITE(VARINT(nHeightLast)); + READWRITE(VARINT(nTimeFirst)); + READWRITE(VARINT(nTimeLast)); + } + + void SetNull() { + nBlocks = 0; + nSize = 0; + nUndoSize = 0; + nHeightFirst = 0; + nHeightLast = 0; + nTimeFirst = 0; + nTimeLast = 0; + } + + CBlockFileInfo() { + SetNull(); + } + + std::string ToString() const; + + /** update statistics (does not update nSize) */ + void AddBlock(unsigned int nHeightIn, uint64_t nTimeIn) { + if (nBlocks==0 || nHeightFirst > nHeightIn) + nHeightFirst = nHeightIn; + if (nBlocks==0 || nTimeFirst > nTimeIn) + nTimeFirst = nTimeIn; + nBlocks++; + if (nHeightIn > nHeightLast) + nHeightLast = nHeightIn; + if (nTimeIn > nTimeLast) + nTimeLast = nTimeIn; + } +}; + struct CDiskBlockPos { int nFile; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 147141bfeca..71160bb9a15 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -89,6 +89,10 @@ class CMainParams : public CChainParams { consensus.nMajorityEnforceBlockUpgrade = 750; consensus.nMajorityRejectBlockOutdated = 950; consensus.nMajorityWindow = 4000; + const size_t N = 200, K = 9; + BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); + consensus.nEquihashN = N; + consensus.nEquihashK = K; consensus.powLimit = uint256S("007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowAveragingWindow = 17; assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); @@ -106,9 +110,12 @@ class CMainParams : public CChainParams { consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = 180000; consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nProtocolVersion = 170007; consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = 245555; + consensus.vUpgrades[Consensus::UPGRADE_BLOSSOM].nProtocolVersion = 170009; + consensus.vUpgrades[Consensus::UPGRADE_BLOSSOM].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; // The best chain should have at least this much work. - consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000000e23bf7a5d76"); + consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000014a6157c4e02"); /** * The message start string should be awesome! ⓩ❤ @@ -120,10 +127,6 @@ class CMainParams : public CChainParams { vAlertPubKey = ParseHex("04b7ecf0baa90495ceb4e4090f6b2fd37eec1e9c85fac68a487f3ce11589692e4a317479316ee814e066638e1db54e37a10689b70286e6315b1087b6615d179264"); nDefaultPort = 8144; nPruneAfterHeight = 100000; - const size_t N = 200, K = 9; - BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); - nEquihashN = N; - nEquihashK = K; genesis = CreateGenesisBlock( 1477641360, @@ -182,14 +185,23 @@ class CMainParams : public CChainParams { (149999, uint256S("0x00000016a4855e91a2f435ad480d16dfe297b9d6e3319da5b3f3aa19641d4236")) (177777, uint256S("0x0000001893fd6c1754987f1256a24758206d8dcb66968d1ac6acd45615987def")) (199999, uint256S("0x0000007f82647f3ed9beb69875ce82c5865253dccf53f4cfa1ede2eda2876001")) - (237000, uint256S("0x0000000d9675244db461a49e7b33ba23521f7c469b3210c2f6ac69179a7b887f")), - 1539512150, // * UNIX timestamp of last checkpoint block - 785014, // * total number of transactions between genesis and last checkpoint + (237000, uint256S("0x0000000d9675244db461a49e7b33ba23521f7c469b3210c2f6ac69179a7b887f")) + (385555, uint256S("0x000000008906c05a5f3620a29cf16172ac7c2634ad992ae1310a6250718f9c4c")), + 1562044893, // * UNIX timestamp of last checkpoint block + 1145662, // * total number of transactions between genesis and last checkpoint // (the tx=... number in the SetBestChain debug.log lines) - 1728 // * estimated number of transactions per day after checkpoint + 1711 // * estimated number of transactions per day after checkpoint // total number of tx / checkpoint block height * 576 }; + // Hardcoded fallback value for the Sprout shielded value pool balance + // for nodes that have not reindexed since the introduction of monitoring + // in #2795. + nSproutValuePoolCheckpointHeight = 385568; + nSproutValuePoolCheckpointBalance = 30102637720247; + fZIP209Enabled = true; + hashSproutValuePoolCheckpointBlock = uint256S("0000004bef4fa6f6353217804e818ba7b2338c1ea6275b1b3fcbe75eee45288b"); + // Founders reward script expects a vector of 2-of-3 multisig addresses vFoundersRewardAddress = { "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", /* main-index: 0*/ @@ -268,6 +280,10 @@ class CTestNetParams : public CChainParams { consensus.nMajorityEnforceBlockUpgrade = 51; consensus.nMajorityRejectBlockOutdated = 75; consensus.nMajorityWindow = 400; + const size_t N = 200, K = 9; + BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); + consensus.nEquihashN = N; + consensus.nEquihashK = K; consensus.powLimit = uint256S("07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowAveragingWindow = 17; assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); @@ -285,6 +301,9 @@ class CTestNetParams : public CChainParams { consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = 207500; consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nProtocolVersion = 170007; consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = 280000; + consensus.vUpgrades[Consensus::UPGRADE_BLOSSOM].nProtocolVersion = 170008; + consensus.vUpgrades[Consensus::UPGRADE_BLOSSOM].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000000001d0c4d9cd"); @@ -296,10 +315,6 @@ class CTestNetParams : public CChainParams { vAlertPubKey = ParseHex("044e7a1553392325c871c5ace5d6ad73501c66f4c185d6b0453cf45dec5a1322e705c672ac1a27ef7cdaf588c10effdf50ed5f95f85f2f54a5f6159fca394ed0c6"); nDefaultPort = 18233; nPruneAfterHeight = 1000; - const size_t N = 200, K = 9; - BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); - nEquihashN = N; - nEquihashK = K; genesis = CreateGenesisBlock( 1477648033, @@ -397,6 +412,10 @@ class CRegTestParams : public CChainParams { consensus.nMajorityEnforceBlockUpgrade = 750; consensus.nMajorityRejectBlockOutdated = 950; consensus.nMajorityWindow = 1000; + const size_t N = 48, K = 5; + BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); + consensus.nEquihashN = N; + consensus.nEquihashK = K; consensus.powLimit = uint256S("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); consensus.nPowAveragingWindow = 17; assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); @@ -416,6 +435,9 @@ class CRegTestParams : public CChainParams { consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nProtocolVersion = 170006; consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; + consensus.vUpgrades[Consensus::UPGRADE_BLOSSOM].nProtocolVersion = 170008; + consensus.vUpgrades[Consensus::UPGRADE_BLOSSOM].nActivationHeight = + Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); @@ -426,10 +448,6 @@ class CRegTestParams : public CChainParams { pchMessageStart[3] = 0x5f; nDefaultPort = 18344; nPruneAfterHeight = 1000; - const size_t N = 48, K = 5; - BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); - nEquihashN = N; - nEquihashK = K; genesis = CreateGenesisBlock( 1296688602, @@ -440,8 +458,8 @@ class CRegTestParams : public CChainParams { assert(consensus.hashGenesisBlock == uint256S("0x" + consensus.hashGenesisBlock.ToString())); assert(genesis.hashMerkleRoot == uint256S("0x"+genesis.hashMerkleRoot.ToString())); - vFixedSeeds.clear(); //! Regtest mode doesn't have any fixed seeds. - vSeeds.clear(); //! Regtest mode doesn't have any DNS seeds. + vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds. + vSeeds.clear(); //!< Regtest mode doesn't have any DNS seeds. fMiningRequiresPeers = false; fDefaultConsistencyChecks = true; @@ -482,6 +500,10 @@ class CRegTestParams : public CChainParams { assert(idx > Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES); consensus.vUpgrades[idx].nActivationHeight = nActivationHeight; } + + void SetRegTestZIP209Enabled() { + fZIP209Enabled = true; + } }; static CRegTestParams regTestParams; @@ -514,6 +536,11 @@ void SelectParams(CBaseChainParams::Network network) { if (network == CBaseChainParams::REGTEST && mapArgs.count("-regtestprotectcoinbase")) { regTestParams.SetRegTestCoinbaseMustBeProtected(); } + + // When a developer is debugging turnstile violations in regtest mode, enable ZIP209 + if (network == CBaseChainParams::REGTEST && mapArgs.count("-developersetpoolsizezero")) { + regTestParams.SetRegTestZIP209Enabled(); + } } bool SelectParamsFromCommandLine() diff --git a/src/chainparams.h b/src/chainparams.h index ce5c1d6a179..17ebe9d31a6 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -83,8 +83,6 @@ class CChainParams /** Policy: Filter transactions that do not match well-defined patterns */ bool RequireStandard() const { return fRequireStandard; } int64_t PruneAfterHeight() const { return nPruneAfterHeight; } - unsigned int EquihashN() const { return nEquihashN; } - unsigned int EquihashK() const { return nEquihashK; } std::string CurrencyUnits() const { return strCurrencyUnits; } uint32_t BIP44CoinType() const { return bip44CoinType; } /** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */ @@ -113,8 +111,6 @@ class CChainParams std::vector vAlertPubKey; int nDefaultPort = 0; uint64_t nPruneAfterHeight = 0; - unsigned int nEquihashN = 0; - unsigned int nEquihashK = 0; std::vector vSeeds; std::vector base58Prefixes[MAX_BASE58_TYPES]; std::string bech32HRPs[MAX_BECH32_TYPES]; diff --git a/src/clientversion.h b/src/clientversion.h index 2ccd6aebe30..259d6d4b3e5 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -17,7 +17,7 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 2 #define CLIENT_VERSION_MINOR 0 -#define CLIENT_VERSION_REVISION 4 +#define CLIENT_VERSION_REVISION 6 #define CLIENT_VERSION_BUILD 50 //! Set to true for release, false for prerelease or test build diff --git a/src/consensus/params.h b/src/consensus/params.h index 26288f24339..9cd3a84617d 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -26,6 +26,7 @@ enum UpgradeIndex { UPGRADE_TESTDUMMY, UPGRADE_OVERWINTER, UPGRADE_SAPLING, + UPGRADE_BLOSSOM, // NOTE: Also add new upgrades to NetworkUpgradeInfo in upgrades.cpp MAX_NETWORK_UPGRADES }; @@ -92,6 +93,8 @@ struct Params { int nMajorityWindow; NetworkUpgrade vUpgrades[MAX_NETWORK_UPGRADES]; /** Proof of work parameters */ + unsigned int nEquihashN = 0; + unsigned int nEquihashK = 0; uint256 powLimit; boost::optional nPowAllowMinDifficultyBlocksAfterHeight; int64_t nPowAveragingWindow; diff --git a/src/consensus/upgrades.cpp b/src/consensus/upgrades.cpp index 04be05cdf99..e11aaa260e4 100644 --- a/src/consensus/upgrades.cpp +++ b/src/consensus/upgrades.cpp @@ -28,6 +28,11 @@ const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = { /*.nBranchId =*/ 0x76b809bb, /*.strName =*/ "Sapling", /*.strInfo =*/ "See https://z.cash/upgrade/sapling.html for details.", + }, + { + /*.nBranchId =*/ 0x2bb40e60, + /*.strName =*/ "Blossom", + /*.strInfo =*/ "See https://z.cash/upgrade/blossom.html for details.", } }; diff --git a/src/deprecation.h b/src/deprecation.h index f36005980ca..37eaf762411 100644 --- a/src/deprecation.h +++ b/src/deprecation.h @@ -8,7 +8,7 @@ // Deprecation policy: // * Shut down 16 weeks' worth of blocks after the estimated release block height. // * A warning is shown during the 2 weeks' worth of blocks prior to shut down. -static const int APPROX_RELEASE_HEIGHT = 348597; +static const int APPROX_RELEASE_HEIGHT = 385555; static const int WEEKS_UNTIL_DEPRECATION = 16; static const int DEPRECATION_HEIGHT = APPROX_RELEASE_HEIGHT + (WEEKS_UNTIL_DEPRECATION * 7 * 24 * 24); diff --git a/src/gtest/test_checkblock.cpp b/src/gtest/test_checkblock.cpp index 2919e1314d7..4195647e815 100644 --- a/src/gtest/test_checkblock.cpp +++ b/src/gtest/test_checkblock.cpp @@ -31,7 +31,7 @@ TEST(CheckBlock, VersionTooLow) { MockCValidationState state; EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "version-too-low", false)).Times(1); - EXPECT_FALSE(CheckBlock(block, state, verifier, false, false)); + EXPECT_FALSE(CheckBlock(block, state, Params(), verifier, false, false)); } @@ -64,7 +64,7 @@ TEST(CheckBlock, BlockSproutRejectsBadVersion) { 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)); + EXPECT_FALSE(CheckBlock(block, state, Params(), verifier, false, false)); } @@ -117,7 +117,7 @@ class ContextualCheckBlockTest : public ::testing::Test { // We now expect this to be a valid block. MockCValidationState state; - EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev)); + EXPECT_TRUE(ContextualCheckBlock(block, state, Params(), &indexPrev)); } // Expects a height-1 block containing a given transaction to fail @@ -135,7 +135,7 @@ class ContextualCheckBlockTest : public ::testing::Test { // We now expect this to be an invalid block, for the given reason. MockCValidationState state; EXPECT_CALL(state, DoS(level, false, REJECT_INVALID, reason, false)).Times(1); - EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev)); + EXPECT_FALSE(ContextualCheckBlock(block, state, Params(), &indexPrev)); } }; @@ -152,7 +152,7 @@ TEST_F(ContextualCheckBlockTest, BadCoinbaseHeight) { // Treating block as genesis should pass MockCValidationState state; - EXPECT_TRUE(ContextualCheckBlock(block, state, NULL)); + EXPECT_TRUE(ContextualCheckBlock(block, state, Params(), NULL)); // Give the transaction a Founder's Reward vout mtx.vout.push_back(CTxOut( @@ -166,20 +166,20 @@ TEST_F(ContextualCheckBlockTest, BadCoinbaseHeight) { 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)); + EXPECT_FALSE(ContextualCheckBlock(block, state, Params(), &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)); + EXPECT_FALSE(ContextualCheckBlock(block, state, Params(), &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)); + EXPECT_TRUE(ContextualCheckBlock(block, state, Params(), &indexPrev)); } // TEST PLAN: first, check that each ruleset accepts its own transaction type. diff --git a/src/gtest/test_checktransaction.cpp b/src/gtest/test_checktransaction.cpp index cf9d7744168..6897d42e9a8 100644 --- a/src/gtest/test_checktransaction.cpp +++ b/src/gtest/test_checktransaction.cpp @@ -167,7 +167,7 @@ TEST(checktransaction_tests, BadTxnsOversize) { // ... but fails contextual ones! EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1); - EXPECT_FALSE(ContextualCheckTransaction(tx, state, 1, 100)); + EXPECT_FALSE(ContextualCheckTransaction(tx, state, Params(), 1, 100)); } { @@ -188,7 +188,7 @@ TEST(checktransaction_tests, BadTxnsOversize) { MockCValidationState state; EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state)); - EXPECT_TRUE(ContextualCheckTransaction(tx, state, 1, 100)); + EXPECT_TRUE(ContextualCheckTransaction(tx, state, Params(), 1, 100)); // Revert to default RegtestDeactivateSapling(); @@ -496,6 +496,7 @@ TEST(checktransaction_tests, bad_txns_prevout_null) { TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) { SelectParams(CBaseChainParams::REGTEST); + auto chainparams = Params(); CMutableTransaction mtx = GetValidTransaction(); mtx.joinSplitSig[0] += 1; @@ -504,13 +505,14 @@ TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) { MockCValidationState state; // during initial block download, DoS ban score should be zero, else 100 EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); - ContextualCheckTransaction(tx, state, 0, 100, []() { return true; }); + ContextualCheckTransaction(tx, state, chainparams, 0, 100, [](const CChainParams&) { return true; }); EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); - ContextualCheckTransaction(tx, state, 0, 100, []() { return false; }); + ContextualCheckTransaction(tx, state, chainparams, 0, 100, [](const CChainParams&) { return false; }); } TEST(checktransaction_tests, non_canonical_ed25519_signature) { SelectParams(CBaseChainParams::REGTEST); + auto chainparams = Params(); CMutableTransaction mtx = GetValidTransaction(); @@ -518,7 +520,7 @@ TEST(checktransaction_tests, non_canonical_ed25519_signature) { { CTransaction tx(mtx); MockCValidationState state; - EXPECT_TRUE(ContextualCheckTransaction(tx, state, 0, 100)); + EXPECT_TRUE(ContextualCheckTransaction(tx, state, chainparams, 0, 100)); } // Copied from libsodium/crypto_sign/ed25519/ref10/open.c @@ -540,9 +542,9 @@ TEST(checktransaction_tests, non_canonical_ed25519_signature) { MockCValidationState state; // during initial block download, DoS ban score should be zero, else 100 EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); - ContextualCheckTransaction(tx, state, 0, 100, []() { return true; }); + ContextualCheckTransaction(tx, state, chainparams, 0, 100, [](const CChainParams&) { return true; }); EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); - ContextualCheckTransaction(tx, state, 0, 100, []() { return false; }); + ContextualCheckTransaction(tx, state, chainparams, 0, 100, [](const CChainParams&) { return false; }); } TEST(checktransaction_tests, OverwinterConstructors) { @@ -797,7 +799,7 @@ TEST(checktransaction_tests, OverwinterVersionNumberHigh) { 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); + ContextualCheckTransaction(tx, state, Params(), 1, 100); // Revert to default UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); @@ -822,6 +824,7 @@ TEST(checktransaction_tests, OverwinterBadVersionGroupId) { // This tests an Overwinter transaction checked against Sprout TEST(checktransaction_tests, OverwinterNotActive) { SelectParams(CBaseChainParams::TESTNET); + auto chainparams = Params(); CMutableTransaction mtx = GetValidTransaction(); mtx.fOverwintered = true; @@ -833,9 +836,9 @@ TEST(checktransaction_tests, OverwinterNotActive) { MockCValidationState state; // during initial block download, DoS ban score should be zero, else 100 EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1); - ContextualCheckTransaction(tx, state, 1, 100, []() { return true; }); + ContextualCheckTransaction(tx, state, chainparams, 1, 100, [](const CChainParams&) { return true; }); EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1); - ContextualCheckTransaction(tx, state, 1, 100, []() { return false; }); + ContextualCheckTransaction(tx, state, chainparams, 1, 100, [](const CChainParams&) { return false; }); } // This tests a transaction without the fOverwintered flag set, against the Overwinter consensus rule set. @@ -852,7 +855,7 @@ TEST(checktransaction_tests, OverwinterFlagNotSet) { 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); + ContextualCheckTransaction(tx, state, Params(), 1, 100); // Revert to default UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); diff --git a/src/gtest/test_merkletree.cpp b/src/gtest/test_merkletree.cpp index 23c39c04467..d55cb13179c 100644 --- a/src/gtest/test_merkletree.cpp +++ b/src/gtest/test_merkletree.cpp @@ -249,7 +249,7 @@ TEST(merkletree, EmptyrootsSapling) { } TEST(merkletree, emptyroot) { - // This literal is the depth-20 empty tree root with the bytes reversed to + // This literal is the depth-29 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"); @@ -258,7 +258,7 @@ TEST(merkletree, emptyroot) { } TEST(merkletree, EmptyrootSapling) { - // This literal is the depth-20 empty tree root with the bytes reversed to + // This literal is the depth-32 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("3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb"); diff --git a/src/gtest/test_transaction_builder.cpp b/src/gtest/test_transaction_builder.cpp index 72aab05f717..5ebf1d737c6 100644 --- a/src/gtest/test_transaction_builder.cpp +++ b/src/gtest/test_transaction_builder.cpp @@ -12,7 +12,68 @@ #include #include -TEST(TransactionBuilder, Invoke) +extern ZCJoinSplit* params; + +// Fake an empty view +class TransactionBuilderCoinsViewDB : public CCoinsView { +public: + std::map sproutTrees; + + TransactionBuilderCoinsViewDB() {} + + bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { + auto it = sproutTrees.find(rt); + if (it != sproutTrees.end()) { + tree = it->second; + return true; + } else { + return false; + } + } + + bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { + return false; + } + + bool GetNullifier(const uint256 &nf, ShieldedType type) 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(ShieldedType type) const { + uint256 a; + return a; + } + + bool BatchWrite(CCoinsMap &mapCoins, + const uint256 &hashBlock, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, + CNullifiersMap &mapSproutNullifiers, + CNullifiersMap saplingNullifiersMap) { + return false; + } + + bool GetStats(CCoinsStats &stats) const { + return false; + } +}; + +TEST(TransactionBuilder, TransparentToSapling) { auto consensusParams = RegtestActivateSapling(); @@ -32,65 +93,178 @@ TEST(TransactionBuilder, Invoke) // Create a shielding transaction from transparent to Sapling // 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee - auto builder1 = TransactionBuilder(consensusParams, 1, &keystore); - builder1.AddTransparentInput(COutPoint(), scriptPubKey, 50000); - builder1.AddSaplingOutput(fvk_from.ovk, pk, 40000, {}); - auto tx1 = builder1.Build().GetTxOrThrow(); - - EXPECT_EQ(tx1.vin.size(), 1); - EXPECT_EQ(tx1.vout.size(), 0); - EXPECT_EQ(tx1.vjoinsplit.size(), 0); - EXPECT_EQ(tx1.vShieldedSpend.size(), 0); - EXPECT_EQ(tx1.vShieldedOutput.size(), 1); - EXPECT_EQ(tx1.valueBalance, -40000); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta, &keystore); + builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000); + builder.AddSaplingOutput(fvk_from.ovk, pk, 40000, {}); + auto tx = builder.Build().GetTxOrThrow(); + + EXPECT_EQ(tx.vin.size(), 1); + EXPECT_EQ(tx.vout.size(), 0); + EXPECT_EQ(tx.vjoinsplit.size(), 0); + EXPECT_EQ(tx.vShieldedSpend.size(), 0); + EXPECT_EQ(tx.vShieldedOutput.size(), 1); + EXPECT_EQ(tx.valueBalance, -40000); CValidationState state; - EXPECT_TRUE(ContextualCheckTransaction(tx1, state, 2, 0)); + EXPECT_TRUE(ContextualCheckTransaction(tx, state, Params(), 2, 0)); EXPECT_EQ(state.GetRejectReason(), ""); - // Prepare to spend the note that was just created - auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt( - tx1.vShieldedOutput[0].encCiphertext, ivk, tx1.vShieldedOutput[0].ephemeralKey, tx1.vShieldedOutput[0].cm); - ASSERT_EQ(static_cast(maybe_pt), true); - auto maybe_note = maybe_pt.get().note(ivk); - ASSERT_EQ(static_cast(maybe_note), true); - auto note = maybe_note.get(); - SaplingMerkleTree tree; - tree.append(tx1.vShieldedOutput[0].cm); - auto anchor = tree.root(); - auto witness = tree.witness(); + // Revert to default + RegtestDeactivateSapling(); +} +TEST(TransactionBuilder, SaplingToSapling) { + auto consensusParams = RegtestActivateSapling(); + + auto sk = libzcash::SaplingSpendingKey::random(); + auto expsk = sk.expanded_spending_key(); + auto fvk = sk.full_viewing_key(); + auto pa = sk.default_address(); + + auto testNote = GetTestSaplingNote(pa, 40000); + // Create a Sapling-only transaction // 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change - auto builder2 = TransactionBuilder(consensusParams, 2); - builder2.AddSaplingSpend(expsk, note, anchor, witness); + auto builder = TransactionBuilder(consensusParams, 2, expiryDelta); + builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); + // Check that trying to add a different anchor fails // TODO: the following check can be split out in to another test - ASSERT_THROW(builder2.AddSaplingSpend(expsk, note, uint256(), witness), UniValue); + ASSERT_THROW(builder.AddSaplingSpend(expsk, testNote.note, uint256(), testNote.tree.witness()), UniValue); + + builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); + auto tx = builder.Build().GetTxOrThrow(); + + EXPECT_EQ(tx.vin.size(), 0); + EXPECT_EQ(tx.vout.size(), 0); + EXPECT_EQ(tx.vjoinsplit.size(), 0); + EXPECT_EQ(tx.vShieldedSpend.size(), 1); + EXPECT_EQ(tx.vShieldedOutput.size(), 2); + EXPECT_EQ(tx.valueBalance, 10000); + + CValidationState state; + EXPECT_TRUE(ContextualCheckTransaction(tx, state, Params(), 3, 0)); + EXPECT_EQ(state.GetRejectReason(), ""); + + // Revert to default + RegtestDeactivateSapling(); +} + +TEST(TransactionBuilder, SaplingToSprout) { + auto consensusParams = RegtestActivateSapling(); + + auto sk = libzcash::SaplingSpendingKey::random(); + auto expsk = sk.expanded_spending_key(); + auto pa = sk.default_address(); - builder2.AddSaplingOutput(fvk.ovk, pk, 25000, {}); - auto tx2 = builder2.Build().GetTxOrThrow(); + auto testNote = GetTestSaplingNote(pa, 40000); - EXPECT_EQ(tx2.vin.size(), 0); - EXPECT_EQ(tx2.vout.size(), 0); - EXPECT_EQ(tx2.vjoinsplit.size(), 0); - EXPECT_EQ(tx2.vShieldedSpend.size(), 1); - EXPECT_EQ(tx2.vShieldedOutput.size(), 2); - EXPECT_EQ(tx2.valueBalance, 10000); + auto sproutSk = libzcash::SproutSpendingKey::random(); + auto sproutAddr = sproutSk.address(); - EXPECT_TRUE(ContextualCheckTransaction(tx2, state, 3, 0)); + // Create a Sapling-to-Sprout transaction (reusing the note from above) + // - 0.0004 Sapling-ZEC in - 0.00025 Sprout-ZEC out + // - 0.00005 Sapling-ZEC change + // - 0.0001 t-ZEC fee + auto builder = TransactionBuilder(consensusParams, 2, expiryDelta, nullptr, params); + builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); + builder.AddSproutOutput(sproutAddr, 25000); + auto tx = builder.Build().GetTxOrThrow(); + + EXPECT_EQ(tx.vin.size(), 0); + EXPECT_EQ(tx.vout.size(), 0); + EXPECT_EQ(tx.vjoinsplit.size(), 1); + EXPECT_EQ(tx.vjoinsplit[0].vpub_old, 25000); + EXPECT_EQ(tx.vjoinsplit[0].vpub_new, 0); + EXPECT_EQ(tx.vShieldedSpend.size(), 1); + EXPECT_EQ(tx.vShieldedOutput.size(), 1); + EXPECT_EQ(tx.valueBalance, 35000); + + CValidationState state; + EXPECT_TRUE(ContextualCheckTransaction(tx, state, Params(), 3, 0)); EXPECT_EQ(state.GetRejectReason(), ""); // Revert to default RegtestDeactivateSapling(); } +TEST(TransactionBuilder, SproutToSproutAndSapling) { + auto consensusParams = RegtestActivateSapling(); + + auto sk = libzcash::SaplingSpendingKey::random(); + auto fvk = sk.full_viewing_key(); + auto pa = sk.default_address(); + + auto sproutSk = libzcash::SproutSpendingKey::random(); + auto sproutAddr = sproutSk.address(); + + auto wtx = GetValidSproutReceive(*params, sproutSk, 25000, true); + auto sproutNote = GetSproutNote(*params, sproutSk, wtx, 0, 1); + + SproutMerkleTree sproutTree; + for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { + sproutTree.append(wtx.vjoinsplit[0].commitments[i]); + } + SproutWitness sproutWitness = sproutTree.witness(); + // Fake a view with the Sprout note in it + auto rt = sproutTree.root(); + TransactionBuilderCoinsViewDB fakeDB; + fakeDB.sproutTrees.insert(std::pair(rt, sproutTree)); + CCoinsViewCache view(&fakeDB); + + // Create a Sprout-to-[Sprout-and-Sapling] transaction + // - 0.00025 Sprout-ZEC in - 0.00006 Sprout-ZEC out + // - 0.00004 Sprout-ZEC out + // - 0.00005 Sprout-ZEC change + // - 0.00005 Sapling-ZEC out + // - 0.00005 t-ZEC fee + auto builder = TransactionBuilder(consensusParams, 2, expiryDelta, nullptr, params, &view); + builder.SetFee(5000); + builder.AddSproutInput(sproutSk, sproutNote, sproutWitness); + builder.AddSproutOutput(sproutAddr, 6000); + builder.AddSproutOutput(sproutAddr, 4000); + builder.AddSaplingOutput(fvk.ovk, pa, 5000); + auto tx = builder.Build().GetTxOrThrow(); + + EXPECT_EQ(tx.vin.size(), 0); + EXPECT_EQ(tx.vout.size(), 0); + // TODO: This should be doable in two JoinSplits. + // There's an inefficiency in the implementation. + EXPECT_EQ(tx.vjoinsplit.size(), 3); + EXPECT_EQ(tx.vjoinsplit[0].vpub_old, 0); + EXPECT_EQ(tx.vjoinsplit[0].vpub_new, 0); + EXPECT_EQ(tx.vjoinsplit[1].vpub_old, 0); + EXPECT_EQ(tx.vjoinsplit[1].vpub_new, 0); + EXPECT_EQ(tx.vjoinsplit[2].vpub_old, 0); + EXPECT_EQ(tx.vjoinsplit[2].vpub_new, 10000); + EXPECT_EQ(tx.vShieldedSpend.size(), 0); + EXPECT_EQ(tx.vShieldedOutput.size(), 1); + EXPECT_EQ(tx.valueBalance, -5000); + + CValidationState state; + EXPECT_TRUE(ContextualCheckTransaction(tx, state, Params(), 4, 0)); + EXPECT_EQ(state.GetRejectReason(), ""); + + // Revert to default + RegtestDeactivateSapling(); +} + +TEST(TransactionBuilder, ThrowsOnSproutOutputWithoutParams) +{ + auto consensusParams = Params().GetConsensus(); + auto sk = libzcash::SproutSpendingKey::random(); + auto addr = sk.address(); + + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); + ASSERT_THROW(builder.AddSproutOutput(addr, 10), std::runtime_error); +} + TEST(TransactionBuilder, ThrowsOnTransparentInputWithoutKeyStore) { SelectParams(CBaseChainParams::REGTEST); auto consensusParams = Params().GetConsensus(); - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); ASSERT_THROW(builder.AddTransparentInput(COutPoint(), CScript(), 1), std::runtime_error); } @@ -101,7 +275,7 @@ TEST(TransactionBuilder, RejectsInvalidTransparentOutput) // Default CTxDestination type is an invalid address CTxDestination taddr; - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); ASSERT_THROW(builder.AddTransparentOutput(taddr, 50), UniValue); } @@ -112,7 +286,7 @@ TEST(TransactionBuilder, RejectsInvalidTransparentChangeAddress) // Default CTxDestination type is an invalid address CTxDestination taddr; - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); ASSERT_THROW(builder.SendChangeTo(taddr), UniValue); } @@ -137,13 +311,13 @@ TEST(TransactionBuilder, FailsWithNegativeChange) // Fail if there is only a Sapling output // 0.0005 z-ZEC out, 0.0001 t-ZEC fee - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingOutput(fvk.ovk, pa, 50000, {}); EXPECT_EQ("Change cannot be negative", builder.Build().GetError()); // Fail if there is only a transparent output // 0.0005 t-ZEC out, 0.0001 t-ZEC fee - builder = TransactionBuilder(consensusParams, 1, &keystore); + builder = TransactionBuilder(consensusParams, 1, expiryDelta, &keystore); builder.AddTransparentOutput(taddr, 50000); EXPECT_EQ("Change cannot be negative", builder.Build().GetError()); @@ -185,14 +359,14 @@ TEST(TransactionBuilder, ChangeOutput) // No change address and no Sapling spends { - auto builder = TransactionBuilder(consensusParams, 1, &keystore); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta, &keystore); builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000); EXPECT_EQ("Could not determine change address", builder.Build().GetError()); } // Change to the same address as the first Sapling spend { - auto builder = TransactionBuilder(consensusParams, 1, &keystore); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta, &keystore); builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); auto tx = builder.Build().GetTxOrThrow(); @@ -207,7 +381,7 @@ TEST(TransactionBuilder, ChangeOutput) // Change to a Sapling address { - auto builder = TransactionBuilder(consensusParams, 1, &keystore); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta, &keystore); builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000); builder.SendChangeTo(zChangeAddr, fvkOut.ovk); auto tx = builder.Build().GetTxOrThrow(); @@ -222,7 +396,7 @@ TEST(TransactionBuilder, ChangeOutput) // Change to a transparent address { - auto builder = TransactionBuilder(consensusParams, 1, &keystore); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta, &keystore); builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000); builder.SendChangeTo(taddr); auto tx = builder.Build().GetTxOrThrow(); @@ -254,7 +428,7 @@ TEST(TransactionBuilder, SetFee) // Default fee { - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -269,7 +443,7 @@ TEST(TransactionBuilder, SetFee) // Configured fee { - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); builder.SetFee(20000); @@ -298,7 +472,7 @@ TEST(TransactionBuilder, CheckSaplingTxVersion) auto pk = sk.default_address(); // Cannot add Sapling outputs to a non-Sapling transaction - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); try { builder.AddSaplingOutput(uint256(), pk, 12345, {}); } catch (std::runtime_error const & err) { diff --git a/src/gtest/test_validation.cpp b/src/gtest/test_validation.cpp index 4e937d04266..0f66b8dcfb6 100644 --- a/src/gtest/test_validation.cpp +++ b/src/gtest/test_validation.cpp @@ -7,7 +7,12 @@ extern ZCJoinSplit* params; -extern bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos); +extern bool ReceivedBlockTransactions( + const CBlock &block, + CValidationState& state, + const CChainParams& chainparams, + CBlockIndex *pindexNew, + const CDiskBlockPos& pos); void ExpectOptionalAmount(CAmount expected, boost::optional actual) { EXPECT_TRUE((bool)actual); @@ -87,6 +92,7 @@ TEST(Validation, ContextualCheckInputsPassesWithCoinbase) { } TEST(Validation, ReceivedBlockTransactions) { + auto chainParams = Params(); auto sk = libzcash::SproutSpendingKey::random(); // Create a fake genesis block @@ -122,7 +128,7 @@ TEST(Validation, ReceivedBlockTransactions) { // Mark the second block's transactions as received first CValidationState state; - EXPECT_TRUE(ReceivedBlockTransactions(block2, state, &fakeIndex2, pos2)); + EXPECT_TRUE(ReceivedBlockTransactions(block2, state, chainParams, &fakeIndex2, pos2)); EXPECT_FALSE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS)); EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS)); @@ -137,7 +143,7 @@ TEST(Validation, ReceivedBlockTransactions) { EXPECT_FALSE((bool)fakeIndex2.nChainSproutValue); // Now mark the first block's transactions as received - EXPECT_TRUE(ReceivedBlockTransactions(block1, state, &fakeIndex1, pos1)); + EXPECT_TRUE(ReceivedBlockTransactions(block1, state, chainParams, &fakeIndex1, pos1)); EXPECT_TRUE(fakeIndex1.IsValid(BLOCK_VALID_TRANSACTIONS)); EXPECT_TRUE(fakeIndex2.IsValid(BLOCK_VALID_TRANSACTIONS)); diff --git a/src/init.cpp b/src/init.cpp index 1f7cfc8d062..be85cfba1a2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -37,6 +37,7 @@ #include "utilmoneystr.h" #include "validationinterface.h" #ifdef ENABLE_WALLET +#include "key_io.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" #endif @@ -406,6 +407,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("Wallet options:")); strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), 100)); + strUsage += HelpMessageOpt("-migration", _("Enable the Sprout to Sapling migration")); + strUsage += HelpMessageOpt("-migrationdestaddress=", _("Set the Sapling migration address")); if (showDebug) strUsage += HelpMessageOpt("-mintxfee=", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)", CURRENCY_UNIT, FormatMoney(CWallet::minTxFee.GetFeePerK()))); @@ -604,6 +607,7 @@ void CleanupBlockRevFiles() void ThreadImport(std::vector vImportFiles) { + const CChainParams& chainparams = Params(); RenameThread("zcash-loadblk"); // -reindex if (fReindex) { @@ -617,14 +621,14 @@ void ThreadImport(std::vector vImportFiles) if (!file) break; // This error is logged in OpenBlockFile LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); - LoadExternalBlockFile(file, &pos); + LoadExternalBlockFile(chainparams, file, &pos); nFile++; } pblocktree->WriteReindexing(false); fReindex = false; LogPrintf("Reindexing finished\n"); // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): - InitBlockIndex(); + InitBlockIndex(chainparams); } // hardcoded $DATADIR/bootstrap.dat @@ -635,7 +639,7 @@ void ThreadImport(std::vector vImportFiles) CImportingNow imp; boost::filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; LogPrintf("Importing bootstrap.dat...\n"); - LoadExternalBlockFile(file); + LoadExternalBlockFile(chainparams, file); RenameOver(pathBootstrap, pathBootstrapOld); } else { LogPrintf("Warning: Could not open bootstrap file %s\n", pathBootstrap.string()); @@ -648,7 +652,7 @@ void ThreadImport(std::vector vImportFiles) if (file) { CImportingNow imp; LogPrintf("Importing blocks file %s...\n", path.string()); - LoadExternalBlockFile(file); + LoadExternalBlockFile(chainparams, file); } else { LogPrintf("Warning: Could not open blocks file %s\n", path.string()); } @@ -857,13 +861,16 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!fExperimentalMode) { if (mapArgs.count("-developerencryptwallet")) { return InitError(_("Wallet encryption requires -experimentalfeatures.")); - } - else if (mapArgs.count("-paymentdisclosure")) { + } else if (mapArgs.count("-developersetpoolsizezero")) { + return InitError(_("Setting the size of shielded pools to zero requires -experimentalfeatures.")); + } else if (mapArgs.count("-paymentdisclosure")) { return InitError(_("Payment disclosure requires -experimentalfeatures.")); } else if (mapArgs.count("-zmergetoaddress")) { return InitError(_("RPC method z_mergetoaddress requires -experimentalfeatures.")); } else if (mapArgs.count("-savesproutr1cs")) { return InitError(_("Saving the Sprout R1CS requires -experimentalfeatures.")); + } else if (mapArgs.count("-insightexplorer")) { + return InitError(_("Insight explorer requires -experimentalfeatures.")); } } @@ -1090,6 +1097,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fSendFreeTransactions = GetBoolArg("-sendfreetransactions", false); std::string strWalletFile = GetArg("-wallet", "wallet.dat"); + // Check Sapling migration address if set and is a valid Sapling address + if (mapArgs.count("-migrationdestaddress")) { + std::string migrationDestAddress = mapArgs["-migrationdestaddress"]; + libzcash::PaymentAddress address = DecodePaymentAddress(migrationDestAddress); + if (boost::get(&address) == nullptr) { + return InitError(_("-migrationdestaddress must be a valid Sapling address.")); + } + } #endif // ENABLE_WALLET fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", true); @@ -1465,6 +1480,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) int64_t nBlockTreeDBCache = nTotalCache / 8; if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false)) nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB + + // https://github.com/bitpay/bitcoin/commit/c91d78b578a8700a45be936cb5bb0931df8f4b87#diff-c865a8939105e6350a50af02766291b7R1233 + if (GetBoolArg("-insightexplorer", false)) { + if (!GetBoolArg("-txindex", false)) { + return InitError(_("-insightexplorer requires -txindex.")); + } + // increase cache if additional indices are needed + nBlockTreeDBCache = nTotalCache * 3 / 4; + } nTotalCache -= nBlockTreeDBCache; int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache nTotalCache -= nCoinDBCache; @@ -1515,7 +1539,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); // Initialize the block index (no-op if non-empty database was already loaded) - if (!InitBlockIndex()) { + if (!InitBlockIndex(chainparams)) { strLoadError = _("Error initializing block database"); break; } @@ -1526,6 +1550,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) break; } + // Check for changed -insightexplorer state + if (fInsightExplorer != GetBoolArg("-insightexplorer", false)) { + strLoadError = _("You need to rebuild the database using -reindex to change -insightexplorer"); + break; + } + // Check for changed -prune state. What we are concerned about is a user who has pruned blocks // in the past, but is now trying to run unpruned. if (fHavePruned && !fPruneMode) { @@ -1546,7 +1576,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("Prune: pruned datadir may not have more than %d blocks; -checkblocks=%d may fail\n", MIN_BLOCKS_TO_KEEP, GetArg("-checkblocks", 288)); } - if (!CVerifyDB().VerifyDB(pcoinsdbview, GetArg("-checklevel", 3), + if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, GetArg("-checklevel", 3), GetArg("-checkblocks", 288))) { strLoadError = _("Corrupted block database detected"); break; @@ -1668,10 +1698,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!pwalletMain->HaveHDSeed()) { - // generate a new HD seed - pwalletMain->GenerateNewSeed(); + // We can't set the new HD seed until the wallet is decrypted. + // https://github.com/zcash/zcash/issues/3607 + if (!pwalletMain->IsCrypted()) { + // generate a new HD seed + pwalletMain->GenerateNewSeed(); + } } + // Set sapling migration status + pwalletMain->fSaplingMigrationEnabled = GetBoolArg("-migration", false); + if (fFirstRun) { // Create new keyUser and set as default key @@ -1809,7 +1846,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.InitMessage(_("Activating best chain...")); // scan for better chains in the block chain database, that are not yet connected in the active best chain CValidationState state; - if (!ActivateBestChain(state)) + if (!ActivateBestChain(state, chainparams)) strErrors << "Failed to connect best block"; std::vector vImportFiles; @@ -1855,7 +1892,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #ifdef ENABLE_MINING // Generate coins in the background - GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 1), Params()); + GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 1), chainparams); #endif // ********************************************************* Step 11: finished diff --git a/src/main.cpp b/src/main.cpp index eaf82076fc4..4cb60c3e040 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -67,6 +67,10 @@ bool fExperimentalMode = false; bool fImporting = false; bool fReindex = false; bool fTxIndex = false; +bool fInsightExplorer = false; // insightexplorer +bool fAddressIndex = false; // insightexplorer +bool fSpentIndex = false; // insightexplorer +bool fTimestampIndex = false; // insightexplorer bool fHavePruned = false; bool fPruneMode = false; bool fIsBareMultisigStd = true; @@ -100,7 +104,7 @@ void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards. */ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams); -static void CheckBlockIndex(); +static void CheckBlockIndex(const Consensus::Params& consensusParams); /** Constant stuff for coinbase transactions we create: */ CScript COINBASE_FLAGS; @@ -196,10 +200,10 @@ namespace { /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; - CBlockIndex *pindex; //! Optional. - int64_t nTime; //! Time of "getdata" request in microseconds. - bool fValidatedHeaders; //! Whether this block has validated headers at the time of request. - int64_t nTimeDisconnect; //! The timeout for this block request (for disconnecting a slow peer) + CBlockIndex* pindex; //!< Optional. + int64_t nTime; //!< Time of "getdata" request in microseconds. + bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. + int64_t nTimeDisconnect; //!< The timeout for this block request (for disconnecting a slow peer) }; map::iterator> > mapBlocksInFlight; @@ -650,10 +654,10 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRE } -bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) +bool IsStandardTx(const CTransaction& tx, string& reason, const CChainParams& chainparams, const int nHeight) { - bool overwinterActive = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); - bool saplingActive = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING); + bool overwinterActive = NetworkUpgradeActive(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_OVERWINTER); + bool saplingActive = NetworkUpgradeActive(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_SAPLING); if (saplingActive) { // Sapling standard rules apply @@ -887,17 +891,18 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in bool ContextualCheckTransaction( const CTransaction& tx, CValidationState &state, + const CChainParams& chainparams, const int nHeight, const int dosLevel, - bool (*isInitBlockDownload)()) + bool (*isInitBlockDownload)(const CChainParams&)) { - bool overwinterActive = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); - bool saplingActive = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING); + bool overwinterActive = NetworkUpgradeActive(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_OVERWINTER); + bool saplingActive = NetworkUpgradeActive(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_SAPLING); bool isSprout = !overwinterActive; // If Sprout rules apply, reject transactions which are intended for Overwinter and beyond if (isSprout && tx.fOverwintered) { - return state.DoS(isInitBlockDownload() ? 0 : dosLevel, + return state.DoS(isInitBlockDownload(chainparams) ? 0 : dosLevel, error("ContextualCheckTransaction(): overwinter is not active yet"), REJECT_INVALID, "tx-overwinter-not-active"); } @@ -911,7 +916,7 @@ bool ContextualCheckTransaction( // Reject transactions with non-Sapling version group ID if (tx.fOverwintered && tx.nVersionGroupId != SAPLING_VERSION_GROUP_ID) { - return state.DoS(isInitBlockDownload() ? 0 : dosLevel, + return state.DoS(isInitBlockDownload(chainparams) ? 0 : dosLevel, error("CheckTransaction(): invalid Sapling tx version"), REJECT_INVALID, "bad-sapling-tx-version-group-id"); } @@ -936,7 +941,7 @@ bool ContextualCheckTransaction( // Reject transactions with non-Overwinter version group ID if (tx.fOverwintered && tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) { - return state.DoS(isInitBlockDownload() ? 0 : dosLevel, + return state.DoS(isInitBlockDownload(chainparams) ? 0 : dosLevel, error("CheckTransaction(): invalid Overwinter tx version"), REJECT_INVALID, "bad-overwinter-tx-version-group-id"); } @@ -979,7 +984,7 @@ bool ContextualCheckTransaction( !tx.vShieldedSpend.empty() || !tx.vShieldedOutput.empty()) { - auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus()); + auto consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus()); // Empty output script. CScript scriptCode; try { @@ -1000,7 +1005,7 @@ bool ContextualCheckTransaction( dataToBeSigned.begin(), 32, tx.joinSplitPubKey.begin() ) != 0) { - return state.DoS(isInitBlockDownload() ? 0 : 100, + return state.DoS(isInitBlockDownload(chainparams) ? 0 : 100, error("CheckTransaction(): invalid joinsplit signature"), REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); } @@ -1378,7 +1383,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // DoS level set to 10 to be more forgiving. // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. - if (!ContextualCheckTransaction(tx, state, nextBlockHeight, 10)) { + if (!ContextualCheckTransaction(tx, state, Params(), nextBlockHeight, 10)) { return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); } @@ -1396,7 +1401,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; - if (Params().RequireStandard() && !IsStandardTx(tx, reason, nextBlockHeight)) + if (Params().RequireStandard() && !IsStandardTx(tx, reason, Params(), nextBlockHeight)) return state.DoS(0, error("AcceptToMemoryPool: nonstandard transaction: %s", reason), REJECT_NONSTANDARD, reason); @@ -1594,7 +1599,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // Store transaction in memory - pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); + pool.addUnchecked(hash, entry, !IsInitialBlockDownload(Params())); } SyncWithWallets(tx, NULL); @@ -1602,8 +1607,16 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return true; } +bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) +{ + AssertLockHeld(cs_main); + if (!fSpentIndex) + return false; + return pblocktree->ReadSpentIndex(key, value); +} + /** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */ -bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) +bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) { CBlockIndex *pindexSlow = NULL; @@ -1649,7 +1662,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock if (pindexSlow) { CBlock block; - if (ReadBlockFromDisk(block, pindexSlow)) { + if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) { BOOST_FOREACH(const CTransaction &tx, block.vtx) { if (tx.GetHash() == hash) { txOut = tx; @@ -1673,7 +1686,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); @@ -1694,7 +1707,7 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::M return true; } -bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) +bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) { block.SetNull(); @@ -1712,16 +1725,16 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) } // Check the header - if (!(CheckEquihashSolution(&block, Params()) && - CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus()))) + if (!(CheckEquihashSolution(&block, consensusParams) && + CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))) return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); return true; } -bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams) { - if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) + if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), consensusParams)) return false; if (block.GetHash() != pindex->GetBlockHash()) return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", @@ -1745,10 +1758,8 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) return nSubsidy; } -bool IsInitialBlockDownload() +bool IsInitialBlockDownload(const CChainParams& chainParams) { - const CChainParams& chainParams = Params(); - // Once this function has returned false, it must remain false. static std::atomic latchToFalse{false}; // Optimization: pre-test latch before taking the lock. @@ -1776,12 +1787,12 @@ static bool fLargeWorkInvalidChainFound = false; static CBlockIndex *pindexBestForkTip = NULL; static CBlockIndex *pindexBestForkBase = NULL; -void CheckForkWarningConditions() +void CheckForkWarningConditions(const CChainParams& chainParams) { AssertLockHeld(cs_main); // Before we get past initial download, we cannot reliably alert about forks // (we assume we don't get stuck on a fork before finishing our initial sync) - if (IsInitialBlockDownload()) + if (IsInitialBlockDownload(chainParams)) return; // If our best fork is no longer within 288 blocks (+/- 12 hours if no one mines it) @@ -1819,7 +1830,7 @@ void CheckForkWarningConditions() } } -void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) +void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip, const CChainParams& chainParams) { AssertLockHeld(cs_main); // If we are on a fork that is sufficiently large, set a warning flag @@ -1849,7 +1860,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) pindexBestForkBase = pfork; } - CheckForkWarningConditions(); + CheckForkWarningConditions(chainParams); } // Requires cs_main. @@ -1872,7 +1883,7 @@ void Misbehaving(NodeId pnode, int howmuch) LogPrintf("%s: %s (%d -> %d)\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); } -void static InvalidChainFound(CBlockIndex* pindexNew) +void static InvalidChainFound(CBlockIndex* pindexNew, const CChainParams& chainParams) { if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) pindexBestInvalid = pindexNew; @@ -1886,10 +1897,10 @@ void static InvalidChainFound(CBlockIndex* pindexNew) LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); - CheckForkWarningConditions(); + CheckForkWarningConditions(chainParams); } -void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { +void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state, const CChainParams& chainParams) { int nDoS = 0; if (state.IsInvalid(nDoS)) { std::map::iterator it = mapBlockSource.find(pindex->GetBlockHash()); @@ -1904,7 +1915,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); - InvalidChainFound(pindex); + InvalidChainFound(pindex, chainParams); } } @@ -2214,8 +2225,12 @@ enum DisconnectResult }; /** Undo the effects of this block (with given index) on the UTXO set represented by coins. - * When UNCLEAN or FAILED is returned, view is left in an indeterminate state. */ -DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view) + * When UNCLEAN or FAILED is returned, view is left in an indeterminate state. + * The addressIndex and spentIndex will be updated if requested. + */ +static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& state, + const CBlockIndex* pindex, CCoinsViewCache& view, const CChainParams& chainparams, + const bool updateIndices) { assert(pindex->GetBlockHash() == view.GetBestBlock()); @@ -2236,11 +2251,36 @@ DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, error("DisconnectBlock(): block and undo data inconsistent"); return DISCONNECT_FAILED; } + std::vector addressIndex; + std::vector addressUnspentIndex; + std::vector spentIndex; // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction &tx = block.vtx[i]; - uint256 hash = tx.GetHash(); + uint256 const hash = tx.GetHash(); + + // insightexplorer + // https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2236 + if (fAddressIndex && updateIndices) { + for (unsigned int k = tx.vout.size(); k-- > 0;) { + const CTxOut &out = tx.vout[k]; + CScript::ScriptType scriptType = out.scriptPubKey.GetType(); + if (scriptType != CScript::UNKNOWN) { + uint160 const addrHash = out.scriptPubKey.AddressHash(); + + // undo receiving activity + addressIndex.push_back(make_pair( + CAddressIndexKey(scriptType, addrHash, pindex->nHeight, i, hash, k, false), + out.nValue)); + + // undo unspent index + addressUnspentIndex.push_back(make_pair( + CAddressUnspentKey(scriptType, addrHash, hash, k), + CAddressUnspentValue())); + } + } + } // Check that all outputs are available and match the outputs in the block itself // exactly. @@ -2276,6 +2316,34 @@ DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, const CTxInUndo &undo = txundo.vprevout[j]; if (!ApplyTxInUndo(undo, view, out)) fClean = false; + + // insightexplorer + // https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2304 + const CTxIn input = tx.vin[j]; + if (fAddressIndex && updateIndices) { + const CTxOut &prevout = view.GetOutputFor(input); + CScript::ScriptType scriptType = prevout.scriptPubKey.GetType(); + if (scriptType != CScript::UNKNOWN) { + uint160 const addrHash = prevout.scriptPubKey.AddressHash(); + + // undo spending activity + addressIndex.push_back(make_pair( + CAddressIndexKey(scriptType, addrHash, pindex->nHeight, i, hash, j, true), + prevout.nValue * -1)); + + // restore unspent index + addressUnspentIndex.push_back(make_pair( + CAddressUnspentKey(scriptType, addrHash, input.prevout.hash, input.prevout.n), + CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); + } + } + // insightexplorer + if (fSpentIndex && updateIndices) { + // undo and delete the spent index + spentIndex.push_back(make_pair( + CSpentIndexKey(input.prevout.hash, input.prevout.n), + CSpentIndexValue())); + } } } } @@ -2288,7 +2356,7 @@ DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, // However, this is only reliable if the last block was on or after // the Sapling activation height. Otherwise, the last anchor was the // empty root. - if (NetworkUpgradeActive(pindex->pprev->nHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { + if (NetworkUpgradeActive(pindex->pprev->nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_SAPLING)) { view.PopAnchor(pindex->pprev->hashFinalSaplingRoot, SAPLING); } else { view.PopAnchor(SaplingMerkleTree::empty_root(), SAPLING); @@ -2297,6 +2365,24 @@ DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); + // insightexplorer + if (fAddressIndex && updateIndices) { + if (!pblocktree->EraseAddressIndex(addressIndex)) { + AbortNode(state, "Failed to delete address index"); + return DISCONNECT_FAILED; + } + if (!pblocktree->UpdateAddressUnspentIndex(addressUnspentIndex)) { + AbortNode(state, "Failed to write address unspent index"); + return DISCONNECT_FAILED; + } + } + // insightexplorer + if (fSpentIndex && updateIndices) { + if (!pblocktree->UpdateSpentIndex(spentIndex)) { + AbortNode(state, "Failed to write transaction index"); + return DISCONNECT_FAILED; + } + } return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; } @@ -2337,10 +2423,11 @@ void ThreadScriptCheck() { // we're being fed a bad chain (blocks being generated much // too slowly or too quickly). // -void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CBlockIndex *const &bestHeader, +void PartitionCheck(bool (*initialDownloadCheck)(const CChainParams&), + CCriticalSection& cs, const CBlockIndex *const &bestHeader, int64_t nPowTargetSpacing) { - if (bestHeader == NULL || initialDownloadCheck()) return; + if (bestHeader == NULL || initialDownloadCheck(Params())) return; static int64_t lastAlertTime = 0; int64_t now = GetAdjustedTime(); @@ -2400,9 +2487,9 @@ static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, + CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck) { - const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); bool fExpensiveChecks = true; @@ -2418,7 +2505,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin auto disabledVerifier = libzcash::ProofVerifier::Disabled(); // Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in - if (!CheckBlock(block, state, fExpensiveChecks ? verifier : disabledVerifier, !fJustCheck, !fJustCheck)) + if (!CheckBlock(block, state, chainparams, fExpensiveChecks ? verifier : disabledVerifier, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block @@ -2493,6 +2580,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > vPos; vPos.reserve(block.vtx.size()); blockundo.vtxundo.reserve(block.vtx.size() - 1); + std::vector addressIndex; + std::vector addressUnspentIndex; + std::vector spentIndex; // Construct the incremental merkle tree at the current // block position, @@ -2516,13 +2606,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); // Grab the consensus branch ID for the block's height - auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, Params().GetConsensus()); + auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, chainparams.GetConsensus()); std::vector txdata; txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction &tx = block.vtx[i]; + const uint256 hash = tx.GetHash(); nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); @@ -2541,6 +2632,38 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"), REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met"); + // insightexplorer + // https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2597 + if (fAddressIndex || fSpentIndex) { + for (size_t j = 0; j < tx.vin.size(); j++) { + + const CTxIn input = tx.vin[j]; + const CTxOut &prevout = view.GetOutputFor(tx.vin[j]); + CScript::ScriptType scriptType = prevout.scriptPubKey.GetType(); + const uint160 addrHash = prevout.scriptPubKey.AddressHash(); + if (fAddressIndex && scriptType != CScript::UNKNOWN) { + // record spending activity + addressIndex.push_back(make_pair( + CAddressIndexKey(scriptType, addrHash, pindex->nHeight, i, hash, j, true), + prevout.nValue * -1)); + + // remove address from unspent index + addressUnspentIndex.push_back(make_pair( + CAddressUnspentKey(scriptType, addrHash, input.prevout.hash, input.prevout.n), + CAddressUnspentValue())); + } + if (fSpentIndex) { + // Add the spent index to determine the txid and input that spent an output + // and to find the amount and address from an input. + // If we do not recognize the script type, we still add an entry to the + // spentindex db, with a script type of 0 and addrhash of all zeroes. + spentIndex.push_back(make_pair( + CSpentIndexKey(input.prevout.hash, input.prevout.n), + CSpentIndexValue(hash, j, pindex->nHeight, prevout.nValue, scriptType, addrHash))); + } + } + } + // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. @@ -2563,6 +2686,28 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin control.Add(vChecks); } + // insightexplorer + // https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2656 + if (fAddressIndex) { + for (unsigned int k = 0; k < tx.vout.size(); k++) { + const CTxOut &out = tx.vout[k]; + CScript::ScriptType scriptType = out.scriptPubKey.GetType(); + if (scriptType != CScript::UNKNOWN) { + uint160 const addrHash = out.scriptPubKey.AddressHash(); + + // record receiving activity + addressIndex.push_back(make_pair( + CAddressIndexKey(scriptType, addrHash, pindex->nHeight, i, hash, k, false), + out.nValue)); + + // record unspent output + addressUnspentIndex.push_back(make_pair( + CAddressUnspentKey(scriptType, addrHash, hash, k), + CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); + } + } + } + CTxUndo undoDummy; if (i > 0) { blockundo.vtxundo.push_back(CTxUndo()); @@ -2639,7 +2784,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Move this if BLOCK_VALID_CONSENSUS is ever altered. static_assert(BLOCK_VALID_CONSENSUS == BLOCK_VALID_SCRIPTS, "nCachedBranchId must be set after all consensus rules have been validated."); - if (IsActivationHeightForAnyUpgrade(pindex->nHeight, Params().GetConsensus())) { + if (IsActivationHeightForAnyUpgrade(pindex->nHeight, chainparams.GetConsensus())) { pindex->nStatus |= BLOCK_ACTIVATES_UPGRADE; pindex->nCachedBranchId = CurrentEpochBranchId(pindex->nHeight, chainparams.GetConsensus()); } else if (pindex->pprev) { @@ -2654,6 +2799,42 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (!pblocktree->WriteTxIndex(vPos)) return AbortNode(state, "Failed to write transaction index"); + // START insightexplorer + if (fAddressIndex) { + if (!pblocktree->WriteAddressIndex(addressIndex)) { + return AbortNode(state, "Failed to write address index"); + } + if (!pblocktree->UpdateAddressUnspentIndex(addressUnspentIndex)) { + return AbortNode(state, "Failed to write address unspent index"); + } + } + if (fSpentIndex) { + if (!pblocktree->UpdateSpentIndex(spentIndex)) { + return AbortNode(state, "Failed to write spent index"); + } + } + if (fTimestampIndex) { + unsigned int logicalTS = pindex->nTime; + unsigned int prevLogicalTS = 0; + + // retrieve logical timestamp of the previous block + if (pindex->pprev) + if (!pblocktree->ReadTimestampBlockIndex(pindex->pprev->GetBlockHash(), prevLogicalTS)) + LogPrintf("%s: Failed to read previous block's logical timestamp\n", __func__); + + if (logicalTS <= prevLogicalTS) { + logicalTS = prevLogicalTS + 1; + LogPrintf("%s: Previous logical timestamp is newer Actual[%d] prevLogical[%d] Logical[%d]\n", __func__, pindex->nTime, prevLogicalTS, logicalTS); + } + + if (!pblocktree->WriteTimestampIndex(CTimestampIndexKey(logicalTS, pindex->GetBlockHash()))) + return AbortNode(state, "Failed to write timestamp index"); + + if (!pblocktree->WriteTimestampBlockIndex(CTimestampBlockIndexKey(pindex->GetBlockHash()), CTimestampBlockIndexValue(logicalTS))) + return AbortNode(state, "Failed to write blockhash index"); + } + // END insightexplorer + // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); @@ -2685,6 +2866,7 @@ enum FlushStateMode { * or always and in all cases if we're in prune mode and are deleting files. */ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { + const CChainParams& chainparams = Params(); LOCK2(cs_main, cs_LastBlockFile); static int64_t nLastWrite = 0; static int64_t nLastFlush = 0; @@ -2693,7 +2875,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { bool fFlushForPrune = false; try { if (fPruneMode && fCheckForPruning && !fReindex) { - FindFilesToPrune(setFilesToPrune); + FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); fCheckForPruning = false; if (!setFilesToPrune.empty()) { fFlushForPrune = true; @@ -2792,8 +2974,7 @@ void PruneAndFlush() { } /** Update chainActive and related internal data structures. */ -void static UpdateTip(CBlockIndex *pindexNew) { - const CChainParams& chainParams = Params(); +void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { chainActive.SetTip(pindexNew); // New best block @@ -2806,41 +2987,19 @@ void static UpdateTip(CBlockIndex *pindexNew) { Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); cvBlockChange.notify_all(); - - // Check the version of the last 100 blocks to see if we need to upgrade: - static bool fWarned = false; - if (!IsInitialBlockDownload() && !fWarned) - { - int nUpgraded = 0; - const CBlockIndex* pindex = chainActive.Tip(); - for (int i = 0; i < 100 && pindex != NULL; i++) - { - if (pindex->nVersion > CBlock::CURRENT_VERSION) - ++nUpgraded; - pindex = pindex->pprev; - } - if (nUpgraded > 0) - LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION); - if (nUpgraded > 100/2) - { - // strMiscWarning is read by GetWarnings(), called by the JSON-RPC code to warn the user: - strMiscWarning = _("Warning: This version is obsolete; upgrade required!"); - CAlert::Notify(strMiscWarning, true); - fWarned = true; - } - } } /** * Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and * mempool.removeWithoutBranchId after this, with cs_main held. */ -bool static DisconnectTip(CValidationState &state, bool fBare = false) { +bool static DisconnectTip(CValidationState &state, const CChainParams& chainparams, bool fBare = false) +{ CBlockIndex *pindexDelete = chainActive.Tip(); assert(pindexDelete); // Read block from disk. CBlock block; - if (!ReadBlockFromDisk(block, pindexDelete)) + if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus())) return AbortNode(state, "Failed to read block"); // Apply the block atomically to the chain state. uint256 sproutAnchorBeforeDisconnect = pcoinsTip->GetBestAnchor(SPROUT); @@ -2848,7 +3007,8 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { int64_t nStart = GetTimeMicros(); { CCoinsViewCache view(pcoinsTip); - if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) + // insightexplorer: update indices (true) + if (DisconnectBlock(block, state, pindexDelete, view, chainparams, true) != DISCONNECT_OK) return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); assert(view.Flush()); } @@ -2881,7 +3041,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { } // Update chainActive and related variables. - UpdateTip(pindexDelete->pprev); + UpdateTip(pindexDelete->pprev, chainparams); // Get the current commitment tree SproutMerkleTree newSproutTree; SaplingMerkleTree newSaplingTree; @@ -2908,13 +3068,14 @@ static int64_t nTimePostConnect = 0; * corresponding to pindexNew, to bypass loading it again from disk. * You probably want to call mempool.removeWithoutBranchId after this, with cs_main held. */ -bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) { +bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock) +{ assert(pindexNew->pprev == chainActive.Tip()); // Read block from disk. int64_t nTime1 = GetTimeMicros(); CBlock block; if (!pblock) { - if (!ReadBlockFromDisk(block, pindexNew)) + if (!ReadBlockFromDisk(block, pindexNew, chainparams.GetConsensus())) return AbortNode(state, "Failed to read block"); pblock = █ } @@ -2929,11 +3090,11 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CCoinsViewCache view(pcoinsTip); - bool rv = ConnectBlock(*pblock, state, pindexNew, view); + bool rv = ConnectBlock(*pblock, state, pindexNew, view, chainparams); GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) - InvalidBlockFound(pindexNew, state); + InvalidBlockFound(pindexNew, state, chainparams); return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } mapBlockSource.erase(pindexNew->GetBlockHash()); @@ -2950,13 +3111,13 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); // Remove conflicting transactions from the mempool. list txConflicted; - mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); + mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload(chainparams)); // Remove transactions that expire at new block height from mempool mempool.removeExpired(pindexNew->nHeight); // Update chainActive & related variables. - UpdateTip(pindexNew); + UpdateTip(pindexNew, chainparams); // Tell wallet about transactions that went from mempool // to conflicted: BOOST_FOREACH(const CTransaction &tx, txConflicted) { @@ -3051,7 +3212,8 @@ static void PruneBlockIndexCandidates() { * Try to make some progress towards making pindexMostWork the active block. * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. */ -static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock) { +static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock) +{ AssertLockHeld(cs_main); bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -3085,7 +3247,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { - if (!DisconnectTip(state)) + if (!DisconnectTip(state, chainparams)) return false; fBlocksDisconnected = true; } @@ -3109,11 +3271,11 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo // Connect new blocks. BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { - if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { + if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { if (state.IsInvalid()) { // The block violates a consensus rule. if (!state.CorruptionPossible()) - InvalidChainFound(vpindexToConnect.back()); + InvalidChainFound(vpindexToConnect.back(), chainparams); state = CValidationState(); fInvalidFound = true; fContinue = false; @@ -3137,14 +3299,14 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); } mempool.removeWithoutBranchId( - CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, chainparams.GetConsensus())); mempool.check(pcoinsTip); // Callbacks/notifications for a new best chain. if (fInvalidFound) - CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); + CheckForkWarningConditionsOnNewFork(vpindexToConnect.back(), chainparams); else - CheckForkWarningConditions(); + CheckForkWarningConditions(chainparams); return true; } @@ -3154,10 +3316,10 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo * or an activated best chain. pblock is either NULL or a pointer to a block * that is already loaded (to avoid loading it again from disk). */ -bool ActivateBestChain(CValidationState &state, CBlock *pblock) { +bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock) +{ CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexMostWork = NULL; - const CChainParams& chainParams = Params(); do { boost::this_thread::interruption_point(); @@ -3170,11 +3332,11 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) return true; - if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) + if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) return false; pindexNewTip = chainActive.Tip(); - fInitialDownload = IsInitialBlockDownload(); + fInitialDownload = IsInitialBlockDownload(chainparams); } // When we reach this point, we switched to a new tip (stored in pindexNewTip). @@ -3184,7 +3346,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { // Relay inventory, but don't relay old inventory during initial block download. int nBlockEstimate = 0; if (fCheckpointsEnabled) - nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints()); + nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -3196,7 +3358,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { uiInterface.NotifyBlockTip(hashNewTip); } } while(pindexMostWork != chainActive.Tip()); - CheckBlockIndex(); + CheckBlockIndex(chainparams.GetConsensus()); // Write changes periodically to disk, after relay. if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { @@ -3206,7 +3368,8 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { return true; } -bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { +bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) +{ AssertLockHeld(cs_main); // Mark the block itself as invalid. @@ -3221,10 +3384,10 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { setBlockIndexCandidates.erase(pindexWalk); // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. - if (!DisconnectTip(state)) { + if (!DisconnectTip(state, chainparams)) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); mempool.removeWithoutBranchId( - CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, chainparams.GetConsensus())); return false; } } @@ -3239,10 +3402,10 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { it++; } - InvalidChainFound(pindex); + InvalidChainFound(pindex, chainparams); mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); mempool.removeWithoutBranchId( - CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, Params().GetConsensus())); + CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, chainparams.GetConsensus())); return true; } @@ -3318,12 +3481,15 @@ void FallbackSproutValuePoolBalance( const CChainParams& chainparams ) { - // We might not want to enable the checkpointing for mainnet - // yet. if (!chainparams.ZIP209Enabled()) { return; } + // When developer option -developersetpoolsizezero is enabled, we don't need a fallback balance. + if (fExperimentalMode && mapArgs.count("-developersetpoolsizezero")) { + return; + } + // Check if the height of this block matches the checkpoint if (pindex->nHeight == chainparams.SproutValuePoolCheckpointHeight()) { if (pindex->GetBlockHash() == chainparams.SproutValuePoolCheckpointBlockHash()) { @@ -3350,9 +3516,13 @@ void FallbackSproutValuePoolBalance( } /** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */ -bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos) +bool ReceivedBlockTransactions( + const CBlock &block, + CValidationState& state, + const CChainParams& chainparams, + CBlockIndex *pindexNew, + const CDiskBlockPos& pos) { - const CChainParams& chainparams = Params(); pindexNew->nTx = block.vtx.size(); pindexNew->nChainTx = 0; CAmount sproutValue = 0; @@ -3521,7 +3691,11 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } -bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) +bool CheckBlockHeader( + const CBlockHeader& block, + CValidationState& state, + const CChainParams& chainparams, + bool fCheckPOW) { // Check block version if (block.nVersion < MIN_BLOCK_VERSION) @@ -3529,12 +3703,12 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f REJECT_INVALID, "version-too-low"); // Check Equihash solution is valid - if (fCheckPOW && !CheckEquihashSolution(&block, Params())) + if (fCheckPOW && !CheckEquihashSolution(&block, chainparams.GetConsensus())) return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"), REJECT_INVALID, "invalid-solution"); // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) + if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), REJECT_INVALID, "high-hash"); @@ -3547,6 +3721,7 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f } bool CheckBlock(const CBlock& block, CValidationState& state, + const CChainParams& chainparams, libzcash::ProofVerifier& verifier, bool fCheckPOW, bool fCheckMerkleRoot) { @@ -3554,7 +3729,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. - if (!CheckBlockHeader(block, state, fCheckPOW)) + if (!CheckBlockHeader(block, state, chainparams, fCheckPOW)) return false; // Check the merkle root. @@ -3608,9 +3783,10 @@ bool CheckBlock(const CBlock& block, CValidationState& state, return true; } -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) +bool ContextualCheckBlockHeader( + const CBlockHeader& block, CValidationState& state, + const CChainParams& chainParams, CBlockIndex * const pindexPrev) { - const CChainParams& chainParams = Params(); const Consensus::Params& consensusParams = chainParams.GetConsensus(); uint256 hash = block.GetHash(); if (hash == consensusParams.hashGenesisBlock) @@ -3646,16 +3822,18 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return true; } -bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev) +bool ContextualCheckBlock( + const CBlock& block, CValidationState& state, + const CChainParams& chainparams, CBlockIndex * const pindexPrev) { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; - const Consensus::Params& consensusParams = Params().GetConsensus(); + const Consensus::Params& consensusParams = chainparams.GetConsensus(); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) { // Check transaction contextually against consensus rules at block height - if (!ContextualCheckTransaction(tx, state, nHeight, 100)) { + if (!ContextualCheckTransaction(tx, state, chainparams, nHeight, 100)) { return false; // Failure reason has been set in validation state object } @@ -3684,9 +3862,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn return true; } -bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) +static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex=NULL) { - const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); // Check for duplicate uint256 hash = block.GetHash(); @@ -3702,7 +3879,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return true; } - if (!CheckBlockHeader(block, state)) + if (!CheckBlockHeader(block, state, chainparams)) return false; // Get prev block index @@ -3716,7 +3893,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev)) return false; if (pindex == NULL) @@ -3728,14 +3905,20 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc return true; } -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) +/** + * Store block on disk. + * JoinSplit proofs are never verified, because: + * - AcceptBlock doesn't perform script checks either. + * - The only caller of AcceptBlock verifies JoinSplit proofs elsewhere. + * If dbp is non-NULL, the file is known to already reside on disk + */ +static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) { - const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); CBlockIndex *&pindex = *ppindex; - if (!AcceptBlockHeader(block, state, &pindex)) + if (!AcceptBlockHeader(block, state, chainparams, &pindex)) return false; // Try to process all requested blocks that we don't have, but only @@ -3761,7 +3944,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // See method docstring for why this is always disabled auto verifier = libzcash::ProofVerifier::Disabled(); - if ((!CheckBlock(block, state, verifier)) || !ContextualCheckBlock(block, state, pindex->pprev)) { + if ((!CheckBlock(block, state, chainparams, verifier)) || !ContextualCheckBlock(block, state, chainparams, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); @@ -3782,7 +3965,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (dbp == NULL) if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) AbortNode(state, "Failed to write block"); - if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) + if (!ReceivedBlockTransactions(block, state, chainparams, pindex, blockPos)) return error("AcceptBlock(): ReceivedBlockTransactions failed"); } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error: ") + e.what()); @@ -3807,11 +3990,11 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned } -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) +bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos* dbp) { // Preliminary checks auto verifier = libzcash::ProofVerifier::Disabled(); - bool checked = CheckBlock(*pblock, state, verifier); + bool checked = CheckBlock(*pblock, state, chainparams, verifier); { LOCK(cs_main); @@ -3823,22 +4006,22 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool // Store to disk CBlockIndex *pindex = NULL; - bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp); + bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp); if (pindex && pfrom) { mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); } - CheckBlockIndex(); + CheckBlockIndex(chainparams.GetConsensus()); if (!ret) return error("%s: AcceptBlock FAILED", __func__); } - if (!ActivateBestChain(state, pblock)) + if (!ActivateBestChain(state, chainparams, pblock)) return error("%s: ActivateBestChain failed", __func__); return true; } -bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex * const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) +bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) { AssertLockHeld(cs_main); assert(pindexPrev == chainActive.Tip()); @@ -3851,13 +4034,13 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex auto verifier = libzcash::ProofVerifier::Disabled(); // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev)) return false; - if (!CheckBlock(block, state, verifier, fCheckPOW, fCheckMerkleRoot)) + if (!CheckBlock(block, state, chainparams, verifier, fCheckPOW, fCheckMerkleRoot)) return false; - if (!ContextualCheckBlock(block, state, pindexPrev)) + if (!ContextualCheckBlock(block, state, chainparams, pindexPrev)) return false; - if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) + if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) return false; assert(state.IsValid()); @@ -3922,13 +4105,13 @@ void UnlinkPrunedFiles(std::set& setFilesToPrune) } /* Calculate the block/rev files that should be deleted to remain under target*/ -void FindFilesToPrune(std::set& setFilesToPrune) +void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight) { LOCK2(cs_main, cs_LastBlockFile); if (chainActive.Tip() == NULL || nPruneTarget == 0) { return; } - if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) { + if (chainActive.Tip()->nHeight <= nPruneAfterHeight) { return; } @@ -4039,8 +4222,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) bool static LoadBlockIndexDB() { const CChainParams& chainparams = Params(); - - if (!pblocktree->LoadBlockIndexGuts()) + if (!pblocktree->LoadBlockIndexGuts(InsertBlockIndex)) return false; boost::this_thread::interruption_point(); @@ -4088,6 +4270,14 @@ bool static LoadBlockIndexDB() // Fall back to hardcoded Sprout value pool balance FallbackSproutValuePoolBalance(pindex, chainparams); + + // If developer option -developersetpoolsizezero has been enabled, + // override and set the in-memory size of shielded pools to zero. An unshielding transaction + // can then be used to trigger and test the handling of turnstile violations. + if (fExperimentalMode && mapArgs.count("-developersetpoolsizezero")) { + pindex->nChainSproutValue = 0; + pindex->nChainSaplingValue = 0; + } } // Construct in-memory chain of branch IDs. // Relies on invariant: a block that does not activate a network upgrade @@ -4161,6 +4351,13 @@ bool static LoadBlockIndexDB() pblocktree->ReadFlag("txindex", fTxIndex); LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); + // insightexplorer + // Check whether block explorer features are enabled + pblocktree->ReadFlag("insightexplorer", fInsightExplorer); + LogPrintf("%s: insight explorer %s\n", __func__, fAddressIndex ? "enabled" : "disabled"); + fAddressIndex = fInsightExplorer; + fSpentIndex = fInsightExplorer; + // Fill in-memory data BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { @@ -4205,7 +4402,7 @@ CVerifyDB::~CVerifyDB() uiInterface.ShowProgress("", 100); } -bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) +bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) { LOCK(cs_main); if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) @@ -4233,10 +4430,10 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth break; CBlock block; // check level 0: read from disk - if (!ReadBlockFromDisk(block, pindex)) + if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !CheckBlock(block, state, verifier)) + if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams, verifier)) return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -4249,7 +4446,8 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth } // check level 3: check for inconsistencies during memory-only disconnect of tip blocks if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { - DisconnectResult res = DisconnectBlock(block, pindex, coins); + // insightexplorer: do not update indices (false) + DisconnectResult res = DisconnectBlock(block, state, pindex, coins, chainparams, false); if (res == DISCONNECT_FAILED) { return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } @@ -4275,9 +4473,9 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)))); pindex = chainActive.Next(pindex); CBlock block; - if (!ReadBlockFromDisk(block, pindex)) + if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!ConnectBlock(block, state, pindex, coins)) + if (!ConnectBlock(block, state, pindex, coins, chainparams)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } @@ -4287,7 +4485,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth return true; } -bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) +bool RewindBlockIndex(const CChainParams& chainparams, bool& clearWitnessCaches) { LOCK(cs_main); @@ -4300,8 +4498,8 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) // // - BLOCK_ACTIVATES_UPGRADE is set only on blocks that activate upgrades. // - nCachedBranchId for each block matches what we expect. - auto sufficientlyValidated = [¶ms](const CBlockIndex* pindex) { - auto consensus = params.GetConsensus(); + auto sufficientlyValidated = [&chainparams](const CBlockIndex* pindex) { + auto consensus = chainparams.GetConsensus(); bool fFlagSet = pindex->nStatus & BLOCK_ACTIVATES_UPGRADE; bool fFlagExpected = IsActivationHeightForAnyUpgrade(pindex->nHeight, consensus); return fFlagSet == fFlagExpected && @@ -4324,7 +4522,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) if (rewindLength > 0) { LogPrintf("*** First insufficiently validated block at height %d, rewind length %d\n", nHeight, rewindLength); const uint256 *phashFirstInsufValidated = chainActive[nHeight]->phashBlock; - auto networkID = params.NetworkIDString(); + auto networkID = chainparams.NetworkIDString(); // This is true when we intend to do a long rewind. bool intendedRewind = @@ -4371,7 +4569,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) // of the blockchain). break; } - if (!DisconnectTip(state, true)) { + if (!DisconnectTip(state, chainparams, true)) { return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); } // Occasionally flush state to disk. @@ -4433,7 +4631,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) PruneBlockIndexCandidates(); - CheckBlockIndex(); + CheckBlockIndex(chainparams.GetConsensus()); if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) { return false; @@ -4481,9 +4679,8 @@ bool LoadBlockIndex() return true; } - -bool InitBlockIndex() { - const CChainParams& chainparams = Params(); +bool InitBlockIndex(const CChainParams& chainparams) +{ LOCK(cs_main); // Initialize global variables that cannot be constructed at startup. @@ -4496,12 +4693,20 @@ bool InitBlockIndex() { // Use the provided setting for -txindex in the new database fTxIndex = GetBoolArg("-txindex", false); pblocktree->WriteFlag("txindex", fTxIndex); + + // Use the provided setting for -insightexplorer in the new database + fInsightExplorer = GetBoolArg("-insightexplorer", false); + pblocktree->WriteFlag("insightexplorer", fInsightExplorer); + fAddressIndex = fInsightExplorer; + fSpentIndex = fInsightExplorer; + fTimestampIndex = fInsightExplorer; + LogPrintf("Initializing databases...\n"); // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) if (!fReindex) { try { - CBlock &block = const_cast(Params().GenesisBlock()); + CBlock &block = const_cast(chainparams.GenesisBlock()); // Start new block file unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; @@ -4511,9 +4716,9 @@ bool InitBlockIndex() { if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) return error("LoadBlockIndex(): writing genesis block to disk failed"); CBlockIndex *pindex = AddToBlockIndex(block); - if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) + if (!ReceivedBlockTransactions(block, state, chainparams, pindex, blockPos)) return error("LoadBlockIndex(): genesis block not accepted"); - if (!ActivateBestChain(state, &block)) + if (!ActivateBestChain(state, chainparams, &block)) return error("LoadBlockIndex(): genesis block cannot be activated"); // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); @@ -4525,11 +4730,8 @@ bool InitBlockIndex() { return true; } - - -bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) +bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp) { - const CChainParams& chainparams = Params(); // Map of disk positions for blocks with unknown parent (only used for reindex) static std::multimap mapBlocksUnknownParent; int64_t nStart = GetTimeMillis(); @@ -4549,10 +4751,10 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) try { // locate a header unsigned char buf[MESSAGE_START_SIZE]; - blkdat.FindByte(Params().MessageStart()[0]); + blkdat.FindByte(chainparams.MessageStart()[0]); nRewind = blkdat.GetPos()+1; blkdat >> FLATDATA(buf); - if (memcmp(buf, Params().MessageStart(), MESSAGE_START_SIZE)) + if (memcmp(buf, chainparams.MessageStart(), MESSAGE_START_SIZE)) continue; // read size blkdat >> nSize; @@ -4586,7 +4788,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) // process in case the block isn't known yet if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { CValidationState state; - if (ProcessNewBlock(state, NULL, &block, true, dbp)) + if (ProcessNewBlock(state, chainparams, NULL, &block, true, dbp)) nLoaded++; if (state.IsError()) break; @@ -4603,12 +4805,12 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) std::pair::iterator, std::multimap::iterator> range = mapBlocksUnknownParent.equal_range(head); while (range.first != range.second) { std::multimap::iterator it = range.first; - if (ReadBlockFromDisk(block, it->second)) + if (ReadBlockFromDisk(block, it->second, chainparams.GetConsensus())) { LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), head.ToString()); CValidationState dummy; - if (ProcessNewBlock(dummy, NULL, &block, true, &it->second)) + if (ProcessNewBlock(dummy, chainparams, NULL, &block, true, &it->second)) { nLoaded++; queue.push_back(block.GetHash()); @@ -4630,9 +4832,8 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) return nLoaded > 0; } -void static CheckBlockIndex() +void static CheckBlockIndex(const Consensus::Params& consensusParams) { - const Consensus::Params& consensusParams = Params().GetConsensus(); if (!fCheckBlockIndex) { return; } @@ -4917,7 +5118,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) return true; } -void static ProcessGetData(CNode* pfrom) +void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams) { int currentHeight = GetHeight(); @@ -4952,7 +5153,7 @@ void static ProcessGetData(CNode* pfrom) // best equivalent proof of work) than the best header chain we know about. send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth); + (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth); if (!send) { LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } @@ -4964,7 +5165,7 @@ void static ProcessGetData(CNode* pfrom) { // Send block from disk CBlock block; - if (!ReadBlockFromDisk(block, (*mi).second)) + if (!ReadBlockFromDisk(block, (*mi).second, consensusParams)) assert(!"cannot load block from disk"); if (inv.type == MSG_BLOCK) pfrom->PushMessage("block", block); @@ -5101,14 +5302,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // Reject incoming connections from nodes that don't know about the current epoch - const Consensus::Params& params = Params().GetConsensus(); - auto currentEpoch = CurrentEpoch(GetHeight(), params); - if (pfrom->nVersion < params.vUpgrades[currentEpoch].nProtocolVersion) + const Consensus::Params& consensusParams = chainparams.GetConsensus(); + auto currentEpoch = CurrentEpoch(GetHeight(), consensusParams); + if (pfrom->nVersion < consensusParams.vUpgrades[currentEpoch].nProtocolVersion) { LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", - params.vUpgrades[currentEpoch].nProtocolVersion)); + consensusParams.vUpgrades[currentEpoch].nProtocolVersion)); pfrom->fDisconnect = true; return false; } @@ -5158,7 +5359,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!pfrom->fInbound) { // Advertise our address - if (fListen && !IsInitialBlockDownload()) + if (fListen && !IsInitialBlockDownload(chainparams)) { CAddress addr = GetLocalAddress(&pfrom->addr); if (addr.IsRoutable()) @@ -5396,7 +5597,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); - ProcessGetData(pfrom); + ProcessGetData(pfrom, chainparams.GetConsensus()); } @@ -5452,7 +5653,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_main); - if (IsInitialBlockDownload()) + if (IsInitialBlockDownload(chainparams)) return true; CBlockIndex* pindex = NULL; @@ -5649,7 +5850,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("non-continuous headers sequence"); } - if (!AcceptBlockHeader(header, state, &pindexLast)) { + if (!AcceptBlockHeader(header, state, chainparams, &pindexLast)) { int nDoS; if (state.IsInvalid(nDoS)) { if (nDoS > 0) @@ -5670,7 +5871,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); } - CheckBlockIndex(); + CheckBlockIndex(chainparams.GetConsensus()); } else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing @@ -5688,8 +5889,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // unless we're still syncing with the network. // Such an unrequested block may still be processed, subject to the // conditions in AcceptBlock(). - bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(); - ProcessNewBlock(state, pfrom, &block, forceProcessing, NULL); + bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(chainparams); + ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL); int nDoS; if (state.IsInvalid(nDoS)) { pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), @@ -5845,7 +6046,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, uint256 alertHash = alert.GetHash(); if (pfrom->setKnown.count(alertHash) == 0) { - if (alert.ProcessAlert(Params().AlertKey())) + if (alert.ProcessAlert(chainparams.AlertKey())) { // Relay pfrom->setKnown.insert(alertHash); @@ -5974,6 +6175,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // requires LOCK(cs_vRecvMsg) bool ProcessMessages(CNode* pfrom) { + const CChainParams& chainparams = Params(); //if (fDebug) // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); @@ -5988,7 +6190,7 @@ bool ProcessMessages(CNode* pfrom) bool fOk = true; if (!pfrom->vRecvGetData.empty()) - ProcessGetData(pfrom); + ProcessGetData(pfrom, chainparams.GetConsensus()); // this maintains the order of responses if (!pfrom->vRecvGetData.empty()) return fOk; @@ -6015,7 +6217,7 @@ bool ProcessMessages(CNode* pfrom) it++; // Scan for message start - if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { + if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), MESSAGE_START_SIZE) != 0) { LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); fOk = false; break; @@ -6023,7 +6225,7 @@ bool ProcessMessages(CNode* pfrom) // Read header CMessageHeader& hdr = msg.hdr; - if (!hdr.IsValid(Params().MessageStart())) + if (!hdr.IsValid(chainparams.MessageStart())) { LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); continue; @@ -6094,7 +6296,8 @@ bool ProcessMessages(CNode* pfrom) bool SendMessages(CNode* pto, bool fSendTrickle) { - const Consensus::Params& consensusParams = Params().GetConsensus(); + const CChainParams& chainParams = Params(); + const Consensus::Params& consensusParams = chainParams.GetConsensus(); { // Don't send anything until we get its version message if (pto->nVersion == 0) @@ -6135,7 +6338,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Address refresh broadcast static int64_t nLastRebroadcast; - if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) + if (!IsInitialBlockDownload(chainParams) && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -6215,7 +6418,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Resend wallet transactions that haven't gotten in a block yet // Except during reindex, importing and IBD, when old wallet // transactions become unconfirmed and spams other nodes. - if (!fReindex && !fImporting && !IsInitialBlockDownload()) + if (!fReindex && !fImporting && !IsInitialBlockDownload(chainParams)) { GetMainSignals().Broadcast(nTimeBestReceived); } @@ -6304,7 +6507,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Message: getdata (blocks) // vector vGetData; - if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload(chainParams)) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { vector vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); @@ -6378,12 +6581,19 @@ static class CMainCleanup // Set default values of new CMutableTransaction based on consensus rules at given height. CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight) { - CMutableTransaction mtx; + return CreateNewContextualCMutableTransaction(consensusParams, nHeight, expiryDelta); +} +CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight, int nExpiryDelta) { + CMutableTransaction mtx; bool isOverwintered = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_OVERWINTER); if (isOverwintered) { mtx.fOverwintered = true; - mtx.nExpiryHeight = nHeight + expiryDelta; + mtx.nExpiryHeight = nHeight + nExpiryDelta; + + if (mtx.nExpiryHeight <= 0 || mtx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD) { + throw new std::runtime_error("CreateNewContextualCMutableTransaction: invalid expiry height"); + } // NOTE: If the expiry height crosses into an incompatible consensus epoch, and it is changed to the last block // of the current epoch (see below: Overwinter->Sapling), the transaction will be rejected if it falls within diff --git a/src/main.h b/src/main.h index ce489d9ae9c..cd2e39a0957 100644 --- a/src/main.h +++ b/src/main.h @@ -26,6 +26,9 @@ #include "tinyformat.h" #include "txmempool.h" #include "uint256.h" +#include "addressindex.h" +#include "spentindex.h" +#include "timestampindex.h" #include #include @@ -41,6 +44,7 @@ class CBlockIndex; class CBlockTreeDB; class CBloomFilter; +class CChainParams; class CInv; class CScriptCheck; class CValidationInterface; @@ -131,6 +135,22 @@ extern bool fImporting; extern bool fReindex; extern int nScriptCheckThreads; extern bool fTxIndex; + +// START insightexplorer +extern bool fInsightExplorer; + +// The following flags enable specific indices (DB tables), but are not exposed as +// separate command-line options; instead they are enabled by experimental feature "-insightexplorer" +// and are always equal to the overall controlling flag, fInsightExplorer. + +// Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses +extern bool fAddressIndex; + +// Maintain a full spent index, used to query the spending txid and input index for an outpoint +extern bool fSpentIndex; + +// END insightexplorer + extern bool fIsBareMultisigStd; extern bool fCheckBlockIndex; extern bool fCheckpointsEnabled; @@ -185,7 +205,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals); * @param[out] dbp If pblock is stored to disk (or already there), this will be set to its location. * @return True if state.IsValid() */ -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp); +bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos* dbp); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); /** Open a block file (blk?????.dat) */ @@ -195,9 +215,9 @@ FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); /** Translation to a filesystem path */ boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix); /** Import blocks from an external file */ -bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); +bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = NULL); /** Initialize a new block tree database + block data on disk */ -bool InitBlockIndex(); +bool InitBlockIndex(const CChainParams& chainparams); /** Load the block tree and coins database from disk */ bool LoadBlockIndex(); /** Unload database information */ @@ -214,15 +234,15 @@ bool SendMessages(CNode* pto, bool fSendTrickle); /** Run an instance of the script checking thread */ void ThreadScriptCheck(); /** Try to detect Partition (network isolation) attacks against us */ -void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CBlockIndex *const &bestHeader, int64_t nPowTargetSpacing); +void PartitionCheck(bool (*initialDownloadCheck)(const CChainParams&), CCriticalSection& cs, const CBlockIndex *const &bestHeader, int64_t nPowTargetSpacing); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ -bool IsInitialBlockDownload(); +bool IsInitialBlockDownload(const CChainParams& chainParams); /** Format a string that describes several potential problems detected by the core */ std::string GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ -bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); +bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false); /** Find the best known block, and make it the tip of the block chain */ -bool ActivateBestChain(CValidationState &state, CBlock *pblock = NULL); +bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** @@ -240,7 +260,7 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); * * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned */ -void FindFilesToPrune(std::set& setFilesToPrune); +void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight); /** * Actually unlink the specified files @@ -270,30 +290,6 @@ struct CNodeStateStats { std::vector vHeightInFlight; }; -struct CDiskTxPos : public CDiskBlockPos -{ - unsigned int nTxOffset; // after header - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*(CDiskBlockPos*)this); - READWRITE(VARINT(nTxOffset)); - } - - CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) { - } - - CDiskTxPos() { - SetNull(); - } - - void SetNull() { - CDiskBlockPos::SetNull(); - nTxOffset = 0; - } -}; CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree); @@ -345,8 +341,9 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons std::vector *pvChecks = NULL); /** Check a transaction contextually against a set of consensus rules */ -bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel, - bool (*isInitBlockDownload)() = IsInitialBlockDownload); +bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, + const CChainParams& chainparams, int nHeight, int dosLevel, + bool (*isInitBlockDownload)(const CChainParams&) = IsInitialBlockDownload); /** Apply the effects of this transaction on the UTXO set represented by view */ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); @@ -360,7 +357,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio /** Check for standard transaction types * @return True if all outputs (scriptPubKeys) use only standard transaction forms */ -bool IsStandardTx(const CTransaction& tx, std::string& reason, int nHeight = 0); +bool IsStandardTx(const CTransaction& tx, std::string& reason, const CChainParams& chainparams, int nHeight = 0); namespace Consensus { @@ -440,47 +437,40 @@ class CScriptCheck ScriptError GetScriptError() const { return error; } }; +bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); /** Functions for disk access for blocks */ -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); -bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos); -bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); - +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); +bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams); +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams); /** Functions for validating blocks and updating the block tree */ -/** Undo the effects of this block (with given index) on the UTXO set represented by coins. - * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean - * will be true if no problems were found. Otherwise, the return value will be false in case - * of problems. Note that in any case, coins may be modified. */ -bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); - -/** Apply the effects of this block (with given index) on the UTXO set represented by coins */ -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); - /** Context-independent validity checks */ -bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, + const CChainParams& chainparams, + bool fCheckPOW = true); bool CheckBlock(const CBlock& block, CValidationState& state, + const CChainParams& chainparams, libzcash::ProofVerifier& verifier, bool fCheckPOW = true, bool fCheckMerkleRoot = true); -/** Context-dependent validity checks */ -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev); -bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev); +/** Context-dependent validity checks. + * By "context", we mean only the previous block headers, but not the UTXO + * set; UTXO-related validity checks are done in ConnectBlock(). */ +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, + const CChainParams& chainparams, CBlockIndex *pindexPrev); +bool ContextualCheckBlock(const CBlock& block, CValidationState& state, + const CChainParams& chainparams, CBlockIndex *pindexPrev); -/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ -bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex *pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); - -/** - * Store block on disk. - * JoinSplit proofs are never verified, because: - * - AcceptBlock doesn't perform script checks either. - * - The only caller of AcceptBlock verifies JoinSplit proofs elsewhere. - * If dbp is non-NULL, the file is known to already reside on disk - */ -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp); -bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL); +/** Apply the effects of this block (with given index) on the UTXO set represented by coins. + * Validity checks that depend on the UTXO set are also done; ConnectBlock() + * can fail if those validity checks fail (among other reasons). */ +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, + const CChainParams& chainparams, bool fJustCheck = false); +/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ +bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); /** @@ -491,75 +481,21 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc * clearWitnessCaches is an output parameter that will be set to true iff * witness caches should be cleared in order to handle an intended long rewind. */ -bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches); - -class CBlockFileInfo -{ -public: - unsigned int nBlocks; //! number of blocks stored in file - unsigned int nSize; //! number of used bytes of block file - unsigned int nUndoSize; //! number of used bytes in the undo file - unsigned int nHeightFirst; //! lowest height of block in file - unsigned int nHeightLast; //! highest height of block in file - uint64_t nTimeFirst; //! earliest time of block in file - uint64_t nTimeLast; //! latest time of block in file - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(VARINT(nBlocks)); - READWRITE(VARINT(nSize)); - READWRITE(VARINT(nUndoSize)); - READWRITE(VARINT(nHeightFirst)); - READWRITE(VARINT(nHeightLast)); - READWRITE(VARINT(nTimeFirst)); - READWRITE(VARINT(nTimeLast)); - } - - void SetNull() { - nBlocks = 0; - nSize = 0; - nUndoSize = 0; - nHeightFirst = 0; - nHeightLast = 0; - nTimeFirst = 0; - nTimeLast = 0; - } - - CBlockFileInfo() { - SetNull(); - } - - std::string ToString() const; - - /** update statistics (does not update nSize) */ - void AddBlock(unsigned int nHeightIn, uint64_t nTimeIn) { - if (nBlocks==0 || nHeightFirst > nHeightIn) - nHeightFirst = nHeightIn; - if (nBlocks==0 || nTimeFirst > nTimeIn) - nTimeFirst = nTimeIn; - nBlocks++; - if (nHeightIn > nHeightLast) - nHeightLast = nHeightIn; - if (nTimeIn > nTimeLast) - nTimeLast = nTimeIn; - } -}; +bool RewindBlockIndex(const CChainParams& chainparams, bool& clearWitnessCaches); /** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */ class CVerifyDB { public: CVerifyDB(); ~CVerifyDB(); - bool VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth); + bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth); }; /** Find the last common block between the parameter chain and a locator. */ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator); /** Mark a block as invalid. */ -bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex); +bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex); /** Remove invalidity status from a block and its descendants. */ bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex); @@ -582,7 +518,10 @@ int GetSpendHeight(const CCoinsViewCache& inputs); uint64_t CalculateCurrentUsage(); -/** Return a CMutableTransaction with contextual default values based on set of consensus rules at height */ +/** Return a CMutableTransaction with contextual default values based on set of consensus rules at nHeight, and the default expiry delta. */ CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight); +/** Return a CMutableTransaction with contextual default values based on set of consensus rules at nHeight, and given expiry delta. */ +CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight, int nExpiryDelta); + #endif // BITCOIN_MAIN_H diff --git a/src/metrics.cpp b/src/metrics.cpp index f64d4a21227..d971a871570 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -216,7 +216,7 @@ int printStats(bool mining) } auto localsolps = GetLocalSolPS(); - if (IsInitialBlockDownload()) { + if (IsInitialBlockDownload(Params())) { int netheight = EstimateNetHeight(height, tipmediantime, Params()); int downloadPercent = height * 100 / netheight; std::cout << " " << _("Downloading blocks") << " | " << height << " / ~" << netheight << " (" << downloadPercent << "%) " << std::endl; @@ -253,7 +253,7 @@ int printMiningStatus(bool mining) } if (fvNodesEmpty) { std::cout << _("Mining is paused while waiting for connections.") << std::endl; - } else if (IsInitialBlockDownload()) { + } else if (IsInitialBlockDownload(Params())) { std::cout << _("Mining is paused while downloading blocks.") << std::endl; } else { std::cout << _("Mining is paused (a JoinSplit may be in progress).") << std::endl; diff --git a/src/miner.cpp b/src/miner.cpp index 0789ad2ac1b..e8f39ef5d7c 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -106,9 +106,8 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, } } -CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) +CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn) { - const CChainParams& chainparams = Params(); // Create new block std::unique_ptr pblocktemplate(new CBlockTemplate()); if(!pblocktemplate.get()) @@ -117,7 +116,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios - if (Params().MineBlocksOnDemand()) + if (chainparams.MineBlocksOnDemand()) pblock->nVersion = GetArg("-blockversion", pblock->nVersion); // Add dummy coinbase tx as first transaction @@ -324,7 +323,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // create only contains transactions that are valid in new blocks. CValidationState state; PrecomputedTransactionData txdata(tx); - if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) + if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, chainparams.GetConsensus(), consensusBranchId)) continue; if (chainparams.ZIP209Enabled() && monitoring_pool_balances) { @@ -423,13 +422,13 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashFinalSaplingRoot = sapling_tree.root(); - UpdateTime(pblock, Params().GetConsensus(), pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); pblock->nSolution.clear(); pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CValidationState state; - if (!TestBlockValidity(state, *pblock, pindexPrev, false, false)) + if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed"); } @@ -467,7 +466,7 @@ void GetScriptForMinerAddress(boost::shared_ptr &script) script->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; } -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce) { // Update nExtraNonce static uint256 hashPrevBlock; @@ -486,7 +485,7 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& pblock->hashMerkleRoot = pblock->BuildMerkleTree(); } -static bool ProcessBlockFound(CBlock* pblock, const CChainParams& chainparams) +static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainparams) { LogPrintf("%s\n", pblock->ToString()); LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue)); @@ -503,7 +502,7 @@ static bool ProcessBlockFound(CBlock* pblock, const CChainParams& chainparams) // Process this block the same as if we had received it from another node CValidationState state; - if (!ProcessNewBlock(state, NULL, pblock, true, NULL)) + if (!ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL)) return error("ZcashMiner: ProcessNewBlock, block not accepted"); TrackMinedBlock(pblock->GetHash()); @@ -523,8 +522,8 @@ void static BitcoinMiner(const CChainParams& chainparams) boost::shared_ptr coinbaseScript; GetMainSignals().ScriptForMining(coinbaseScript); - unsigned int n = chainparams.EquihashN(); - unsigned int k = chainparams.EquihashK(); + unsigned int n = chainparams.GetConsensus().nEquihashN; + unsigned int k = chainparams.GetConsensus().nEquihashK; std::string solver = GetArg("-equihashsolver", "default"); assert(solver == "tromp" || solver == "default"); @@ -556,7 +555,7 @@ void static BitcoinMiner(const CChainParams& chainparams) LOCK(cs_vNodes); fvNodesEmpty = vNodes.empty(); } - if (!fvNodesEmpty && !IsInitialBlockDownload()) + if (!fvNodesEmpty && !IsInitialBlockDownload(chainparams)) break; MilliSleep(1000); } while (true); @@ -569,7 +568,7 @@ void static BitcoinMiner(const CChainParams& chainparams) unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); CBlockIndex* pindexPrev = chainActive.Tip(); - unique_ptr pblocktemplate(CreateNewBlock(coinbaseScript->reserveScript)); + unique_ptr pblocktemplate(CreateNewBlock(chainparams, coinbaseScript->reserveScript)); if (!pblocktemplate.get()) { if (GetArg("-mineraddress", "").empty()) { diff --git a/src/miner.h b/src/miner.h index 07098905445..9059be633e5 100644 --- a/src/miner.h +++ b/src/miner.h @@ -24,13 +24,13 @@ struct CBlockTemplate }; /** Generate a new block, without valid proof-of-work */ -CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); +CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn); #ifdef ENABLE_MINING /** Get script for -mineraddress */ void GetScriptForMinerAddress(boost::shared_ptr &script); /** Modify the extranonce in a block */ -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce); /** Run the miner threads */ void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams); #endif diff --git a/src/policy/fees.h b/src/policy/fees.h index ee865c67911..52b0c36cdc7 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -259,8 +259,8 @@ class CBlockPolicyEstimator void Read(CAutoFile& filein); private: - CFeeRate minTrackedFee; //! Passed to constructor to avoid dependency on main - double minTrackedPriority; //! Set to AllowFreeThreshold + CFeeRate minTrackedFee; //!< Passed to constructor to avoid dependency on main + double minTrackedPriority; //!< Set to AllowFreeThreshold unsigned int nBestSeenHeight; struct TxStatsInfo { diff --git a/src/pow.cpp b/src/pow.cpp index 743df08ee30..ec0410acca2 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -91,10 +91,10 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg, return bnNew.GetCompact(); } -bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& params) +bool CheckEquihashSolution(const CBlockHeader *pblock, const Consensus::Params& params) { - unsigned int n = params.EquihashN(); - unsigned int k = params.EquihashK(); + unsigned int n = params.nEquihashN; + unsigned int k = params.nEquihashK; // Hash state crypto_generichash_blake2b_state state; diff --git a/src/pow.h b/src/pow.h index 30d0f5e63b7..e3cf822d8dd 100644 --- a/src/pow.h +++ b/src/pow.h @@ -22,7 +22,7 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg, const Consensus::Params&); /** Check whether the Equihash solution in a block header is valid */ -bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams&); +bool CheckEquihashSolution(const CBlockHeader *pblock, const Consensus::Params&); /** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&); diff --git a/src/rest.cpp b/src/rest.cpp index 4f92bbb0af7..e70ce517aac 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "chainparams.h" #include "primitives/block.h" #include "primitives/transaction.h" #include "main.h" @@ -214,7 +215,7 @@ static bool rest_block(HTTPRequest* req, if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)"); - if (!ReadBlockFromDisk(block, pblockindex)) + if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); } @@ -355,7 +356,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) CTransaction tx; uint256 hashBlock = uint256(); - if (!GetTransaction(hash, tx, hashBlock, true)) + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 58089d0d676..c0ca1f07c43 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -493,7 +493,7 @@ UniValue getblock(const UniValue& params, bool fHelp) if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - if(!ReadBlockFromDisk(block, pblockindex)) + if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); if (verbosity == 0) @@ -650,7 +650,7 @@ UniValue verifychain(const UniValue& params, bool fHelp) if (params.size() > 1) nCheckDepth = params[1].get_int(); - return CVerifyDB().VerifyDB(pcoinsTip, nCheckLevel, nCheckDepth); + return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth); } /** Implementation of IsSuperMajority with better feedback */ @@ -976,11 +976,11 @@ UniValue invalidateblock(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); CBlockIndex* pblockindex = mapBlockIndex[hash]; - InvalidateBlock(state, pblockindex); + InvalidateBlock(state, Params(), pblockindex); } if (state.IsValid()) { - ActivateBestChain(state); + ActivateBestChain(state, Params()); } if (!state.IsValid()) { @@ -1019,7 +1019,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp) } if (state.IsValid()) { - ActivateBestChain(state); + ActivateBestChain(state, Params()); } if (!state.IsValid()) { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 5288d977e60..61c521a7b97 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -17,8 +17,8 @@ using namespace std; class CRPCConvertParam { public: - std::string methodName; //! method whose params want conversion - int paramIdx; //! 0-based idx of param to convert + std::string methodName; //!< method whose params want conversion + int paramIdx; //!< 0-based idx of param to convert }; static const CRPCConvertParam vRPCConvertParams[] = @@ -130,7 +130,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "z_importkey", 2 }, { "z_importviewingkey", 2 }, { "z_getpaymentdisclosure", 1}, - { "z_getpaymentdisclosure", 2} + { "z_getpaymentdisclosure", 2}, + { "z_setmigration", 0}, }; class CRPCConvertTable diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 34406f9c4eb..0a9ef875725 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -195,11 +195,11 @@ UniValue generate(const UniValue& params, bool fHelp) } unsigned int nExtraNonce = 0; UniValue blockHashes(UniValue::VARR); - unsigned int n = Params().EquihashN(); - unsigned int k = Params().EquihashK(); + unsigned int n = Params().GetConsensus().nEquihashN; + unsigned int k = Params().GetConsensus().nEquihashK; while (nHeight < nHeightEnd) { - std::unique_ptr pblocktemplate(CreateNewBlock(coinbaseScript->reserveScript)); + std::unique_ptr pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; @@ -247,7 +247,7 @@ UniValue generate(const UniValue& params, bool fHelp) } endloop: CValidationState state; - if (!ProcessNewBlock(state, NULL, pblock, true, NULL)) + if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL)) throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); ++nHeight; blockHashes.push_back(pblock->GetHash().GetHex()); @@ -523,7 +523,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (block.hashPrevBlock != pindexPrev->GetBlockHash()) return "inconclusive-not-best-prevblk"; CValidationState state; - TestBlockValidity(state, block, pindexPrev, false, true); + TestBlockValidity(state, Params(), block, pindexPrev, false, true); return BIP22ValidationResult(state); } } @@ -534,7 +534,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (vNodes.empty()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Zcash is not connected!"); - if (IsInitialBlockDownload()) + if (IsInitialBlockDownload(Params())) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Zcash is downloading blocks..."); static unsigned int nTransactionsUpdatedLast; @@ -614,7 +614,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (!coinbaseScript->reserveScript.size()) throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet or -mineraddress)"); - pblocktemplate = CreateNewBlock(coinbaseScript->reserveScript); + pblocktemplate = CreateNewBlock(Params(), coinbaseScript->reserveScript); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -782,7 +782,7 @@ UniValue submitblock(const UniValue& params, bool fHelp) CValidationState state; submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(state, NULL, &block, true, NULL); + bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL); UnregisterValidationInterface(&sc); if (fBlockPresent) { diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 90069112b8a..80ff66ee7bd 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -147,7 +147,8 @@ UniValue TxShieldedOutputsToJSON(const CTransaction& tx) { void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { - entry.push_back(Pair("txid", tx.GetHash().GetHex())); + const uint256 txid = tx.GetHash(); + entry.push_back(Pair("txid", txid.GetHex())); entry.push_back(Pair("overwintered", tx.fOverwintered)); entry.push_back(Pair("version", tx.nVersion)); if (tx.fOverwintered) { @@ -169,6 +170,20 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true))); o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); in.push_back(Pair("scriptSig", o)); + + // Add address and value info if spentindex enabled + CSpentIndexValue spentInfo; + CSpentIndexKey spentKey(txin.prevout.hash, txin.prevout.n); + if (GetSpentIndex(spentKey, spentInfo)) { + in.push_back(Pair("value", ValueFromAmount(spentInfo.satoshis))); + in.push_back(Pair("valueSat", spentInfo.satoshis)); + + boost::optional dest = + DestFromAddressHash(spentInfo.addressType, spentInfo.addressHash); + if (dest) { + in.push_back(Pair("address", EncodeDestination(*dest))); + } + } } in.push_back(Pair("sequence", (int64_t)txin.nSequence)); vin.push_back(in); @@ -184,6 +199,15 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(txout.scriptPubKey, o, true); out.push_back(Pair("scriptPubKey", o)); + + // Add spent information if spentindex is enabled + CSpentIndexValue spentInfo; + CSpentIndexKey spentKey(txid, i); + if (GetSpentIndex(spentKey, spentInfo)) { + out.push_back(Pair("spentTxId", spentInfo.txid.GetHex())); + out.push_back(Pair("spentIndex", (int)spentInfo.inputIndex)); + out.push_back(Pair("spentHeight", spentInfo.blockHeight)); + } vout.push_back(out); } entry.push_back(Pair("vout", vout)); @@ -208,12 +232,14 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; if (chainActive.Contains(pindex)) { + entry.push_back(Pair("height", pindex->nHeight)); entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); entry.push_back(Pair("time", pindex->GetBlockTime())); entry.push_back(Pair("blocktime", pindex->GetBlockTime())); - } - else + } else { + entry.push_back(Pair("height", -1)); entry.push_back(Pair("confirmations", 0)); + } } } } @@ -312,17 +338,17 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1") ); - LOCK(cs_main); - uint256 hash = ParseHashV(params[0], "parameter 1"); bool fVerbose = false; if (params.size() > 1) fVerbose = (params[1].get_int() != 0); + LOCK(cs_main); + CTransaction tx; uint256 hashBlock; - if (!GetTransaction(hash, tx, hashBlock, true)) + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); string strHex = EncodeHexTx(tx); @@ -392,7 +418,7 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp) if (pblockindex == NULL) { CTransaction tx; - if (!GetTransaction(oneTxid, tx, hashBlock, false) || hashBlock.IsNull()) + if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); if (!mapBlockIndex.count(hashBlock)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); @@ -400,7 +426,7 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp) } CBlock block; - if(!ReadBlockFromDisk(block, pblockindex)) + if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); unsigned int ntxFound = 0; diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index ec1ac52551f..6719443d0b7 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -965,12 +965,12 @@ namespace { */ class CTransactionSignatureSerializer { private: - const CTransaction &txTo; //! reference to the spending transaction (the one being serialized) - const CScript &scriptCode; //! output script being consumed - const unsigned int nIn; //! input index of txTo being signed - const bool fAnyoneCanPay; //! whether the hashtype has the SIGHASH_ANYONECANPAY flag set - const bool fHashSingle; //! whether the hashtype is SIGHASH_SINGLE - const bool fHashNone; //! whether the hashtype is SIGHASH_NONE + const CTransaction& txTo; //!< reference to the spending transaction (the one being serialized) + const CScript& scriptCode; //!< output script being consumed + const unsigned int nIn; //!< input index of txTo being signed + const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set + const bool fHashSingle; //!< whether the hashtype is SIGHASH_SINGLE + const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE public: CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : diff --git a/src/script/script.cpp b/src/script/script.cpp index d5234cae803..bcd1c6b2d11 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -201,6 +201,19 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const return subscript.GetSigOpCount(true); } +// insightexplorer +// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-f7ca24fb80ddba0f291cb66344ca6fcbR204 +bool CScript::IsPayToPublicKeyHash() const +{ + // Extra-fast test for pay-to-pubkey-hash CScripts: + return (this->size() == 25 && + (*this)[0] == OP_DUP && + (*this)[1] == OP_HASH160 && + (*this)[2] == 0x14 && + (*this)[23] == OP_EQUALVERIFY && + (*this)[24] == OP_CHECKSIG); +} + bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: @@ -227,3 +240,33 @@ bool CScript::IsPushOnly() const } return true; } + +// insightexplorer +CScript::ScriptType CScript::GetType() const +{ + if (this->IsPayToPublicKeyHash()) + return CScript::P2PKH; + if (this->IsPayToScriptHash()) + return CScript::P2SH; + // We don't know this script + return CScript::UNKNOWN; +} + +// insightexplorer +uint160 CScript::AddressHash() const +{ + // where the address bytes begin depends on the script type + int start; + if (this->IsPayToPublicKeyHash()) + start = 3; + else if (this->IsPayToScriptHash()) + start = 2; + else { + // unknown script type; return zeros + vector hashBytes; + hashBytes.resize(20); + return uint160(hashBytes); + } + vector hashBytes(this->begin()+start, this->begin()+start+20); + return uint160(hashBytes); +} diff --git a/src/script/script.h b/src/script/script.h index 541ece1f519..2a511cd2eb5 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -18,6 +18,8 @@ #include #include +#include "uint256.h" + static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes // Maximum script length in bytes @@ -565,7 +567,16 @@ class CScript : public CScriptBase */ unsigned int GetSigOpCount(const CScript& scriptSig) const; + // insightexplorer, there may be more script types in the future + enum ScriptType : int { + UNKNOWN = 0, + P2PKH = 1, + P2SH = 2, + }; + bool IsPayToPublicKeyHash() const; bool IsPayToScriptHash() const; + ScriptType GetType() const; + uint160 AddressHash() const; /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ bool IsPushOnly() const; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 88cde36988a..12df6d8556a 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -320,3 +320,16 @@ CScript GetScriptForMultisig(int nRequired, const std::vector& keys) bool IsValidDestination(const CTxDestination& dest) { return dest.which() != 0; } + +// insightexplorer +boost::optional DestFromAddressHash(int scriptType, uint160& addressHash) +{ + switch (scriptType) { + case CScript::P2PKH: + return CTxDestination(CKeyID(addressHash)); + case CScript::P2SH: + return CTxDestination(CScriptID(addressHash)); + default: + return boost::none; + } +} diff --git a/src/script/standard.h b/src/script/standard.h index fdb02f7c74f..40b3c329d6c 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -25,7 +25,7 @@ class CScriptID : public uint160 CScriptID(const uint160& in) : uint160(in) {} }; -static const unsigned int MAX_OP_RETURN_RELAY = 80; //! bytes +static const unsigned int MAX_OP_RETURN_RELAY = 80; //!< bytes extern unsigned nMaxDatacarrierBytes; /** @@ -96,4 +96,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: CScript GetScriptForDestination(const CTxDestination& dest); CScript GetScriptForMultisig(int nRequired, const std::vector& keys); +// insightexplorer +boost::optional DestFromAddressHash(int scriptType, uint160& addressHash); + #endif // BITCOIN_SCRIPT_STANDARD_H diff --git a/src/serialize.h b/src/serialize.h index a945650d6f5..ac2db6eddfb 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -110,6 +110,11 @@ template inline void ser_writedata32(Stream &s, uint32_t obj) obj = htole32(obj); s.write((char*)&obj, 4); } +template inline void ser_writedata32be(Stream &s, uint32_t obj) +{ + obj = htobe32(obj); + s.write((char*)&obj, 4); +} template inline void ser_writedata64(Stream &s, uint64_t obj) { obj = htole64(obj); @@ -133,6 +138,12 @@ template inline uint32_t ser_readdata32(Stream &s) s.read((char*)&obj, 4); return le32toh(obj); } +template inline uint32_t ser_readdata32be(Stream &s) +{ + uint32_t obj; + s.read((char*)&obj, 4); + return be32toh(obj); +} template inline uint64_t ser_readdata64(Stream &s) { uint64_t obj; diff --git a/src/spentindex.h b/src/spentindex.h new file mode 100644 index 00000000000..3e11b60809a --- /dev/null +++ b/src/spentindex.h @@ -0,0 +1,97 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_SPENTINDEX_H +#define BITCOIN_SPENTINDEX_H + +#include "uint256.h" +#include "amount.h" + +struct CSpentIndexKey { + uint256 txid; + unsigned int outputIndex; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(txid); + READWRITE(outputIndex); + } + + CSpentIndexKey(uint256 t, unsigned int i) { + txid = t; + outputIndex = i; + } + + CSpentIndexKey() { + SetNull(); + } + + void SetNull() { + txid.SetNull(); + outputIndex = 0; + } +}; + +struct CSpentIndexValue { + uint256 txid; + unsigned int inputIndex; + int blockHeight; + CAmount satoshis; + int addressType; + uint160 addressHash; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(txid); + READWRITE(inputIndex); + READWRITE(blockHeight); + READWRITE(satoshis); + READWRITE(addressType); + READWRITE(addressHash); + } + + CSpentIndexValue(uint256 t, unsigned int i, int h, CAmount s, int type, uint160 a) { + txid = t; + inputIndex = i; + blockHeight = h; + satoshis = s; + addressType = type; + addressHash = a; + } + + CSpentIndexValue() { + SetNull(); + } + + void SetNull() { + txid.SetNull(); + inputIndex = 0; + blockHeight = 0; + satoshis = 0; + addressType = 0; + addressHash.SetNull(); + } + + bool IsNull() const { + return txid.IsNull(); + } +}; + +struct CSpentIndexKeyCompare +{ + bool operator()(const CSpentIndexKey& a, const CSpentIndexKey& b) const { + if (a.txid == b.txid) { + return a.outputIndex < b.outputIndex; + } else { + return a.txid < b.txid; + } + } +}; + +#endif // BITCOIN_SPENTINDEX_H diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index a0b49b4ddc2..18acf1f67c5 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -383,7 +383,7 @@ BOOST_AUTO_TEST_CASE(AlertDisablesRPC) mapAlerts.clear(); } -static bool falseFunc() { return false; } +static bool falseFunc(const CChainParams&) { return false; } BOOST_AUTO_TEST_CASE(PartitionAlert) { diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index c813c9af9ae..dc906282034 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -58,7 +58,7 @@ BOOST_AUTO_TEST_CASE(May15) // After May 15'th, big blocks are OK: forkingBlock.nTime = tMay15; // Invalidates PoW auto verifier = libzcash::ProofVerifier::Strict(); - BOOST_CHECK(CheckBlock(forkingBlock, state, verifier, false, false)); + BOOST_CHECK(CheckBlock(forkingBlock, state, Params(), verifier, false, false)); } SetMockTime(0); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 3f6a0af77ea..23e7634abbd 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -138,6 +138,7 @@ struct { // NOTE: These tests rely on CreateNewBlock doing its own self-validation! BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { + const CChainParams& chainparams = Params(CBaseChainParams::MAIN); CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; CBlockTemplate *pblocktemplate; CMutableTransaction tx,tx2; @@ -158,7 +159,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i) { // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 4; @@ -182,8 +183,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) /* { arith_uint256 try_nonce(0); - unsigned int n = Params().EquihashN(); - unsigned int k = Params().EquihashK(); + unsigned int n = Params().GetConsensus().nEquihashN; + unsigned int k = Params().GetConsensus().nEquihashK; // Hash state crypto_generichash_blake2b_state eh_state; @@ -264,7 +265,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->hashFinalSaplingRoot = uint256(); CValidationState state; - BOOST_CHECK(ProcessNewBlock(state, NULL, pblock, true, NULL)); + BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL)); BOOST_CHECK_MESSAGE(state.IsValid(), state.GetRejectReason()); pblock->hashPrevBlock = pblock->GetHash(); @@ -273,7 +274,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) } // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; // block sigops > limit: 1000 CHECKMULTISIG + 1 @@ -292,7 +293,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -313,14 +314,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); // orphan in mempool hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -338,7 +339,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 49000LL; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -349,7 +350,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 0; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -367,7 +368,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue -= 10000; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -381,17 +382,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); // subsidy changing int nHeight = chainActive.Height(); chainActive.Tip()->nHeight = 209999; - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = 210000; - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = nHeight; @@ -423,7 +424,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx2)); BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); // Neither tx should have made it into the template. BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); @@ -438,7 +439,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) //BOOST_CHECK(CheckFinalTx(tx)); //BOOST_CHECK(CheckFinalTx(tx2)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 2); delete pblocktemplate; diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index bfcc8f88d5e..f04d3f2c216 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -296,6 +296,15 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) BOOST_CHECK_NO_THROW(CallRPC("getblock 0 2")); BOOST_CHECK_THROW(CallRPC("getblock 0 -1"), runtime_error); // bad verbosity BOOST_CHECK_THROW(CallRPC("getblock 0 3"), runtime_error); // bad verbosity + + /* + * migration (sprout to sapling) + */ + BOOST_CHECK_NO_THROW(CallRPC("z_setmigration true")); + BOOST_CHECK_NO_THROW(CallRPC("z_setmigration false")); + BOOST_CHECK_THROW(CallRPC("z_setmigration"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_setmigration nonboolean"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_setmigration 1"), runtime_error); } BOOST_AUTO_TEST_CASE(rpc_wallet_getbalance) @@ -1293,7 +1302,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) pwalletMain->AddToWallet(wtx, true, NULL); // Context that z_sendmany requires - auto builder = TransactionBuilder(consensusParams, nextBlockHeight, pwalletMain); + auto builder = TransactionBuilder(consensusParams, nextBlockHeight, expiryDelta, pwalletMain); mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight); std::vector recipients = { SendManyRecipient(zaddr1, 1 * COIN, "ABCD") }; diff --git a/src/test/script_P2PKH_tests.cpp b/src/test/script_P2PKH_tests.cpp new file mode 100644 index 00000000000..3a7dc16608a --- /dev/null +++ b/src/test/script_P2PKH_tests.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2012-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "script/script.h" +#include "test/test_bitcoin.h" + +#include + +using namespace std; + +BOOST_FIXTURE_TEST_SUITE(script_P2PKH_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(IsPayToPublicKeyHash) +{ + // Test CScript::IsPayToPublicKeyHash() + uint160 dummy; + CScript p2pkh; + p2pkh << OP_DUP << OP_HASH160 << ToByteVector(dummy) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(p2pkh.IsPayToPublicKeyHash()); + + static const unsigned char direct[] = { + OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG + }; + BOOST_CHECK(CScript(direct, direct+sizeof(direct)).IsPayToPublicKeyHash()); + + static const unsigned char notp2pkh1[] = { + OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_CHECKSIG + }; + BOOST_CHECK(!CScript(notp2pkh1, notp2pkh1+sizeof(notp2pkh1)).IsPayToPublicKeyHash()); + + static const unsigned char p2sh[] = { + OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL + }; + BOOST_CHECK(!CScript(p2sh, p2sh+sizeof(p2sh)).IsPayToPublicKeyHash()); + + static const unsigned char extra[] = { + OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_CHECKSIG + }; + BOOST_CHECK(!CScript(extra, extra+sizeof(extra)).IsPayToPublicKeyHash()); + + static const unsigned char missing[] = { + OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_RETURN + }; + BOOST_CHECK(!CScript(missing, missing+sizeof(missing)).IsPayToPublicKeyHash()); + + static const unsigned char missing2[] = { + OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + }; + BOOST_CHECK(!CScript(missing2, missing2+sizeof(missing)).IsPayToPublicKeyHash()); + + static const unsigned char tooshort[] = { + OP_DUP, OP_HASH160, 2, 0,0, OP_EQUALVERIFY, OP_CHECKSIG + }; + BOOST_CHECK(!CScript(tooshort, tooshort+sizeof(direct)).IsPayToPublicKeyHash()); + +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index fac63dbf564..80e293675a1 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -95,7 +95,7 @@ BOOST_DATA_TEST_CASE(sign, boost::unit_test::data::xrange(static_cast(Conse txFrom.vout[i+4].scriptPubKey = standardScripts[i]; txFrom.vout[i+4].nValue = COIN; } - BOOST_CHECK(IsStandardTx(txFrom, reason)); + BOOST_CHECK(IsStandardTx(txFrom, reason, Params())); CMutableTransaction txTo[8]; // Spending transactions for (int i = 0; i < 8; i++) @@ -198,7 +198,7 @@ BOOST_DATA_TEST_CASE(set, boost::unit_test::data::xrange(static_cast(Consen txFrom.vout[i].scriptPubKey = outer[i]; txFrom.vout[i].nValue = CENT; } - BOOST_CHECK(IsStandardTx(txFrom, reason)); + BOOST_CHECK(IsStandardTx(txFrom, reason, Params())); CMutableTransaction txTo[4]; // Spending transactions for (int i = 0; i < 4; i++) @@ -216,7 +216,7 @@ BOOST_DATA_TEST_CASE(set, boost::unit_test::data::xrange(static_cast(Consen for (int i = 0; i < 4; i++) { BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0, SIGHASH_ALL, consensusBranchId), strprintf("SignSignature %d", i)); - BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i)); + BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason, Params()), strprintf("txTo[%d].IsStandard", i)); } } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 3548b9d4bec..f23837da4aa 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -87,6 +87,7 @@ BasicTestingSetup::~BasicTestingSetup() TestingSetup::TestingSetup() { + const CChainParams& chainparams = Params(); // Ideally we'd move all the RPC tests to the functional testing framework // instead of unit tests, but for now we need these here. RegisterAllCoreRPCCommands(tableRPC); @@ -105,7 +106,7 @@ TestingSetup::TestingSetup() pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(pcoinsdbview); - InitBlockIndex(); + InitBlockIndex(chainparams); #ifdef ENABLE_WALLET bool fFirstRun; pwalletMain = new CWallet("wallet.dat"); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 88f390c1774..4219e01f4ce 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -491,7 +491,7 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa jsdesc->nullifiers[1] = GetRandHash(); BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state)); - BOOST_CHECK(!ContextualCheckTransaction(newTx, state, 0, 100)); + BOOST_CHECK(!ContextualCheckTransaction(newTx, state, Params(), 0, 100)); BOOST_CHECK(state.GetRejectReason() == "bad-txns-invalid-joinsplit-signature"); // Empty output script. @@ -505,7 +505,7 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa ) == 0); BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state)); - BOOST_CHECK(ContextualCheckTransaction(newTx, state, 0, 100)); + BOOST_CHECK(ContextualCheckTransaction(newTx, state, Params(), 0, 100)); } { // Ensure that values within the joinsplit are well-formed. @@ -748,6 +748,7 @@ BOOST_AUTO_TEST_CASE(test_big_overwinter_transaction) { BOOST_AUTO_TEST_CASE(test_IsStandard) { LOCK(cs_main); + auto chainparams = Params(); CBasicKeyStore keystore; CCoinsView coinsDummy; CCoinsViewCache coins(&coinsDummy); @@ -765,48 +766,49 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); string reason; - BOOST_CHECK(IsStandardTx(t, reason)); + BOOST_CHECK(IsStandardTx(t, reason, chainparams)); t.vout[0].nValue = 53; // dust - BOOST_CHECK(!IsStandardTx(t, reason)); + BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); t.vout[0].nValue = 2730; // not dust - BOOST_CHECK(IsStandardTx(t, reason)); + BOOST_CHECK(IsStandardTx(t, reason, chainparams)); t.vout[0].scriptPubKey = CScript() << OP_1; - BOOST_CHECK(!IsStandardTx(t, reason)); + BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); // 80-byte TX_NULL_DATA (standard) t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); - BOOST_CHECK(IsStandardTx(t, reason)); + BOOST_CHECK(IsStandardTx(t, reason, chainparams)); // 81-byte TX_NULL_DATA (non-standard) t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"); - BOOST_CHECK(!IsStandardTx(t, reason)); + BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); // TX_NULL_DATA w/o PUSHDATA t.vout.resize(1); t.vout[0].scriptPubKey = CScript() << OP_RETURN; - BOOST_CHECK(IsStandardTx(t, reason)); + BOOST_CHECK(IsStandardTx(t, reason, chainparams)); // Only one TX_NULL_DATA permitted in all cases t.vout.resize(2); t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); - BOOST_CHECK(!IsStandardTx(t, reason)); + BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); t.vout[1].scriptPubKey = CScript() << OP_RETURN; - BOOST_CHECK(!IsStandardTx(t, reason)); + BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); t.vout[0].scriptPubKey = CScript() << OP_RETURN; t.vout[1].scriptPubKey = CScript() << OP_RETURN; - BOOST_CHECK(!IsStandardTx(t, reason)); + BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); } BOOST_AUTO_TEST_CASE(test_IsStandardV2) { LOCK(cs_main); + auto chainparams = Params(); CBasicKeyStore keystore; CCoinsView coinsDummy; CCoinsViewCache coins(&coinsDummy); @@ -826,33 +828,33 @@ BOOST_AUTO_TEST_CASE(test_IsStandardV2) string reason; // A v2 transaction with no JoinSplits is still standard. t.nVersion = 2; - BOOST_CHECK(IsStandardTx(t, reason)); + BOOST_CHECK(IsStandardTx(t, reason, chainparams)); // ... and with one JoinSplit. t.vjoinsplit.push_back(JSDescription()); - BOOST_CHECK(IsStandardTx(t, reason)); + BOOST_CHECK(IsStandardTx(t, reason, chainparams)); // ... and when that JoinSplit takes from a transparent input. JSDescription *jsdesc = &t.vjoinsplit[0]; jsdesc->vpub_old = 10*CENT; t.vout[0].nValue -= 10*CENT; - BOOST_CHECK(IsStandardTx(t, reason)); + BOOST_CHECK(IsStandardTx(t, reason, chainparams)); // A v2 transaction with JoinSplits but no transparent inputs is standard. jsdesc->vpub_old = 0; jsdesc->vpub_new = 100*CENT; t.vout[0].nValue = 90*CENT; t.vin.resize(0); - BOOST_CHECK(IsStandardTx(t, reason)); + BOOST_CHECK(IsStandardTx(t, reason, chainparams)); // v2 transactions can still be non-standard for the same reasons as v1. t.vout[0].nValue = 53; // dust - BOOST_CHECK(!IsStandardTx(t, reason)); + BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); // v3 is not standard. t.nVersion = 3; t.vout[0].nValue = 90*CENT; - BOOST_CHECK(!IsStandardTx(t, reason)); + BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/timestampindex.h b/src/timestampindex.h new file mode 100644 index 00000000000..3e402872cf4 --- /dev/null +++ b/src/timestampindex.h @@ -0,0 +1,131 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TIMESTAMPINDEX_H +#define BITCOIN_TIMESTAMPINDEX_H + +#include "uint256.h" + +struct CTimestampIndexIteratorKey { + unsigned int timestamp; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 4; + } + template + void Serialize(Stream& s) const { + ser_writedata32be(s, timestamp); + } + template + void Unserialize(Stream& s) { + timestamp = ser_readdata32be(s); + } + + CTimestampIndexIteratorKey(unsigned int time) { + timestamp = time; + } + + CTimestampIndexIteratorKey() { + SetNull(); + } + + void SetNull() { + timestamp = 0; + } +}; + +struct CTimestampIndexKey { + unsigned int timestamp; + uint256 blockHash; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 36; + } + template + void Serialize(Stream& s) const { + ser_writedata32be(s, timestamp); + blockHash.Serialize(s); + } + template + void Unserialize(Stream& s) { + timestamp = ser_readdata32be(s); + blockHash.Unserialize(s); + } + + CTimestampIndexKey(unsigned int time, uint256 hash) { + timestamp = time; + blockHash = hash; + } + + CTimestampIndexKey() { + SetNull(); + } + + void SetNull() { + timestamp = 0; + blockHash.SetNull(); + } +}; + +struct CTimestampBlockIndexKey { + uint256 blockHash; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 32; + } + + template + void Serialize(Stream& s) const { + blockHash.Serialize(s); + } + + template + void Unserialize(Stream& s) { + blockHash.Unserialize(s); + } + + CTimestampBlockIndexKey(uint256 hash) { + blockHash = hash; + } + + CTimestampBlockIndexKey() { + SetNull(); + } + + void SetNull() { + blockHash.SetNull(); + } +}; + +struct CTimestampBlockIndexValue { + unsigned int ltimestamp; + size_t GetSerializeSize(int nType, int nVersion) const { + return 4; + } + + template + void Serialize(Stream& s) const { + ser_writedata32be(s, ltimestamp); + } + + template + void Unserialize(Stream& s) { + ltimestamp = ser_readdata32be(s); + } + + CTimestampBlockIndexValue (unsigned int time) { + ltimestamp = time; + } + + CTimestampBlockIndexValue() { + SetNull(); + } + + void SetNull() { + ltimestamp = 0; + } +}; + +#endif // BITCOIN_TIMESTAMPINDEX_H diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index c90ac968076..687c1a42024 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -8,6 +8,7 @@ #include "pubkey.h" #include "rpc/protocol.h" #include "script/sign.h" +#include "utilmoneystr.h" #include #include @@ -49,11 +50,32 @@ std::string TransactionBuilderResult::GetError() { TransactionBuilder::TransactionBuilder( const Consensus::Params& consensusParams, int nHeight, - CKeyStore* keystore) : consensusParams(consensusParams), nHeight(nHeight), keystore(keystore) + int nExpiryDelta, + CKeyStore* keystore, + ZCJoinSplit* sproutParams, + CCoinsViewCache* coinsView, + CCriticalSection* cs_coinsView) : + consensusParams(consensusParams), + nHeight(nHeight), + keystore(keystore), + sproutParams(sproutParams), + coinsView(coinsView), + cs_coinsView(cs_coinsView) { - mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight); + mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight, nExpiryDelta); } +// This exception is thrown in certain scenarios when building JoinSplits fails. +struct JSDescException : public std::exception +{ + JSDescException (const std::string msg_) : msg(msg_) {} + + const char* what() { return msg.c_str(); } + +private: + std::string msg; +}; + void TransactionBuilder::AddSaplingSpend( libzcash::SaplingExpandedSpendingKey expsk, libzcash::SaplingNote note, @@ -90,6 +112,39 @@ void TransactionBuilder::AddSaplingOutput( mtx.valueBalance -= value; } +void TransactionBuilder::AddSproutInput( + libzcash::SproutSpendingKey sk, + libzcash::SproutNote note, + SproutWitness witness) +{ + if (sproutParams == nullptr) { + throw std::runtime_error("Cannot add Sprout inputs to a TransactionBuilder without Sprout params"); + } + + // Consistency check: all anchors must equal the first one + if (!jsInputs.empty()) { + if (jsInputs[0].witness.root() != witness.root()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Anchor does not match previously-added Sprout spends."); + } + } + + jsInputs.emplace_back(witness, note, sk); +} + +void TransactionBuilder::AddSproutOutput( + libzcash::SproutPaymentAddress to, + CAmount value, + std::array memo) +{ + if (sproutParams == nullptr) { + throw std::runtime_error("Cannot add Sprout outputs to a TransactionBuilder without Sprout params"); + } + + libzcash::JSOutput jsOutput(to, value); + jsOutput.memo = memo; + jsOutputs.push_back(jsOutput); +} + void TransactionBuilder::AddTransparentInput(COutPoint utxo, CScript scriptPubKey, CAmount value) { if (keystore == nullptr) { @@ -118,7 +173,15 @@ void TransactionBuilder::SetFee(CAmount fee) void TransactionBuilder::SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, uint256 ovk) { - zChangeAddr = std::make_pair(ovk, changeAddr); + saplingChangeAddr = std::make_pair(ovk, changeAddr); + sproutChangeAddr = boost::none; + tChangeAddr = boost::none; +} + +void TransactionBuilder::SendChangeTo(libzcash::SproutPaymentAddress changeAddr) +{ + sproutChangeAddr = changeAddr; + saplingChangeAddr = boost::none; tChangeAddr = boost::none; } @@ -129,7 +192,8 @@ void TransactionBuilder::SendChangeTo(CTxDestination& changeAddr) } tChangeAddr = changeAddr; - zChangeAddr = boost::none; + saplingChangeAddr = boost::none; + sproutChangeAddr = boost::none; } TransactionBuilderResult TransactionBuilder::Build() @@ -140,6 +204,12 @@ TransactionBuilderResult TransactionBuilder::Build() // Valid change CAmount change = mtx.valueBalance - fee; + for (auto jsInput : jsInputs) { + change += jsInput.note.value(); + } + for (auto jsOutput : jsOutputs) { + change -= jsOutput.value; + } for (auto tIn : tIns) { change += tIn.value; } @@ -156,9 +226,13 @@ TransactionBuilderResult TransactionBuilder::Build() if (change > 0) { // Send change to the specified change address. If no change address - // was set, send change to the first Sapling address given as input. - if (zChangeAddr) { - AddSaplingOutput(zChangeAddr->first, zChangeAddr->second, change); + // was set, send change to the first Sapling address given as input + // if any; otherwise the first Sprout address given as input. + // (A t-address can only be used as the change address if explicitly set.) + if (saplingChangeAddr) { + AddSaplingOutput(saplingChangeAddr->first, saplingChangeAddr->second, change); + } else if (sproutChangeAddr) { + AddSproutOutput(sproutChangeAddr.get(), change); } else if (tChangeAddr) { // tChangeAddr has already been validated. AddTransparentOutput(tChangeAddr.value(), change); @@ -167,6 +241,9 @@ TransactionBuilderResult TransactionBuilder::Build() auto note = spends[0].note; libzcash::SaplingPaymentAddress changeAddr(note.d, note.pk_d); AddSaplingOutput(fvk.ovk, changeAddr, change); + } else if (!jsInputs.empty()) { + auto changeAddr = jsInputs[0].key.address(); + AddSproutOutput(changeAddr, change); } else { return TransactionBuilderResult("Could not determine change address"); } @@ -260,6 +337,26 @@ TransactionBuilderResult TransactionBuilder::Build() mtx.vShieldedOutput.push_back(odesc); } + // + // Sprout JoinSplits + // + + unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; + crypto_sign_keypair(mtx.joinSplitPubKey.begin(), joinSplitPrivKey); + + // Create Sprout JSDescriptions + if (!jsInputs.empty() || !jsOutputs.empty()) { + try { + CreateJSDescriptions(); + } catch (JSDescException e) { + librustzcash_sapling_proving_ctx_free(ctx); + return TransactionBuilderResult(e.what()); + } catch (std::runtime_error e) { + librustzcash_sapling_proving_ctx_free(ctx); + throw e; + } + } + // // Signatures // @@ -292,6 +389,24 @@ TransactionBuilderResult TransactionBuilder::Build() librustzcash_sapling_proving_ctx_free(ctx); + // Create Sprout joinSplitSig + if (crypto_sign_detached( + mtx.joinSplitSig.data(), NULL, + dataToBeSigned.begin(), 32, + joinSplitPrivKey) != 0) + { + return TransactionBuilderResult("Failed to create Sprout joinSplitSig"); + } + + // Sanity check Sprout joinSplitSig + if (crypto_sign_verify_detached( + mtx.joinSplitSig.data(), + dataToBeSigned.begin(), 32, + mtx.joinSplitPubKey.begin()) != 0) + { + return TransactionBuilderResult("Sprout joinSplitSig sanity check failed"); + } + // Transparent signatures CTransaction txNewConst(mtx); for (int nIn = 0; nIn < mtx.vin.size(); nIn++) { @@ -311,3 +426,289 @@ TransactionBuilderResult TransactionBuilder::Build() return TransactionBuilderResult(CTransaction(mtx)); } + +void TransactionBuilder::CreateJSDescriptions() +{ + // Copy jsInputs and jsOutputs to more flexible containers + std::deque jsInputsDeque; + for (auto jsInput : jsInputs) { + jsInputsDeque.push_back(jsInput); + } + std::deque jsOutputsDeque; + for (auto jsOutput : jsOutputs) { + jsOutputsDeque.push_back(jsOutput); + } + + // If we have no Sprout shielded inputs, then we do the simpler more-leaky + // process where we just create outputs directly. We save the chaining logic, + // at the expense of leaking the sums of pairs of output values in vpub_old. + if (jsInputs.empty()) { + // Create joinsplits, where each output represents a zaddr recipient. + while (jsOutputsDeque.size() > 0) { + // Default array entries are dummy inputs and outputs + std::array vjsin; + std::array vjsout; + uint64_t vpub_old = 0; + + for (int n = 0; n < ZC_NUM_JS_OUTPUTS && jsOutputsDeque.size() > 0; n++) { + vjsout[n] = jsOutputsDeque.front(); + jsOutputsDeque.pop_front(); + + // Funds are removed from the value pool and enter the private pool + vpub_old += vjsout[n].value; + } + + std::array inputMap; + std::array outputMap; + CreateJSDescription(vpub_old, 0, vjsin, vjsout, inputMap, outputMap); + } + return; + } + + // At this point, we are guaranteed to have at least one input note. + // Use address of first input note as the temporary change address. + auto changeKey = jsInputsDeque.front().key; + auto changeAddress = changeKey.address(); + + CAmount jsChange = 0; // this is updated after each joinsplit + int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 + bool vpubOldProcessed = false; // updated when vpub_old for taddr inputs is set in first joinsplit + bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit + + CAmount valueOut = 0; + for (auto jsInput : jsInputs) { + valueOut += jsInput.note.value(); + } + for (auto jsOutput : jsOutputs) { + valueOut -= jsOutput.value; + } + CAmount vpubOldTarget = valueOut < 0 ? -valueOut : 0; + CAmount vpubNewTarget = valueOut > 0 ? valueOut : 0; + + // Keep track of treestate within this transaction + boost::unordered_map intermediates; + std::vector previousCommitments; + + while (!vpubNewProcessed) { + // Default array entries are dummy inputs and outputs + std::array vjsin; + std::array vjsout; + uint64_t vpub_old = 0; + uint64_t vpub_new = 0; + + // Set vpub_old in the first joinsplit + if (!vpubOldProcessed) { + vpub_old += vpubOldTarget; // funds flowing from public pool + vpubOldProcessed = true; + } + + CAmount jsInputValue = 0; + uint256 jsAnchor; + + JSDescription prevJoinSplit; + + // Keep track of previous JoinSplit and its commitments + if (mtx.vjoinsplit.size() > 0) { + prevJoinSplit = mtx.vjoinsplit.back(); + } + + // If there is no change, the chain has terminated so we can reset the tracked treestate. + if (jsChange == 0 && mtx.vjoinsplit.size() > 0) { + intermediates.clear(); + previousCommitments.clear(); + } + + // + // Consume change as the first input of the JoinSplit. + // + if (jsChange > 0) { + // Update tree state with previous joinsplit + SproutMerkleTree tree; + { + // assert that coinsView is not null + assert(coinsView); + // We do not check cs_coinView because we do not set this in testing + // assert(cs_coinsView); + LOCK(cs_coinsView); + auto it = intermediates.find(prevJoinSplit.anchor); + if (it != intermediates.end()) { + tree = it->second; + } else if (!coinsView->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { + throw JSDescException("Could not find previous JoinSplit anchor"); + } + } + + assert(changeOutputIndex != -1); + assert(changeOutputIndex < prevJoinSplit.commitments.size()); + boost::optional changeWitness; + int n = 0; + for (const uint256& commitment : prevJoinSplit.commitments) { + tree.append(commitment); + previousCommitments.push_back(commitment); + if (!changeWitness && changeOutputIndex == n++) { + changeWitness = tree.witness(); + } else if (changeWitness) { + changeWitness.get().append(commitment); + } + } + assert(changeWitness.has_value()); + jsAnchor = tree.root(); + intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) + + // Decrypt the change note's ciphertext to retrieve some data we need + ZCNoteDecryption decryptor(changeKey.receiving_key()); + auto hSig = prevJoinSplit.h_sig(*sproutParams, mtx.joinSplitPubKey); + try { + auto plaintext = libzcash::SproutNotePlaintext::decrypt( + decryptor, + prevJoinSplit.ciphertexts[changeOutputIndex], + prevJoinSplit.ephemeralKey, + hSig, + (unsigned char)changeOutputIndex); + + auto note = plaintext.note(changeAddress); + vjsin[0] = libzcash::JSInput(changeWitness.get(), note, changeKey); + + jsInputValue += plaintext.value(); + + LogPrint("zrpcunsafe", "spending change (amount=%s)\n", FormatMoney(plaintext.value())); + + } catch (const std::exception& e) { + throw JSDescException("Error decrypting output note of previous JoinSplit"); + } + } + + // + // Consume spendable non-change notes + // + for (int n = (jsChange > 0) ? 1 : 0; n < ZC_NUM_JS_INPUTS && jsInputsDeque.size() > 0; n++) { + auto jsInput = jsInputsDeque.front(); + jsInputsDeque.pop_front(); + + // Add history of previous commitments to witness + if (jsChange > 0) { + for (const uint256& commitment : previousCommitments) { + jsInput.witness.append(commitment); + } + if (jsAnchor != jsInput.witness.root()) { + throw JSDescException("Witness for spendable note does not have same anchor as change input"); + } + } + + // The jsAnchor is null if this JoinSplit is at the start of a new chain + if (jsAnchor.IsNull()) { + jsAnchor = jsInput.witness.root(); + } + + jsInputValue += jsInput.note.value(); + vjsin[n] = jsInput; + } + + // Find recipient to transfer funds to + libzcash::JSOutput recipient; + if (jsOutputsDeque.size() > 0) { + recipient = jsOutputsDeque.front(); + jsOutputsDeque.pop_front(); + } + // `recipient` is now either a valid recipient, or a dummy output with value = 0 + + // Reset change + jsChange = 0; + CAmount outAmount = recipient.value; + + // Set vpub_new in the last joinsplit (when there are no more notes to spend or zaddr outputs to satisfy) + if (jsOutputsDeque.empty() && jsInputsDeque.empty()) { + assert(!vpubNewProcessed); + if (jsInputValue < vpubNewTarget) { + throw JSDescException(strprintf("Insufficient funds for vpub_new %s", FormatMoney(vpubNewTarget))); + } + outAmount += vpubNewTarget; + vpub_new += vpubNewTarget; // funds flowing back to public pool + vpubNewProcessed = true; + jsChange = jsInputValue - outAmount; + assert(jsChange >= 0); + } else { + // This is not the last joinsplit, so compute change and any amount still due to the recipient + if (jsInputValue > outAmount) { + jsChange = jsInputValue - outAmount; + } else if (outAmount > jsInputValue) { + // Any amount due is owed to the recipient. Let the miners fee get paid first. + CAmount due = outAmount - jsInputValue; + libzcash::JSOutput recipientDue(recipient.addr, due); + recipientDue.memo = recipient.memo; + jsOutputsDeque.push_front(recipientDue); + + // reduce the amount being sent right now to the value of all inputs + recipient.value = jsInputValue; + } + } + + // create output for recipient + assert(ZC_NUM_JS_OUTPUTS == 2); // If this changes, the logic here will need to be adjusted + vjsout[0] = recipient; + + // create output for any change + if (jsChange > 0) { + vjsout[1] = libzcash::JSOutput(changeAddress, jsChange); + + LogPrint("zrpcunsafe", "generating note for change (amount=%s)\n", FormatMoney(jsChange)); + } + + std::array inputMap; + std::array outputMap; + CreateJSDescription(vpub_old, vpub_new, vjsin, vjsout, inputMap, outputMap); + + if (jsChange > 0) { + changeOutputIndex = -1; + for (size_t i = 0; i < outputMap.size(); i++) { + if (outputMap[i] == 1) { + changeOutputIndex = i; + } + } + assert(changeOutputIndex != -1); + } + } +} + +void TransactionBuilder::CreateJSDescription( + uint64_t vpub_old, + uint64_t vpub_new, + std::array vjsin, + std::array vjsout, + std::array& inputMap, + std::array& outputMap) +{ + LogPrint("zrpcunsafe", "CreateJSDescription: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", + mtx.vjoinsplit.size(), + FormatMoney(vpub_old), FormatMoney(vpub_new), + FormatMoney(vjsin[0].note.value()), FormatMoney(vjsin[1].note.value()), + FormatMoney(vjsout[0].value), FormatMoney(vjsout[1].value)); + + uint256 esk; // payment disclosure - secret + + // Generate the proof, this can take over a minute. + JSDescription jsdesc = JSDescription::Randomized( + mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION), + *sproutParams, + mtx.joinSplitPubKey, + vjsin[0].witness.root(), + vjsin, + vjsout, + inputMap, + outputMap, + vpub_old, + vpub_new, + true, //!this->testmode, + &esk); // parameter expects pointer to esk, so pass in address + + { + auto verifier = libzcash::ProofVerifier::Strict(); + if (!jsdesc.Verify(*sproutParams, verifier, mtx.joinSplitPubKey)) { + throw std::runtime_error("error verifying joinsplit"); + } + } + + mtx.vjoinsplit.push_back(jsdesc); + + // TODO: Sprout payment disclosure +} diff --git a/src/transaction_builder.h b/src/transaction_builder.h index 592f4f64db2..46f38481ecc 100644 --- a/src/transaction_builder.h +++ b/src/transaction_builder.h @@ -5,6 +5,7 @@ #ifndef TRANSACTION_BUILDER_H #define TRANSACTION_BUILDER_H +#include "coins.h" #include "consensus/params.h" #include "keystore.h" #include "primitives/transaction.h" @@ -13,6 +14,7 @@ #include "uint256.h" #include "zcash/Address.hpp" #include "zcash/IncrementalMerkleTree.hpp" +#include "zcash/JoinSplit.hpp" #include "zcash/Note.hpp" #include "zcash/NoteEncryption.hpp" @@ -72,19 +74,32 @@ class TransactionBuilder Consensus::Params consensusParams; int nHeight; const CKeyStore* keystore; + ZCJoinSplit* sproutParams; + const CCoinsViewCache* coinsView; + CCriticalSection* cs_coinsView; CMutableTransaction mtx; CAmount fee = 10000; std::vector spends; std::vector outputs; + std::vector jsInputs; + std::vector jsOutputs; std::vector tIns; - boost::optional> zChangeAddr; + boost::optional> saplingChangeAddr; + boost::optional sproutChangeAddr; boost::optional tChangeAddr; public: TransactionBuilder() {} - TransactionBuilder(const Consensus::Params& consensusParams, int nHeight, CKeyStore* keyStore = nullptr); + TransactionBuilder( + const Consensus::Params& consensusParams, + int nHeight, + int nExpiryDelta, + CKeyStore* keyStore = nullptr, + ZCJoinSplit* sproutParams = nullptr, + CCoinsViewCache* coinsView = nullptr, + CCriticalSection* cs_coinsView = nullptr); void SetFee(CAmount fee); @@ -102,6 +117,18 @@ class TransactionBuilder CAmount value, std::array memo = {{0xF6}}); + // Throws if the anchor does not match the anchor used by + // previously-added Sprout inputs. + void AddSproutInput( + libzcash::SproutSpendingKey sk, + libzcash::SproutNote note, + SproutWitness witness); + + void AddSproutOutput( + libzcash::SproutPaymentAddress to, + CAmount value, + std::array memo = {{0xF6}}); + // Assumes that the value correctly corresponds to the provided UTXO. void AddTransparentInput(COutPoint utxo, CScript scriptPubKey, CAmount value); @@ -109,9 +136,22 @@ class TransactionBuilder void SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, uint256 ovk); + void SendChangeTo(libzcash::SproutPaymentAddress); + void SendChangeTo(CTxDestination& changeAddr); TransactionBuilderResult Build(); + +private: + void CreateJSDescriptions(); + + void CreateJSDescription( + uint64_t vpub_old, + uint64_t vpub_new, + std::array vjsin, + std::array vjsout, + std::array& inputMap, + std::array& outputMap); }; #endif /* TRANSACTION_BUILDER_H */ diff --git a/src/txdb.cpp b/src/txdb.cpp index 8749ffcc6b1..2d421d36934 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -35,6 +35,12 @@ static const char DB_FLAG = 'F'; static const char DB_REINDEX_FLAG = 'R'; static const char DB_LAST_BLOCK = 'l'; +// insightexplorer +static const char DB_ADDRESSINDEX = 'd'; +static const char DB_ADDRESSUNSPENTINDEX = 'u'; +static const char DB_SPENTINDEX = 'p'; +static const char DB_TIMESTAMPINDEX = 'T'; +static const char DB_BLOCKHASHINDEX = 'h'; CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) { } @@ -293,6 +299,151 @@ bool CBlockTreeDB::WriteTxIndex(const std::vector return WriteBatch(batch); } +// START insightexplorer +// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-81e4f16a1b5d5b7ca25351a63d07cb80R183 +bool CBlockTreeDB::UpdateAddressUnspentIndex(const std::vector &vect) +{ + CDBBatch batch(*this); + for (std::vector::const_iterator it=vect.begin(); it!=vect.end(); it++) { + if (it->second.IsNull()) { + batch.Erase(make_pair(DB_ADDRESSUNSPENTINDEX, it->first)); + } else { + batch.Write(make_pair(DB_ADDRESSUNSPENTINDEX, it->first), it->second); + } + } + return WriteBatch(batch); +} + +bool CBlockTreeDB::ReadAddressUnspentIndex(uint160 addressHash, int type, std::vector &unspentOutputs) +{ + boost::scoped_ptr pcursor(NewIterator()); + + pcursor->Seek(make_pair(DB_ADDRESSUNSPENTINDEX, CAddressIndexIteratorKey(type, addressHash))); + + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + std::pair key; + if (!(pcursor->GetKey(key) && key.first == DB_ADDRESSUNSPENTINDEX && key.second.hashBytes == addressHash)) + break; + CAddressUnspentValue nValue; + if (pcursor->GetValue(nValue)) + return error("failed to get address unspent value"); + unspentOutputs.push_back(make_pair(key.second, nValue)); + pcursor->Next(); + } + return true; +} + +bool CBlockTreeDB::WriteAddressIndex(const std::vector &vect) { + CDBBatch batch(*this); + for (std::vector::const_iterator it=vect.begin(); it!=vect.end(); it++) + batch.Write(make_pair(DB_ADDRESSINDEX, it->first), it->second); + return WriteBatch(batch); +} + +bool CBlockTreeDB::EraseAddressIndex(const std::vector &vect) { + CDBBatch batch(*this); + for (std::vector::const_iterator it=vect.begin(); it!=vect.end(); it++) + batch.Erase(make_pair(DB_ADDRESSINDEX, it->first)); + return WriteBatch(batch); +} + +bool CBlockTreeDB::ReadAddressIndex( + uint160 addressHash, int type, + std::vector &addressIndex, + int start, int end) +{ + boost::scoped_ptr pcursor(NewIterator()); + + if (start > 0 && end > 0) { + pcursor->Seek(make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorHeightKey(type, addressHash, start))); + } else { + pcursor->Seek(make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorKey(type, addressHash))); + } + + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + std::pair key; + if (!(pcursor->GetKey(key) && key.first == DB_ADDRESSINDEX && key.second.hashBytes == addressHash)) + break; + if (end > 0 && key.second.blockHeight > end) + break; + CAmount nValue; + if (!pcursor->GetValue(nValue)) + return error("failed to get address index value"); + addressIndex.push_back(make_pair(key.second, nValue)); + pcursor->Next(); + } + return true; +} + +bool CBlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) { + return Read(make_pair(DB_SPENTINDEX, key), value); +} + +bool CBlockTreeDB::UpdateSpentIndex(const std::vector &vect) { + CDBBatch batch(*this); + for (std::vector::const_iterator it=vect.begin(); it!=vect.end(); it++) { + if (it->second.IsNull()) { + batch.Erase(make_pair(DB_SPENTINDEX, it->first)); + } else { + batch.Write(make_pair(DB_SPENTINDEX, it->first), it->second); + } + } + return WriteBatch(batch); +} + +bool CBlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey ×tampIndex) { + CDBBatch batch(*this); + batch.Write(make_pair(DB_TIMESTAMPINDEX, timestampIndex), 0); + return WriteBatch(batch); +} + +bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, + const bool fActiveOnly, std::vector > &hashes) +{ + boost::scoped_ptr pcursor(NewIterator()); + + pcursor->Seek(make_pair(DB_TIMESTAMPINDEX, CTimestampIndexIteratorKey(low))); + + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + std::pair key; + if (!(pcursor->GetKey(key) && key.first == DB_TIMESTAMPINDEX && key.second.timestamp < high)) { + break; + } + if (fActiveOnly) { + CBlockIndex* pblockindex = mapBlockIndex[key.second.blockHash]; + if (chainActive.Contains(pblockindex)) { + hashes.push_back(std::make_pair(key.second.blockHash, key.second.timestamp)); + } + } else { + hashes.push_back(std::make_pair(key.second.blockHash, key.second.timestamp)); + } + pcursor->Next(); + } + return true; +} + +bool CBlockTreeDB::WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, + const CTimestampBlockIndexValue &logicalts) +{ + CDBBatch batch(*this); + batch.Write(make_pair(DB_BLOCKHASHINDEX, blockhashIndex), logicalts); + return WriteBatch(batch); +} + +bool CBlockTreeDB::ReadTimestampBlockIndex(const uint256 &hash, unsigned int <imestamp) +{ + CTimestampBlockIndexValue(lts); + if (!Read(std::make_pair(DB_BLOCKHASHINDEX, hash), lts)) + return false; + + ltimestamp = lts.ltimestamp; + return true; +} +// END insightexplorer + bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); } @@ -305,7 +456,7 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { return true; } -bool CBlockTreeDB::LoadBlockIndexGuts() +bool CBlockTreeDB::LoadBlockIndexGuts(boost::function insertBlockIndex) { boost::scoped_ptr pcursor(NewIterator()); @@ -319,8 +470,8 @@ bool CBlockTreeDB::LoadBlockIndexGuts() CDiskBlockIndex diskindex; if (pcursor->GetValue(diskindex)) { // Construct block index object - CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); - pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); + CBlockIndex* pindexNew = insertBlockIndex(diskindex.GetBlockHash()); + pindexNew->pprev = insertBlockIndex(diskindex.hashPrev); pindexNew->nHeight = diskindex.nHeight; pindexNew->nFile = diskindex.nFile; pindexNew->nDataPos = diskindex.nDataPos; diff --git a/src/txdb.h b/src/txdb.h index a415ad2bbfd..e3ef5b57c33 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -8,15 +8,35 @@ #include "coins.h" #include "dbwrapper.h" +#include "chain.h" #include #include #include #include -class CBlockFileInfo; +#include + class CBlockIndex; -struct CDiskTxPos; + +// START insightexplorer +struct CAddressUnspentKey; +struct CAddressUnspentValue; +struct CAddressIndexKey; +struct CAddressIndexIteratorKey; +struct CAddressIndexIteratorHeightKey; +struct CSpentIndexKey; +struct CSpentIndexValue; +struct CTimestampIndexKey; +struct CTimestampIndexIteratorKey; +struct CTimestampBlockIndexKey; +struct CTimestampBlockIndexValue; + +typedef std::pair CAddressUnspentDbEntry; +typedef std::pair CAddressIndexDbEntry; +typedef std::pair CSpentIndexDbEntry; +// END insightexplorer + class uint256; //! -dbcache default (MiB) @@ -26,6 +46,31 @@ static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024; //! min. -dbcache in (MiB) static const int64_t nMinDbCache = 4; +struct CDiskTxPos : public CDiskBlockPos +{ + unsigned int nTxOffset; // after header + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(*(CDiskBlockPos*)this); + READWRITE(VARINT(nTxOffset)); + } + + CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) { + } + + CDiskTxPos() { + SetNull(); + } + + void SetNull() { + CDiskBlockPos::SetNull(); + nTxOffset = 0; + } +}; + /** CCoinsView backed by the coin database (chainstate/) */ class CCoinsViewDB : public CCoinsView { @@ -70,9 +115,26 @@ class CBlockTreeDB : public CDBWrapper bool ReadReindexing(bool &fReindex); bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); bool WriteTxIndex(const std::vector > &list); + + // START insightexplorer + bool UpdateAddressUnspentIndex(const std::vector &vect); + bool ReadAddressUnspentIndex(uint160 addressHash, int type, std::vector &vect); + bool WriteAddressIndex(const std::vector &vect); + bool EraseAddressIndex(const std::vector &vect); + bool ReadAddressIndex(uint160 addressHash, int type, std::vector &addressIndex, int start = 0, int end = 0); + bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); + bool UpdateSpentIndex(const std::vector &vect); + bool WriteTimestampIndex(const CTimestampIndexKey ×tampIndex); + bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, + const bool fActiveOnly, std::vector > &vect); + bool WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, + const CTimestampBlockIndexValue &logicalts); + bool ReadTimestampBlockIndex(const uint256 &hash, unsigned int &logicalTS); + // END insightexplorer + bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); - bool LoadBlockIndexGuts(); + bool LoadBlockIndexGuts(boost::function insertBlockIndex); }; #endif // BITCOIN_TXDB_H diff --git a/src/txmempool.h b/src/txmempool.h index ec8a8518aa4..ca8055c90e4 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -41,17 +41,17 @@ class CTxMemPoolEntry { private: CTransaction tx; - CAmount nFee; //! Cached to avoid expensive parent-transaction lookups - size_t nTxSize; //! ... and avoid recomputing tx size - size_t nModSize; //! ... and modified size for priority - size_t nUsageSize; //! ... and total memory usage - CFeeRate feeRate; //! ... and fee per kB - int64_t nTime; //! Local time when entering the mempool - double dPriority; //! Priority when entering the mempool - unsigned int nHeight; //! Chain height when entering the mempool - bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool - bool spendsCoinbase; //! keep track of transactions that spend a coinbase - uint32_t nBranchId; //! Branch ID this transaction is known to commit to, cached for efficiency + CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups + size_t nTxSize; //!< ... and avoid recomputing tx size + size_t nModSize; //!< ... and modified size for priority + size_t nUsageSize; //!< ... and total memory usage + CFeeRate feeRate; //!< ... and fee per kB + int64_t nTime; //!< Local time when entering the mempool + double dPriority; //!< Priority when entering the mempool + unsigned int nHeight; //!< Chain height when entering the mempool + bool hadNoDependencies; //!< Not dependent on any other txs when it entered the mempool + bool spendsCoinbase; //!< keep track of transactions that spend a coinbase + uint32_t nBranchId; //!< Branch ID this transaction is known to commit to, cached for efficiency public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, @@ -124,12 +124,12 @@ class CInPoint class CTxMemPool { private: - uint32_t nCheckFrequency; //! Value n means that n times in 2^32 we check. + uint32_t nCheckFrequency; //!< Value n means that n times in 2^32 we check. unsigned int nTransactionsUpdated; CBlockPolicyEstimator* minerPolicyEstimator; - uint64_t totalTxSize = 0; //! sum of all mempool tx' byte sizes - uint64_t cachedInnerUsage; //! sum of dynamic memory usage of all the map elements (NOT the maps themselves) + uint64_t totalTxSize = 0; //!< sum of all mempool tx' byte sizes + uint64_t cachedInnerUsage; //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves) std::map mapSproutNullifiers; std::map mapSaplingNullifiers; diff --git a/src/utiltest.cpp b/src/utiltest.cpp index 8eb23249f27..e62c5611be3 100644 --- a/src/utiltest.cpp +++ b/src/utiltest.cpp @@ -232,7 +232,7 @@ CWalletTx GetValidSaplingReceive(const Consensus::Params& consensusParams, auto fvk = sk.expsk.full_viewing_key(); auto pa = sk.DefaultAddress(); - auto builder = TransactionBuilder(consensusParams, 1, &keyStore); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta, &keyStore); builder.SetFee(0); builder.AddTransparentInput(COutPoint(), scriptPubKey, value); builder.AddSaplingOutput(fvk.ovk, pa, value, {}); diff --git a/src/utiltime.cpp b/src/utiltime.cpp index f1a408a31d4..5d0b284fdf8 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -15,7 +15,7 @@ using namespace std; -static int64_t nMockTime = 0; //! For unit testing +static int64_t nMockTime = 0; //!< For unit testing int64_t GetTime() { diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index a0b414614b3..0e5dd34096e 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -348,12 +348,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() // generate a common one from the HD seed. This ensures the data is // recoverable, while keeping it logically separate from the ZIP 32 // Sapling key hierarchy, which the user might not be using. - HDSeed seed; - if (!pwalletMain->GetHDSeed(seed)) { - throw JSONRPCError( - RPC_WALLET_ERROR, - "AsyncRPCOperation_sendmany: HD seed not found"); - } + HDSeed seed = pwalletMain->GetHDSeedForRPC(); ovk = ovkForShieldingFromTaddr(seed); } if (!ovk) { @@ -650,7 +645,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; wtxDepth = wtx.GetDepthInMainChain(); } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", + LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, jsoutindex=%d, amount=%s, height=%d, confirmations=%d)\n", getId(), jso.hash.ToString().substr(0, 10), jso.js, diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp new file mode 100644 index 00000000000..d99bc539fe0 --- /dev/null +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -0,0 +1,233 @@ +#include "assert.h" +#include "boost/variant/static_visitor.hpp" +#include "asyncrpcoperation_saplingmigration.h" +#include "init.h" +#include "key_io.h" +#include "rpc/protocol.h" +#include "random.h" +#include "sync.h" +#include "tinyformat.h" +#include "transaction_builder.h" +#include "util.h" +#include "utilmoneystr.h" +#include "wallet.h" + +const CAmount FEE = 10000; +const int MIGRATION_EXPIRY_DELTA = 450; + +AsyncRPCOperation_saplingmigration::AsyncRPCOperation_saplingmigration(int targetHeight) : targetHeight_(targetHeight) {} + +AsyncRPCOperation_saplingmigration::~AsyncRPCOperation_saplingmigration() {} + +void AsyncRPCOperation_saplingmigration::main() { + if (isCancelled()) + return; + + set_state(OperationStatus::EXECUTING); + start_execution_clock(); + + bool success = false; + + try { + success = main_impl(); + } catch (const UniValue& objError) { + int code = find_value(objError, "code").get_int(); + std::string message = find_value(objError, "message").get_str(); + set_error_code(code); + set_error_message(message); + } catch (const runtime_error& e) { + set_error_code(-1); + set_error_message("runtime error: " + string(e.what())); + } catch (const logic_error& e) { + set_error_code(-1); + set_error_message("logic error: " + string(e.what())); + } catch (const exception& e) { + set_error_code(-1); + set_error_message("general exception: " + string(e.what())); + } catch (...) { + set_error_code(-2); + set_error_message("unknown error"); + } + + stop_execution_clock(); + + if (success) { + set_state(OperationStatus::SUCCESS); + } else { + set_state(OperationStatus::FAILED); + } + + std::string s = strprintf("%s: Sprout->Sapling transactions created. (status=%s", getId(), getStateAsString()); + if (success) { + s += strprintf(", success)\n"); + } else { + s += strprintf(", error=%s)\n", getErrorMessage()); + } + + LogPrintf("%s", s); +} + +bool AsyncRPCOperation_saplingmigration::main_impl() { + LogPrint("zrpcunsafe", "%s: Beginning AsyncRPCOperation_saplingmigration.\n", getId()); + std::vector sproutEntries; + std::vector saplingEntries; + { + LOCK2(cs_main, pwalletMain->cs_wallet); + // We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying + // an anchor at height N-10 for each Sprout JoinSplit description + // Consider, should notes be sorted? + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, "", 11); + } + CAmount availableFunds = 0; + for (const SproutNoteEntry& sproutEntry : sproutEntries) { + availableFunds += sproutEntry.note.value(); + } + // If the remaining amount to be migrated is less than 0.01 ZEC, end the migration. + if (availableFunds < CENT) { + LogPrint("zrpcunsafe", "%s: Available Sprout balance (%s) less than required minimum (%s). Stopping.\n", + getId(), FormatMoney(availableFunds), FormatMoney(CENT)); + setMigrationResult(0, 0, std::vector()); + return true; + } + + HDSeed seed = pwalletMain->GetHDSeedForRPC(); + libzcash::SaplingPaymentAddress migrationDestAddress = getMigrationDestAddress(seed); + + auto consensusParams = Params().GetConsensus(); + + // Up to the limit of 5, as many transactions are sent as are needed to migrate the remaining funds + int numTxCreated = 0; + CAmount amountMigrated = 0; + std::vector migrationTxIds; + int noteIndex = 0; + CCoinsViewCache coinsView(pcoinsTip); + do { + CAmount amountToSend = chooseAmount(availableFunds); + auto builder = TransactionBuilder(consensusParams, targetHeight_, MIGRATION_EXPIRY_DELTA, pwalletMain, pzcashParams, + &coinsView, &cs_main); + LogPrint("zrpcunsafe", "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - FEE)); + std::vector fromNotes; + CAmount fromNoteAmount = 0; + while (fromNoteAmount < amountToSend) { + auto sproutEntry = sproutEntries[noteIndex++]; + fromNotes.push_back(sproutEntry); + fromNoteAmount += sproutEntry.note.value(); + } + availableFunds -= fromNoteAmount; + for (const SproutNoteEntry& sproutEntry : fromNotes) { + std::string data(sproutEntry.memo.begin(), sproutEntry.memo.end()); + LogPrint("zrpcunsafe", "%s: Adding Sprout note input (txid=%s, vjoinsplit=%d, jsoutindex=%d, amount=%s, memo=%s)\n", + getId(), + sproutEntry.jsop.hash.ToString().substr(0, 10), + sproutEntry.jsop.js, + int(sproutEntry.jsop.n), // uint8_t + FormatMoney(sproutEntry.note.value()), + HexStr(data).substr(0, 10) + ); + libzcash::SproutSpendingKey sproutSk; + pwalletMain->GetSproutSpendingKey(sproutEntry.address, sproutSk); + std::vector vOutPoints = {sproutEntry.jsop}; + // Each migration transaction SHOULD specify an anchor at height N-10 + // for each Sprout JoinSplit description + // TODO: the above functionality (in comment) is not implemented in zcashd + uint256 inputAnchor; + std::vector> vInputWitnesses; + pwalletMain->GetSproutNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor); + builder.AddSproutInput(sproutSk, sproutEntry.note, vInputWitnesses[0].get()); + } + // The amount chosen *includes* the 0.0001 ZEC fee for this transaction, i.e. + // the value of the Sapling output will be 0.0001 ZEC less. + builder.SetFee(FEE); + builder.AddSaplingOutput(ovkForShieldingFromTaddr(seed), migrationDestAddress, amountToSend - FEE); + CTransaction tx = builder.Build().GetTxOrThrow(); + if (isCancelled()) { + LogPrint("zrpcunsafe", "%s: Canceled. Stopping.\n", getId()); + break; + } + pwalletMain->AddPendingSaplingMigrationTx(tx); + LogPrint("zrpcunsafe", "%s: Added pending migration transaction with txid=%s\n", getId(), tx.GetHash().ToString()); + ++numTxCreated; + amountMigrated += amountToSend - FEE; + migrationTxIds.push_back(tx.GetHash().ToString()); + } while (numTxCreated < 5 && availableFunds > CENT); + + LogPrint("zrpcunsafe", "%s: Created %d transactions with total Sapling output amount=%s\n", getId(), numTxCreated, FormatMoney(amountMigrated)); + setMigrationResult(numTxCreated, amountMigrated, migrationTxIds); + return true; +} + +void AsyncRPCOperation_saplingmigration::setMigrationResult(int numTxCreated, const CAmount& amountMigrated, const std::vector& migrationTxIds) { + UniValue res(UniValue::VOBJ); + res.push_back(Pair("num_tx_created", numTxCreated)); + res.push_back(Pair("amount_migrated", FormatMoney(amountMigrated))); + UniValue txIds(UniValue::VARR); + for (const std::string& txId : migrationTxIds) { + txIds.push_back(txId); + } + res.push_back(Pair("migration_txids", txIds)); + set_result(res); +} + +CAmount AsyncRPCOperation_saplingmigration::chooseAmount(const CAmount& availableFunds) { + CAmount amount = 0; + do { + // 1. Choose an integer exponent uniformly in the range 6 to 8 inclusive. + int exponent = GetRand(3) + 6; + // 2. Choose an integer mantissa uniformly in the range 1 to 99 inclusive. + uint64_t mantissa = GetRand(99) + 1; + // 3. Calculate amount := (mantissa * 10^exponent) zatoshi. + int pow = std::pow(10, exponent); + amount = mantissa * pow; + // 4. If amount is greater than the amount remaining to send, repeat from step 1. + } while (amount > availableFunds); + return amount; +} + +// Unless otherwise specified, the migration destination address is the address for Sapling account 0 +libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) { + if (mapArgs.count("-migrationdestaddress")) { + std::string migrationDestAddress = mapArgs["-migrationdestaddress"]; + auto address = DecodePaymentAddress(migrationDestAddress); + auto saplingAddress = boost::get(&address); + assert(saplingAddress != nullptr); // This is checked in init.cpp + return *saplingAddress; + } + // Derive the address for Sapling account 0 + auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); + uint32_t bip44CoinType = Params().BIP44CoinType(); + + // We use a fixed keypath scheme of m/32'/coin_type'/account' + // Derive m/32' + auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); + // Derive m/32'/coin_type' + auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); + + // Derive m/32'/coin_type'/0' + libzcash::SaplingExtendedSpendingKey xsk = m_32h_cth.Derive(0 | ZIP32_HARDENED_KEY_LIMIT); + + libzcash::SaplingPaymentAddress toAddress = xsk.DefaultAddress(); + + // Refactor: this is similar logic as in the visitor HaveSpendingKeyForPaymentAddress and is used elsewhere + libzcash::SaplingIncomingViewingKey ivk; + libzcash::SaplingFullViewingKey fvk; + if (!(pwalletMain->GetSaplingIncomingViewingKey(toAddress, ivk) && + pwalletMain->GetSaplingFullViewingKey(ivk, fvk) && + pwalletMain->HaveSaplingSpendingKey(fvk))) { + // Sapling account 0 must be the first address returned by GenerateNewSaplingZKey + assert(pwalletMain->GenerateNewSaplingZKey() == toAddress); + } + + return toAddress; +} + +void AsyncRPCOperation_saplingmigration::cancel() { + set_state(OperationStatus::CANCELLED); +} + +UniValue AsyncRPCOperation_saplingmigration::getStatus() const { + UniValue v = AsyncRPCOperation::getStatus(); + UniValue obj = v.get_obj(); + obj.push_back(Pair("method", "saplingmigration")); + obj.push_back(Pair("target_height", targetHeight_)); + return obj; +} diff --git a/src/wallet/asyncrpcoperation_saplingmigration.h b/src/wallet/asyncrpcoperation_saplingmigration.h new file mode 100644 index 00000000000..6e0c452f932 --- /dev/null +++ b/src/wallet/asyncrpcoperation_saplingmigration.h @@ -0,0 +1,35 @@ +#include "amount.h" +#include "asyncrpcoperation.h" +#include "univalue.h" +#include "zcash/Address.hpp" +#include "zcash/zip32.h" + +class AsyncRPCOperation_saplingmigration : public AsyncRPCOperation +{ +public: + AsyncRPCOperation_saplingmigration(int targetHeight); + virtual ~AsyncRPCOperation_saplingmigration(); + + // We don't want to be copied or moved around + AsyncRPCOperation_saplingmigration(AsyncRPCOperation_saplingmigration const&) = delete; // Copy construct + AsyncRPCOperation_saplingmigration(AsyncRPCOperation_saplingmigration&&) = delete; // Move construct + AsyncRPCOperation_saplingmigration& operator=(AsyncRPCOperation_saplingmigration const&) = delete; // Copy assign + AsyncRPCOperation_saplingmigration& operator=(AsyncRPCOperation_saplingmigration&&) = delete; // Move assign + + static libzcash::SaplingPaymentAddress getMigrationDestAddress(const HDSeed& seed); + + virtual void main(); + + virtual void cancel(); + + virtual UniValue getStatus() const; + +private: + int targetHeight_; + + bool main_impl(); + + void setMigrationResult(int numTxCreated, const CAmount& amountMigrated, const std::vector& migrationTxIds); + + CAmount chooseAmount(const CAmount& availableFunds); +}; diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index e33440a4d0e..9538a2736e6 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -381,12 +381,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { // generate a common one from the HD seed. This ensures the data is // recoverable, while keeping it logically separate from the ZIP 32 // Sapling key hierarchy, which the user might not be using. - HDSeed seed; - if (!pwalletMain->GetHDSeed(seed)) { - throw JSONRPCError( - RPC_WALLET_ERROR, - "AsyncRPCOperation_sendmany::main_impl(): HD seed not found"); - } + HDSeed seed = pwalletMain->GetHDSeedForRPC(); ovk = ovkForShieldingFromTaddr(seed); } @@ -789,7 +784,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; wtxDepth = wtx.GetDepthInMainChain(); } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n", + LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, jsoutindex=%d, amount=%s, height=%d, confirmations=%d)\n", getId(), jso.hash.ToString().substr(0, 10), jso.js, @@ -1034,7 +1029,7 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) { bool AsyncRPCOperation_sendmany::find_unspent_notes() { - std::vector sproutEntries; + std::vector sproutEntries; std::vector saplingEntries; { LOCK2(cs_main, pwalletMain->cs_wallet); @@ -1050,15 +1045,15 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { saplingEntries.clear(); } - for (CSproutNotePlaintextEntry & entry : sproutEntries) { - z_sprout_inputs_.push_back(SendManyInputJSOP(entry.jsop, entry.plaintext.note(boost::get(frompaymentaddress_)), CAmount(entry.plaintext.value()))); - std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end()); - LogPrint("zrpcunsafe", "%s: found unspent Sprout note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, memo=%s)\n", + for (SproutNoteEntry & entry : sproutEntries) { + z_sprout_inputs_.push_back(SendManyInputJSOP(entry.jsop, entry.note, CAmount(entry.note.value()))); + std::string data(entry.memo.begin(), entry.memo.end()); + LogPrint("zrpcunsafe", "%s: found unspent Sprout note (txid=%s, vjoinsplit=%d, jsoutindex=%d, amount=%s, memo=%s)\n", getId(), entry.jsop.hash.ToString().substr(0, 10), entry.jsop.js, int(entry.jsop.n), // uint8_t - FormatMoney(entry.plaintext.value()), + FormatMoney(entry.note.value()), HexStr(data).substr(0, 10) ); } diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index 4280ac2f15c..84811c6868e 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -247,12 +247,7 @@ bool ShieldToAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) c // generate a common one from the HD seed. This ensures the data is // recoverable, while keeping it logically separate from the ZIP 32 // Sapling key hierarchy, which the user might not be using. - HDSeed seed; - if (!pwalletMain->GetHDSeed(seed)) { - throw JSONRPCError( - RPC_WALLET_ERROR, - "CWallet::GenerateNewSaplingZKey(): HD seed not found"); - } + HDSeed seed = pwalletMain->GetHDSeedForRPC(); uint256 ovk = ovkForShieldingFromTaddr(seed); // Add transparent inputs diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 9af4aa0609a..029753a9483 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -188,7 +188,7 @@ TEST(WalletTests, FindUnspentSproutNotes) { EXPECT_FALSE(wallet.IsSproutSpent(nullifier)); // We currently have an unspent and unconfirmed note in the wallet (depth of -1) - std::vector sproutEntries; + std::vector sproutEntries; std::vector saplingEntries; wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); EXPECT_EQ(0, sproutEntries.size()); @@ -379,7 +379,7 @@ TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) { ASSERT_TRUE(nf); uint256 nullifier = nf.get(); - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingSpend(expsk, note, anchor, witness); builder.AddSaplingOutput(fvk.ovk, pk, 50000, {}); builder.SetFee(0); @@ -506,7 +506,7 @@ TEST(WalletTests, FindMySaplingNotes) { auto testNote = GetTestSaplingNote(pa, 50000); // Generate transaction - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -638,7 +638,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) { auto witness = saplingTree.witness(); // Generate tx to create output note B - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingSpend(expsk, note, anchor, witness); builder.AddSaplingOutput(fvk.ovk, pk, 35000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -692,13 +692,13 @@ TEST(WalletTests, GetConflictedSaplingNotes) { anchor = saplingTree.root(); // Create transaction to spend note B - auto builder2 = TransactionBuilder(consensusParams, 2); + auto builder2 = TransactionBuilder(consensusParams, 2, expiryDelta); builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness); builder2.AddSaplingOutput(fvk.ovk, pk, 20000, {}); auto tx2 = builder2.Build().GetTxOrThrow(); // Create conflicting transaction which also spends note B - auto builder3 = TransactionBuilder(consensusParams, 2); + auto builder3 = TransactionBuilder(consensusParams, 2, expiryDelta); builder3.AddSaplingSpend(expsk, note2, anchor, spend_note_witness); builder3.AddSaplingOutput(fvk.ovk, pk, 19999, {}); auto tx3 = builder3.Build().GetTxOrThrow(); @@ -785,7 +785,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) { auto testNote = GetTestSaplingNote(pa, 50000); // Generate transaction - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -868,7 +868,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) { auto testNote = GetTestSaplingNote(pa, 50000); // Generate transaction - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -996,7 +996,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { auto witness = saplingTree.witness(); // Generate transaction, which sends funds to note B - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingSpend(expsk, note, anchor, witness); builder.AddSaplingOutput(fvk.ovk, pk, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -1066,7 +1066,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { anchor = saplingTree.root(); // Create transaction to spend note B - auto builder2 = TransactionBuilder(consensusParams, 2); + auto builder2 = TransactionBuilder(consensusParams, 2, expiryDelta); builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness); builder2.AddSaplingOutput(fvk.ovk, pk, 12500, {}); auto tx2 = builder2.Build().GetTxOrThrow(); @@ -1771,7 +1771,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) { auto testNote = GetTestSaplingNote(pa, 50000); // Generate transaction - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSaplingOutput(fvk.ovk, pa2, 25000, {}); auto tx = builder.Build().GetTxOrThrow(); @@ -1912,7 +1912,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { // Generate shielding tx from transparent to Sapling // 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee - auto builder = TransactionBuilder(consensusParams, 1, &keystore); + auto builder = TransactionBuilder(consensusParams, 1, expiryDelta, &keystore); builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000); builder.AddSaplingOutput(fvk.ovk, pk, 40000, {}); auto tx1 = builder.Build().GetTxOrThrow(); @@ -1967,7 +1967,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { // Create a Sapling-only transaction // 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change - auto builder2 = TransactionBuilder(consensusParams, 2); + auto builder2 = TransactionBuilder(consensusParams, 2, expiryDelta); builder2.AddSaplingSpend(expsk, note, anchor, witness); builder2.AddSaplingOutput(fvk.ovk, pk, 25000, {}); auto tx2 = builder2.Build().GetTxOrThrow(); diff --git a/src/wallet/rpcdisclosure.cpp b/src/wallet/rpcdisclosure.cpp index df35bb6ddf2..82d20b76ce2 100644 --- a/src/wallet/rpcdisclosure.cpp +++ b/src/wallet/rpcdisclosure.cpp @@ -82,7 +82,7 @@ UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp) uint256 hashBlock; // Check txid has been seen - if (!GetTransaction(hash, tx, hashBlock, true)) { + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); } @@ -210,7 +210,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) CTransaction tx; uint256 hashBlock; // Check if we have seen the transaction - if (!GetTransaction(hash, tx, hashBlock, true)) { + if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ac16695878b..c76a630a6ad 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -27,6 +27,7 @@ #include "asyncrpcoperation.h" #include "asyncrpcqueue.h" #include "wallet/asyncrpcoperation_mergetoaddress.h" +#include "wallet/asyncrpcoperation_saplingmigration.h" #include "wallet/asyncrpcoperation_sendmany.h" #include "wallet/asyncrpcoperation_shieldcoinbase.h" @@ -2566,7 +2567,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) UniValue results(UniValue::VARR); if (zaddrs.size() > 0) { - std::vector sproutEntries; + std::vector sproutEntries; std::vector saplingEntries; pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false); std::set> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs); @@ -2580,8 +2581,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) bool hasSproutSpendingKey = pwalletMain->HaveSproutSpendingKey(boost::get(entry.address)); obj.push_back(Pair("spendable", hasSproutSpendingKey)); obj.push_back(Pair("address", EncodePaymentAddress(entry.address))); - obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value())))); - std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end()); + obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value())))); + std::string data(entry.memo.begin(), entry.memo.end()); obj.push_back(Pair("memo", HexStr(data))); if (hasSproutSpendingKey) { obj.push_back(Pair("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop))); @@ -3290,12 +3291,12 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ign CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) { CAmount balance = 0; - std::vector sproutEntries; + std::vector sproutEntries; std::vector saplingEntries; LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, address, minDepth, true, ignoreUnspendable); for (auto & entry : sproutEntries) { - balance += CAmount(entry.plaintext.value()); + balance += CAmount(entry.note.value()); } for (auto & entry : saplingEntries) { balance += CAmount(entry.note.value()); @@ -3353,7 +3354,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) } UniValue result(UniValue::VARR); - std::vector sproutEntries; + std::vector sproutEntries; std::vector saplingEntries; pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress, nMinDepth, false, false); @@ -3364,11 +3365,11 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) } if (boost::get(&zaddr) != nullptr) { - for (CSproutNotePlaintextEntry & entry : sproutEntries) { + for (SproutNoteEntry & entry : sproutEntries) { UniValue obj(UniValue::VOBJ); obj.push_back(Pair("txid", entry.jsop.hash.ToString())); - obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value())))); - std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end()); + obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value())))); + std::string data(entry.memo.begin(), entry.memo.end()); obj.push_back(Pair("memo", HexStr(data))); obj.push_back(Pair("jsindex", entry.jsop.js)); obj.push_back(Pair("jsoutindex", entry.jsop.n)); @@ -3885,7 +3886,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) // Builder (used if Sapling addresses are involved) boost::optional builder; if (noSproutAddrs) { - builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); + builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, expiryDelta, pwalletMain); } // Contextual transaction we will build on @@ -3904,6 +3905,131 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) return operationId; } +UniValue z_setmigration(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() != 1) + throw runtime_error( + "z_setmigration enabled\n" + "When enabled the Sprout to Sapling migration will attempt to migrate all funds from this wallet’s\n" + "Sprout addresses to either the address for Sapling account 0 or the address specified by the parameter\n" + "'-migrationdestaddress'.\n" + "\n" + "This migration is designed to minimize information leakage. As a result for wallets with a significant\n" + "Sprout balance, this process may take several weeks. The migration works by sending, up to 5, as many\n" + "transactions as possible whenever the blockchain reaches a height equal to 499 modulo 500. The transaction\n" + "amounts are picked according to the random distribution specified in ZIP 308. The migration will end once\n" + "the wallet’s Sprout balance is below " + strprintf("%s %s", FormatMoney(CENT), CURRENCY_UNIT) + ".\n" + "\nArguments:\n" + "1. enabled (boolean, required) 'true' or 'false' to enable or disable respectively.\n" + ); + LOCK(pwalletMain->cs_wallet); + pwalletMain->fSaplingMigrationEnabled = params[0].get_bool(); + return NullUniValue; +} + +UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) { + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + if (fHelp || params.size() != 0) + throw runtime_error( + "z_getmigrationstatus\n" + "Returns information about the status of the Sprout to Sapling migration.\n" + "Note: A transaction is defined as finalized if it has at least ten confirmations.\n" + "Also, it is possible that manually created transactions involving this wallet\n" + "will be included in the result.\n" + "\nResult:\n" + "{\n" + " \"enabled\": true|false, (boolean) Whether or not migration is enabled\n" + " \"destination_address\": \"zaddr\", (string) The Sapling address that will receive Sprout funds\n" + " \"unmigrated_amount\": nnn.n, (numeric) The total amount of unmigrated " + CURRENCY_UNIT +" \n" + " \"unfinalized_migrated_amount\": nnn.n, (numeric) The total amount of unfinalized " + CURRENCY_UNIT + " \n" + " \"finalized_migrated_amount\": nnn.n, (numeric) The total amount of finalized " + CURRENCY_UNIT + " \n" + " \"finalized_migration_transactions\": nnn, (numeric) The number of migration transactions involving this wallet\n" + " \"time_started\": ttt, (numeric, optional) The block time of the first migration transaction as a Unix timestamp\n" + " \"migration_txids\": [txids] (json array of strings) An array of all migration txids involving this wallet\n" + "}\n" + ); + LOCK2(cs_main, pwalletMain->cs_wallet); + UniValue migrationStatus(UniValue::VOBJ); + migrationStatus.push_back(Pair("enabled", pwalletMain->fSaplingMigrationEnabled)); + // The "destination_address" field MAY be omitted if the "-migrationdestaddress" + // parameter is not set and no default address has yet been generated. + // Note: The following function may return the default address even if it has not been added to the wallet + auto destinationAddress = AsyncRPCOperation_saplingmigration::getMigrationDestAddress(pwalletMain->GetHDSeedForRPC()); + migrationStatus.push_back(Pair("destination_address", EncodePaymentAddress(destinationAddress))); + // The values of "unmigrated_amount" and "migrated_amount" MUST take into + // account failed transactions, that were not mined within their expiration + // height. + { + std::vector sproutEntries; + std::vector saplingEntries; + std::set noFilter; + // Here we are looking for any and all Sprout notes for which we have the spending key, including those + // which are locked and/or only exist in the mempool, as they should be included in the unmigrated amount. + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, noFilter, 0, INT_MAX, true, true, false); + CAmount unmigratedAmount = 0; + for (const auto& sproutEntry : sproutEntries) { + unmigratedAmount += sproutEntry.note.value(); + } + migrationStatus.push_back(Pair("unmigrated_amount", FormatMoney(unmigratedAmount))); + } + // "migration_txids" is a list of strings representing transaction IDs of all + // known migration transactions involving this wallet, as lowercase hexadecimal + // in RPC byte order. + UniValue migrationTxids(UniValue::VARR); + CAmount unfinalizedMigratedAmount = 0; + CAmount finalizedMigratedAmount = 0; + int numFinalizedMigrationTxs = 0; + uint64_t timeStarted = 0; + for (const auto& txPair : pwalletMain->mapWallet) { + CWalletTx tx = txPair.second; + // A given transaction is defined as a migration transaction iff it has: + // * one or more Sprout JoinSplits with nonzero vpub_new field; and + // * no Sapling Spends, and; + // * one or more Sapling Outputs. + if (tx.vjoinsplit.size() > 0 && tx.vShieldedSpend.empty() && tx.vShieldedOutput.size() > 0) { + bool nonZeroVPubNew = false; + for (const auto& js : tx.vjoinsplit) { + if (js.vpub_new > 0) { + nonZeroVPubNew = true; + break; + } + } + if (!nonZeroVPubNew) { + continue; + } + migrationTxids.push_back(txPair.first.ToString()); + // A transaction is "finalized" iff it has at least 10 confirmations. + // TODO: subject to change, if the recommended number of confirmations changes. + if (tx.GetDepthInMainChain() >= 10) { + finalizedMigratedAmount -= tx.valueBalance; + ++numFinalizedMigrationTxs; + } else { + unfinalizedMigratedAmount -= tx.valueBalance; + } + // If the transaction is in the mempool it will not be associated with a block yet + if (tx.hashBlock.IsNull() || mapBlockIndex[tx.hashBlock] == nullptr) { + continue; + } + CBlockIndex* blockIndex = mapBlockIndex[tx.hashBlock]; + // The value of "time_started" is the earliest Unix timestamp of any known + // migration transaction involving this wallet; if there is no such transaction, + // then the field is absent. + if (timeStarted == 0 || timeStarted > blockIndex->GetBlockTime()) { + timeStarted = blockIndex->GetBlockTime(); + } + } + } + migrationStatus.push_back(Pair("unfinalized_migrated_amount", FormatMoney(unfinalizedMigratedAmount))); + migrationStatus.push_back(Pair("finalized_migrated_amount", FormatMoney(finalizedMigratedAmount))); + migrationStatus.push_back(Pair("finalized_migration_transactions", numFinalizedMigrationTxs)); + if (timeStarted > 0) { + migrationStatus.push_back(Pair("time_started", timeStarted)); + } + migrationStatus.push_back(Pair("migration_txids", migrationTxids)); + return migrationStatus; +} /** When estimating the number of coinbase utxos we can shield in a single transaction: @@ -4100,7 +4226,7 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) // Builder (used if Sapling addresses are involved) TransactionBuilder builder = TransactionBuilder( - Params().GetConsensus(), nextBlockHeight, pwalletMain); + Params().GetConsensus(), nextBlockHeight, expiryDelta, pwalletMain); // Contextual transaction we will build on // (used if no Sapling addresses are involved) @@ -4169,7 +4295,9 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) " - \"ANY_TADDR\": Merge UTXOs from any taddrs belonging to the wallet.\n" " - \"ANY_SPROUT\": Merge notes from any Sprout zaddrs belonging to the wallet.\n" " - \"ANY_SAPLING\": Merge notes from any Sapling zaddrs belonging to the wallet.\n" - " If a special string is given, any given addresses of that type will be counted as duplicates and cause an error.\n" + " While it is possible to use a variety of different combinations of addresses and the above values,\n" + " it is not possible to send funds from both sprout and sapling addresses simultaneously. If a special\n" + " string is given, any given addresses of that type will be counted as duplicates and cause an error.\n" " [\n" " \"address\" (string) Can be a taddr or a zaddr\n" " ,...\n" @@ -4396,7 +4524,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) if (useAnySprout || useAnySapling || zaddrs.size() > 0) { // Get available notes - std::vector sproutEntries; + std::vector sproutEntries; std::vector saplingEntries; pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs); @@ -4404,8 +4532,15 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) if (!saplingActive && saplingEntries.size() > 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); } + // Do not include Sprout/Sapling notes if using "ANY_SAPLING"/"ANY_SPROUT" respectively + if (useAnySprout) { + saplingEntries.clear(); + } + if (useAnySapling) { + sproutEntries.clear(); + } // Sending from both Sprout and Sapling is currently unsupported using z_mergetoaddress - if (sproutEntries.size() > 0 && saplingEntries.size() > 0) { + if ((sproutEntries.size() > 0 && saplingEntries.size() > 0) || (useAnySprout && useAnySapling)) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress"); @@ -4418,9 +4553,9 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) } // Find unspent notes and update estimated size - for (const CSproutNotePlaintextEntry& entry : sproutEntries) { + for (const SproutNoteEntry& entry : sproutEntries) { noteCounter++; - CAmount nValue = entry.plaintext.value(); + CAmount nValue = entry.note.value(); if (!maxedOutNotesFlag) { // If we haven't added any notes yet and the merge is to a @@ -4435,7 +4570,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) auto zaddr = entry.address; SproutSpendingKey zkey; pwalletMain->GetSproutSpendingKey(zaddr, zkey); - sproutNoteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, zkey); + sproutNoteInputs.emplace_back(entry.jsop, entry.note, nValue, zkey); mergedNoteValue += nValue; } } @@ -4517,7 +4652,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) // Builder (used if Sapling addresses are involved) boost::optional builder; if (isToSaplingZaddr || saplingNoteInputs.size() > 0) { - builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); + builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, expiryDelta, pwalletMain); } // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); @@ -4658,6 +4793,8 @@ static const CRPCCommand commands[] = { "wallet", "z_gettotalbalance", &z_gettotalbalance, false }, { "wallet", "z_mergetoaddress", &z_mergetoaddress, false }, { "wallet", "z_sendmany", &z_sendmany, false }, + { "wallet", "z_setmigration", &z_setmigration, false }, + { "wallet", "z_getmigrationstatus", &z_getmigrationstatus, false }, { "wallet", "z_shieldcoinbase", &z_shieldcoinbase, false }, { "wallet", "z_getoperationstatus", &z_getoperationstatus, true }, { "wallet", "z_getoperationresult", &z_getoperationresult, true }, diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index dca34ea6883..2f063610c1e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5,8 +5,10 @@ #include "wallet/wallet.h" +#include "asyncrpcqueue.h" #include "checkpoints.h" #include "coincontrol.h" +#include "core_io.h" #include "consensus/upgrades.h" #include "consensus/validation.h" #include "consensus/consensus.h" @@ -15,12 +17,14 @@ #include "main.h" #include "net.h" #include "rpc/protocol.h" +#include "rpc/server.h" #include "script/script.h" #include "script/sign.h" #include "timedata.h" #include "utilmoneystr.h" #include "zcash/Note.hpp" #include "crypter.h" +#include "wallet/asyncrpcoperation_saplingmigration.h" #include "zcash/zip32.h" #include @@ -32,6 +36,8 @@ using namespace std; using namespace libzcash; +extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); + /** * Settings */ @@ -499,8 +505,14 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) continue; // try another master key - if (CCryptoKeyStore::Unlock(vMasterKey)) + if (CCryptoKeyStore::Unlock(vMasterKey)) { + // Now that the wallet is decrypted, ensure we have an HD seed. + // https://github.com/zcash/zcash/issues/3607 + if (!this->HaveHDSeed()) { + this->GenerateNewSeed(); + } return true; + } } } return false; @@ -552,6 +564,15 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; } +void CWallet::ChainTipAdded(const CBlockIndex *pindex, + const CBlock *pblock, + SproutMerkleTree sproutTree, + SaplingMerkleTree saplingTree) +{ + IncrementNoteWitnesses(pindex, pblock, sproutTree, saplingTree); + UpdateSaplingNullifierNoteMapForBlock(pblock); +} + void CWallet::ChainTip(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, @@ -559,11 +580,69 @@ void CWallet::ChainTip(const CBlockIndex *pindex, bool added) { if (added) { - IncrementNoteWitnesses(pindex, pblock, sproutTree, saplingTree); + ChainTipAdded(pindex, pblock, sproutTree, saplingTree); + // Prevent migration transactions from being created when node is syncing after launch, + // and also when node wakes up from suspension/hibernation and incoming blocks are old. + if (!IsInitialBlockDownload(Params()) && + pblock->GetBlockTime() > GetAdjustedTime() - 3 * 60 * 60) + { + RunSaplingMigration(pindex->nHeight); + } } else { DecrementNoteWitnesses(pindex); + UpdateSaplingNullifierNoteMapForBlock(pblock); + } +} + +void CWallet::RunSaplingMigration(int blockHeight) { + if (!NetworkUpgradeActive(blockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { + return; + } + LOCK(cs_wallet); + if (!fSaplingMigrationEnabled) { + return; + } + // The migration transactions to be sent in a particular batch can take + // significant time to generate, and this time depends on the speed of the user's + // computer. If they were generated only after a block is seen at the target + // height minus 1, then this could leak information. Therefore, for target + // height N, implementations SHOULD start generating the transactions at around + // height N-5 + if (blockHeight % 500 == 495) { + std::shared_ptr q = getAsyncRPCQueue(); + std::shared_ptr lastOperation = q->getOperationForId(saplingMigrationOperationId); + if (lastOperation != nullptr) { + lastOperation->cancel(); + } + pendingSaplingMigrationTxs.clear(); + std::shared_ptr operation(new AsyncRPCOperation_saplingmigration(blockHeight + 5)); + saplingMigrationOperationId = operation->getId(); + q->addOperation(operation); + } else if (blockHeight % 500 == 499) { + std::shared_ptr q = getAsyncRPCQueue(); + std::shared_ptr lastOperation = q->getOperationForId(saplingMigrationOperationId); + if (lastOperation != nullptr) { + lastOperation->cancel(); + } + for (const CTransaction& transaction : pendingSaplingMigrationTxs) { + // The following is taken from z_sendmany/z_mergetoaddress + // Send the transaction + // TODO: Use CWallet::CommitTransaction instead of sendrawtransaction + auto signedtxn = EncodeHexTx(transaction); + UniValue params = UniValue(UniValue::VARR); + params.push_back(signedtxn); + UniValue sendResultValue = sendrawtransaction(params, false); + if (sendResultValue.isNull()) { + throw JSONRPCError(RPC_WALLET_ERROR, "sendrawtransaction did not return an error or a txid."); + } + } + pendingSaplingMigrationTxs.clear(); } - UpdateSaplingNullifierNoteMapForBlock(pblock); +} + +void CWallet::AddPendingSaplingMigrationTx(const CTransaction& tx) { + LOCK(cs_wallet); + pendingSaplingMigrationTxs.push_back(tx); } void CWallet::SetBestChain(const CBlockLocator& loc) @@ -1055,7 +1134,7 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex, const CBlock* pblock {pblockIn}; CBlock block; if (!pblock) { - ReadBlockFromDisk(block, pindex); + ReadBlockFromDisk(block, pindex, Params().GetConsensus()); pblock = █ } @@ -2086,6 +2165,14 @@ bool CWallet::SetCryptedHDSeed(const uint256& seedFp, const std::vectorGetHDSeed(seed)) { + throw JSONRPCError(RPC_WALLET_ERROR, "HD seed not found"); + } + return seed; +} + void CWallet::SetHDChain(const CHDChain& chain, bool memonly) { LOCK(cs_wallet); @@ -2345,7 +2432,7 @@ void CWallet::WitnessNoteCommitment(std::vector commitments, while (pindex) { CBlock block; - ReadBlockFromDisk(block, pindex); + ReadBlockFromDisk(block, pindex, Params().GetConsensus()); BOOST_FOREACH(const CTransaction& tx, block.vtx) { @@ -2424,7 +2511,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); CBlock block; - ReadBlockFromDisk(block, pindex); + ReadBlockFromDisk(block, pindex, Params().GetConsensus()); BOOST_FOREACH(CTransaction& tx, block.vtx) { if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) { @@ -2444,7 +2531,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) } } // Increment note witness caches - ChainTip(pindex, &block, sproutTree, saplingTree, true); + ChainTipAdded(pindex, &block, sproutTree, saplingTree); pindex = chainActive.Next(pindex); if (GetTime() >= nNow + 60) { @@ -4393,7 +4480,7 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) * These notes are decrypted and added to the output parameter vector, outEntries. */ void CWallet::GetFilteredNotes( - std::vector& sproutEntries, + std::vector& sproutEntries, std::vector& saplingEntries, std::string address, int minDepth, @@ -4415,7 +4502,7 @@ void CWallet::GetFilteredNotes( * These notes are decrypted and added to the output parameter vector, outEntries. */ void CWallet::GetFilteredNotes( - std::vector& sproutEntries, + std::vector& sproutEntries, std::vector& saplingEntries, std::set& filterAddresses, int minDepth, @@ -4490,7 +4577,8 @@ void CWallet::GetFilteredNotes( hSig, (unsigned char) j); - sproutEntries.push_back(CSproutNotePlaintextEntry{jsop, pa, plaintext, wtx.GetDepthInMainChain()}); + sproutEntries.push_back(SproutNoteEntry { + jsop, pa, plaintext.note(pa), plaintext.memo(), wtx.GetDepthInMainChain() }); } catch (const note_decryption_failed &err) { // Couldn't decrypt with this spending key diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3f43e434e83..39c473a2f46 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -7,6 +7,7 @@ #define BITCOIN_WALLET_WALLET_H #include "amount.h" +#include "asyncrpcoperation.h" #include "coins.h" #include "key.h" #include "keystore.h" @@ -311,12 +312,13 @@ class SaplingNoteData typedef std::map mapSproutNoteData_t; typedef std::map mapSaplingNoteData_t; -/** Decrypted note, its location in a transaction, and number of confirmations. */ -struct CSproutNotePlaintextEntry +/** Sprout note, its location in a transaction, and number of confirmations. */ +struct SproutNoteEntry { JSOutPoint jsop; libzcash::SproutPaymentAddress address; - libzcash::SproutNotePlaintext plaintext; + libzcash::SproutNote note; + std::array memo; int confirmations; }; @@ -403,11 +405,11 @@ class CWalletTx : public CMerkleTx mapSaplingNoteData_t mapSaplingNoteData; std::vector > vOrderForm; unsigned int fTimeReceivedIsTxTime; - unsigned int nTimeReceived; //! time received by this node + unsigned int nTimeReceived; //!< time received by this node unsigned int nTimeSmart; char fFromMe; std::string strFromAccount; - int64_t nOrderPos; //! position in ordered transaction list + int64_t nOrderPos; //!< position in ordered transaction list // memory only mutable bool fDebitCached; @@ -501,7 +503,7 @@ class CWalletTx : public CMerkleTx } READWRITE(*(CMerkleTx*)this); - std::vector vUnused; //! Used to be vtxPrev + std::vector vUnused; //!< Used to be vtxPrev READWRITE(vUnused); READWRITE(mapValue); READWRITE(mapSproutNoteData); @@ -647,7 +649,7 @@ class CAccountingEntry std::string strOtherAccount; std::string strComment; mapValue_t mapValue; - int64_t nOrderPos; //! position in ordered transaction list + int64_t nOrderPos; //!< position in ordered transaction list uint64_t nEntryNo; CAccountingEntry() @@ -755,6 +757,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface TxNullifiers mapTxSproutNullifiers; TxNullifiers mapTxSaplingNullifiers; + std::vector pendingSaplingMigrationTxs; + AsyncRPCOperationId saplingMigrationOperationId; + void AddToTransparentSpends(const COutPoint& outpoint, const uint256& wtxid); void AddToSproutSpends(const uint256& nullifier, const uint256& wtxid); void AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid); @@ -767,6 +772,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * incremental witness cache in any transaction in mapWallet. */ int64_t nWitnessCacheSize; + bool fSaplingMigrationEnabled = false; void ClearNoteWitnessCache(); @@ -832,6 +838,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface private: template void SyncMetaData(std::pair::iterator, typename TxSpendMap::iterator>); + void ChainTipAdded(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree); protected: bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx); @@ -1183,6 +1190,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetChange(const CTransaction& tx) const; void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree, bool added); + void RunSaplingMigration(int blockHeight); + void AddPendingSaplingMigrationTx(const CTransaction& tx); /** Saves witness caches and best block locator to disk. */ void SetBestChain(const CBlockLocator& loc); std::set> GetNullifiersForAddresses(const std::set & addresses); @@ -1280,6 +1289,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool SetHDSeed(const HDSeed& seed); bool SetCryptedHDSeed(const uint256& seedFp, const std::vector &vchCryptedSecret); + /* Returns the wallet's HD seed or throw JSONRPCError(...) */ + HDSeed GetHDSeedForRPC() const; + /* Set the HD chain model (chain child index counters) */ void SetHDChain(const CHDChain& chain, bool memonly); const CHDChain& GetHDChain() const { return hdChain; } @@ -1290,7 +1302,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed); /* Find notes filtered by payment address, min depth, ability to spend */ - void GetFilteredNotes(std::vector& sproutEntries, + void GetFilteredNotes(std::vector& sproutEntries, std::vector& saplingEntries, std::string address, int minDepth=1, @@ -1299,7 +1311,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* Find notes filtered by payment addresses, min depth, max depth, if they are spent, if a spending key is required, and if they are locked */ - void GetFilteredNotes(std::vector& sproutEntries, + void GetFilteredNotes(std::vector& sproutEntries, std::vector& saplingEntries, std::set& filterAddresses, int minDepth=1, diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 5283d50975e..cb4cc181258 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -172,8 +172,9 @@ double benchmark_solve_equihash() CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << I; - unsigned int n = Params(CBaseChainParams::MAIN).EquihashN(); - unsigned int k = Params(CBaseChainParams::MAIN).EquihashK(); + auto params = Params(CBaseChainParams::MAIN).GetConsensus(); + unsigned int n = params.nEquihashN; + unsigned int k = params.nEquihashK; crypto_generichash_blake2b_state eh_state; EhInitialiseState(n, k, eh_state); crypto_generichash_blake2b_update(&eh_state, (unsigned char*)&ss[0], ss.size()); @@ -217,11 +218,11 @@ std::vector benchmark_solve_equihash_threaded(int nThreads) double benchmark_verify_equihash() { CChainParams params = Params(CBaseChainParams::MAIN); - CBlock genesis = Params(CBaseChainParams::MAIN).GenesisBlock(); + CBlock genesis = params.GenesisBlock(); CBlockHeader genesis_header = genesis.GetBlockHeader(); struct timeval tv_start; timer_start(tv_start); - CheckEquihashSolution(&genesis_header, params); + CheckEquihashSolution(&genesis_header, params.GetConsensus()); return timer_stop(tv_start); } @@ -453,9 +454,11 @@ double benchmark_increment_sapling_note_witnesses(size_t nTxs) // This class is based on the class CCoinsViewDB, but with limited functionality. // The construtor and the functions `GetCoins` and `HaveCoins` come directly from // CCoinsViewDB, but the rest are either mocks and/or don't really do anything. + +// The following constant is a duplicate of the one found in txdb.cpp +static const char DB_COINS = 'c'; + class FakeCoinsViewDB : public CCoinsView { - // The following constant is a duplicate of the one found in txdb.cpp - static const char DB_COINS = 'c'; CDBWrapper db; @@ -553,7 +556,7 @@ double benchmark_connectblock_slow() CValidationState state; struct timeval tv_start; timer_start(tv_start); - assert(ConnectBlock(block, state, &index, view, true)); + assert(ConnectBlock(block, state, &index, view, Params(), true)); auto duration = timer_stop(tv_start); // Undo alterations to global state diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp index 4567f25b1f9..28cf2e597dc 100644 --- a/src/zmq/zmqpublishnotifier.cpp +++ b/src/zmq/zmqpublishnotifier.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "chainparams.h" #include "zmqpublishnotifier.h" #include "main.h" #include "util.h" @@ -164,11 +165,12 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex) { LogPrint("zmq", "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex()); + const Consensus::Params& consensusParams = Params().GetConsensus(); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); { LOCK(cs_main); CBlock block; - if(!ReadBlockFromDisk(block, pindex)) + if(!ReadBlockFromDisk(block, pindex, consensusParams)) { zmqError("Can't read block from disk"); return false; diff --git a/zcutil/make-release.py b/zcutil/make-release.py index bc47d0b1909..303048ed00b 100755 --- a/zcutil/make-release.py +++ b/zcutil/make-release.py @@ -292,7 +292,7 @@ def gen_release_notes(release, releasefrom): @phase('Updating debian changelog.') def update_debian_changelog(release): os.environ['DEBEMAIL'] = 'team@z.cash' - os.environ['DEBFULLNAME'] = 'Zcash Company' + os.environ['DEBFULLNAME'] = 'Electric Coin Company' sh_log( 'debchange', '--newversion', release.debversion, diff --git a/zcutil/release-notes.py b/zcutil/release-notes.py index 6e0f277df32..b1a262232e1 100755 --- a/zcutil/release-notes.py +++ b/zcutil/release-notes.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import re, os, os.path import subprocess import argparse @@ -20,14 +22,19 @@ ] author_aliases = { - 'Simon': 'Simon Liu', - 'bitcartel': 'Simon Liu', - 'EthanHeilman': 'Ethan Heilman', 'Ariel': 'Ariel Gabizon', 'arielgabizon': 'Ariel Gabizon', + 'bitcartel': 'Simon Liu', 'Charlie OKeefe': 'Charlie O\'Keefe', + 'Duke Leto': 'Jonathan \"Duke\" Leto', + 'Eirik0': 'Eirik Ogilvie-Wigley', + 'EthanHeilman': 'Ethan Heilman', + 'MarcoFalke': 'Marco Falke', + 'mdr0id': 'Marshall Gaucher', + 'paveljanik': 'Pavel Janík', + 'Simon': 'Simon Liu', 'str4d': 'Jack Grigg', - 'Duke Leto': 'Jonathan \"Duke\" Leto' + 'zebambam': 'Benjamin Winston' } def apply_author_aliases(name):