diff --git a/README.md b/README.md index 9f7ae7cbff2..91543956abd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zclassic 1.0.3 +Zclassic 1.0.4 ============== What is Zclassic? @@ -67,6 +67,12 @@ https://github.com/z-classic/zclassic/wiki/1.0-User-Guide Participation in the Zcash project is subject to a [Code of Conduct](code_of_conduct.md). +Building +-------- + +Build Zcash along with most dependencies from source by running +./zcutil/build.sh. Currently only Linux is officially supported. + License ------- diff --git a/configure.ac b/configure.ac index 3a6ce27540e..a6e40ab5a1f 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 1) define(_CLIENT_VERSION_MINOR, 0) -define(_CLIENT_VERSION_REVISION, 3) +define(_CLIENT_VERSION_REVISION, 4) 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))) diff --git a/contrib/DEBIAN/changelog b/contrib/DEBIAN/changelog index 3b3305c932e..eb9c5f41598 100644 --- a/contrib/DEBIAN/changelog +++ b/contrib/DEBIAN/changelog @@ -1,3 +1,9 @@ +zcash (1.0.4) jessie; urgency=medium + + * 1.0.4 release. + + -- Zcash Company Thu, 15 Dec 2016 16:46:14 +1300 + zcash (1.0.3) jessie; urgency=medium * 1.0.3 release. diff --git a/contrib/DEBIAN/control b/contrib/DEBIAN/control index 4046e372baa..b24ffc54d3b 100644 --- a/contrib/DEBIAN/control +++ b/contrib/DEBIAN/control @@ -10,7 +10,7 @@ Build-Depends: autoconf, automake, bsdmainutils, build-essential Vcs-Git: https://github.com/zcash/zcash.git Vcs-Browser: https://github.com/zcash/zcash Package: zcash -Version: 1.0.3 +Version: 1.0.4 Architecture: amd64 Depends: libgomp1 Description: An implementation of the "Zerocash" protocol. diff --git a/contrib/DEBIAN/manpages/zcash-cli.1 b/contrib/DEBIAN/manpages/zcash-cli.1 index 01e778a80af..e5b47babdca 100644 --- a/contrib/DEBIAN/manpages/zcash-cli.1 +++ b/contrib/DEBIAN/manpages/zcash-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3. -.TH ZCASH-CLI "1" "November 2016" "Zcash RPC client version v1.0.3" "User Commands" +.TH ZCASH-CLI "1" "December 2016" "Zcash RPC client version v1.0.4" "User Commands" .SH NAME zcash-cli \- RPC client for the Zcash daemon .SH DESCRIPTION -Zcash RPC client version v1.0.3 +Zcash RPC client version v1.0.4 .SS "Usage:" .TP zcash\-cli [options] [params] diff --git a/contrib/DEBIAN/manpages/zcashd.1 b/contrib/DEBIAN/manpages/zcashd.1 index 9a036263086..a110108bec6 100644 --- a/contrib/DEBIAN/manpages/zcashd.1 +++ b/contrib/DEBIAN/manpages/zcashd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3. -.TH ZCASHD "1" "November 2016" "Zcash Daemon version v1.0.3" "User Commands" +.TH ZCASHD "1" "December 2016" "Zcash Daemon version v1.0.4" "User Commands" .SH NAME zcashd \- Network daemon for interacting with the Zcash blockchain .SH DESCRIPTION -Zcash Daemon version v1.0.3 +Zcash Daemon version v1.0.4 .SS "Usage:" .TP zcashd [options] @@ -62,7 +62,7 @@ Keep at most unconnectable transactions in memory (default: 100) .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-8\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-4\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP \fB\-pid=\fR @@ -219,7 +219,7 @@ Fee (in BTC/kB) to add to transactions you send (default: 0.00) .HP \fB\-rescan\fR .IP -Rescan the block chain for missing wallet transactions on startup +Rescan the blockchain for missing wallet transactions on startup .HP \fB\-salvagewallet\fR .IP @@ -271,9 +271,9 @@ Debugging/Testing options: \fB\-debug=\fR .IP Output debugging information (default: 0, supplying is -optional). If is not supplied, output all debugging -information. can be: addrman, alert, bench, coindb, db, lock, -rand, rpc, selectcoins, mempool, net, proxy, prune. +optional). If is not supplied or if = 1, output +all debugging information. can be: addrman, alert, bench, +coindb, db, lock, rand, rpc, selectcoins, mempool, net, proxy, prune. .HP \fB\-gen\fR .IP @@ -303,7 +303,7 @@ Prepend debug output with timestamp (default: 1) \fB\-minrelaytxfee=\fR .IP Fees (in BTC/Kb) smaller than this are considered zero fee for relaying -(default: 0.00005) +(default: 0.00001) .HP \fB\-printtoconsole\fR .IP @@ -336,12 +336,12 @@ Set minimum block size in bytes (default: 0) .HP \fB\-blockmaxsize=\fR .IP -Set maximum block size in bytes (default: 750000) +Set maximum block size in bytes (default: 2000000) .HP \fB\-blockprioritysize=\fR .IP Set maximum size of high\-priority/low\-fee transactions in bytes -(default: 50000) +(default: 1000000) .PP RPC server options: .HP @@ -382,10 +382,6 @@ multiple times \fB\-rpcthreads=\fR .IP Set the number of threads to service RPC calls (default: 4) -.HP -\fB\-rpckeepalive\fR -.IP -RPC support for HTTP persistent connections (default: 1) .PP RPC SSL options: (see the Bitcoin Wiki for SSL setup instructions) .HP @@ -405,6 +401,22 @@ Server private key (default: server.pem) .IP Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH) +.PP +Metrics Options (only if \fB\-daemon\fR and \fB\-printtoconsole\fR are not set): +.HP +\fB\-showmetrics\fR +.IP +Show metrics on stdout (default: 1 if running in a console, 0 otherwise) +.HP +\fB\-metricsui\fR +.IP +Set to 1 for a persistent metrics screen, 0 for sequential metrics +output (default: 1 if running in a console, 0 otherwise) +.HP +\fB\-metricsrefreshtime\fR +.IP +Number of seconds between metrics refreshes (default: 1 if running in a +console, 600 otherwise) .SH COPYRIGHT Copyright \(co 2009\-2016 The Bitcoin Core Developers .br diff --git a/contrib/bitcoin-cli.bash-completion b/contrib/bitcoin-cli.bash-completion new file mode 100644 index 00000000000..f2a44d2328a --- /dev/null +++ b/contrib/bitcoin-cli.bash-completion @@ -0,0 +1,156 @@ +# bash programmable completion for bitcoin-cli(1) +# Copyright (c) 2012-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# call $bitcoin-cli for RPC +_zcash_rpc() { + # determine already specified args necessary for RPC + local rpcargs=() + for i in ${COMP_LINE}; do + case "$i" in + -conf=*|-datadir=*|-regtest|-rpc*|-testnet) + rpcargs=( "${rpcargs[@]}" "$i" ) + ;; + esac + done + $bitcoin_cli "${rpcargs[@]}" "$@" +} + +# Add wallet accounts to COMPREPLY +_zcash_accounts() { + local accounts + # Accounts are deprecated in Zcash + #accounts=$(_zcash_rpc listaccounts | awk -F '"' '{ print $2 }') + accounts="\\\"\\\"" + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$accounts" -- "$cur" ) ) +} + +_zcash_cli() { + local cur prev words=() cword + local bitcoin_cli + + # save and use original argument to invoke bitcoin-cli for -help, help and RPC + # as bitcoin-cli might not be in $PATH + bitcoin_cli="$1" + + COMPREPLY=() + _get_comp_words_by_ref -n = cur prev words cword + + if ((cword > 5)); then + case ${words[cword-5]} in + sendtoaddress) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + esac + fi + + if ((cword > 4)); then + case ${words[cword-4]} in + importaddress|listtransactions|setban) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + signrawtransaction) + COMPREPLY=( $( compgen -W "ALL NONE SINGLE ALL|ANYONECANPAY NONE|ANYONECANPAY SINGLE|ANYONECANPAY" -- "$cur" ) ) + return 0 + ;; + esac + fi + + if ((cword > 3)); then + case ${words[cword-3]} in + addmultisigaddress) + _zcash_accounts + return 0 + ;; + getbalance|gettxout|importaddress|importpubkey|importprivkey|listreceivedbyaccount|listreceivedbyaddress|listsinceblock) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + esac + fi + + if ((cword > 2)); then + case ${words[cword-2]} in + addnode) + COMPREPLY=( $( compgen -W "add remove onetry" -- "$cur" ) ) + return 0 + ;; + setban) + COMPREPLY=( $( compgen -W "add remove" -- "$cur" ) ) + return 0 + ;; + fundrawtransaction|getblock|getblockheader|getmempoolancestors|getmempooldescendants|getrawtransaction|gettransaction|listaccounts|listreceivedbyaccount|listreceivedbyaddress|sendrawtransaction|z_importkey) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + move|setaccount) + _zcash_accounts + return 0 + ;; + esac + fi + + case "$prev" in + backupwallet|dumpwallet|importwallet|z_exportwallet|z_importwallet) + _filedir + return 0 + ;; + getaddednodeinfo|getrawmempool|lockunspent|setgenerate) + COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) + return 0 + ;; + getaccountaddress|getaddressesbyaccount|getbalance|getnewaddress|getreceivedbyaccount|listtransactions|move|sendfrom|sendmany) + _zcash_accounts + return 0 + ;; + esac + + case "$cur" in + -conf=*) + cur="${cur#*=}" + _filedir + return 0 + ;; + -datadir=*) + cur="${cur#*=}" + _filedir -d + return 0 + ;; + -*=*) # prevent nonsense completions + return 0 + ;; + *) + local helpopts commands + + # only parse -help if senseful + if [[ -z "$cur" || "$cur" =~ ^- ]]; then + helpopts=$($bitcoin_cli -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) + fi + + # only parse help if senseful + if [[ -z "$cur" || "$cur" =~ ^[a-z] ]]; then + commands=$(_zcash_rpc help 2>/dev/null | awk '$1 ~ /^[a-z]/ { print $1; }') + fi + + COMPREPLY=( $( compgen -W "$helpopts $commands" -- "$cur" ) ) + + # Prevent space if an argument is desired + if [[ $COMPREPLY == *= ]]; then + compopt -o nospace + fi + return 0 + ;; + esac +} && +complete -F _zcash_cli zcash-cli + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/contrib/bitcoin-tx.bash-completion b/contrib/bitcoin-tx.bash-completion new file mode 100644 index 00000000000..0206eba7489 --- /dev/null +++ b/contrib/bitcoin-tx.bash-completion @@ -0,0 +1,57 @@ +# bash programmable completion for bitcoin-tx(1) +# Copyright (c) 2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +_bitcoin_tx() { + local cur prev words=() cword + local bitcoin_tx + + # save and use original argument to invoke bitcoin-tx for -help + # it might not be in $PATH + bitcoin_tx="$1" + + COMPREPLY=() + _get_comp_words_by_ref -n =: cur prev words cword + + case "$cur" in + load=*:*) + cur="${cur#load=*:}" + _filedir + return 0 + ;; + *=*) # prevent attempts to complete other arguments + return 0 + ;; + esac + + if [[ "$cword" == 1 || ( "$prev" != "-create" && "$prev" == -* ) ]]; then + # only options (or an uncompletable hex-string) allowed + # parse bitcoin-tx -help for options + local helpopts + helpopts=$($bitcoin_tx -help | sed -e '/^ -/ p' -e d ) + COMPREPLY=( $( compgen -W "$helpopts" -- "$cur" ) ) + else + # only commands are allowed + # parse -help for commands + local helpcmds + helpcmds=$($bitcoin_tx -help | sed -e '1,/Commands:/d' -e 's/=.*/=/' -e '/^ [a-z]/ p' -e d ) + COMPREPLY=( $( compgen -W "$helpcmds" -- "$cur" ) ) + fi + + # Prevent space if an argument is desired + if [[ $COMPREPLY == *= ]]; then + compopt -o nospace + fi + + return 0 +} && +complete -F _bitcoin_tx zcash-tx + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/contrib/bitcoind.bash-completion b/contrib/bitcoind.bash-completion index 3cc959c0a6d..378738877a7 100644 --- a/contrib/bitcoind.bash-completion +++ b/contrib/bitcoind.bash-completion @@ -1,102 +1,21 @@ -# bash programmable completion for bitcoind(1) and bitcoin-cli(1) -# Copyright (c) 2012,2014 Christian von Roques +# bash programmable completion for bitcoind(1) and bitcoin-qt(1) +# Copyright (c) 2012-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -have bitcoind && { - -# call $bitcoind for RPC -_bitcoin_rpc() { - # determine already specified args necessary for RPC - local rpcargs=() - for i in ${COMP_LINE}; do - case "$i" in - -conf=*|-proxy*|-rpc*) - rpcargs=( "${rpcargs[@]}" "$i" ) - ;; - esac - done - $bitcoind "${rpcargs[@]}" "$@" -} - -# Add bitcoin accounts to COMPREPLY -_bitcoin_accounts() { - local accounts - accounts=$(_bitcoin_rpc listaccounts | awk '/".*"/ { a=$1; gsub(/"/, "", a); print a}') - COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$accounts" -- "$cur" ) ) -} - -_bitcoind() { +_zcashd() { local cur prev words=() cword local bitcoind - # save and use original argument to invoke bitcoind - # bitcoind might not be in $PATH + # save and use original argument to invoke bitcoind for -help + # it might not be in $PATH bitcoind="$1" COMPREPLY=() _get_comp_words_by_ref -n = cur prev words cword - if ((cword > 4)); then - case ${words[cword-4]} in - listtransactions) - COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) - return 0 - ;; - signrawtransaction) - COMPREPLY=( $( compgen -W "ALL NONE SINGLE ALL|ANYONECANPAY NONE|ANYONECANPAY SINGLE|ANYONECANPAY" -- "$cur" ) ) - return 0 - ;; - esac - fi - - if ((cword > 3)); then - case ${words[cword-3]} in - addmultisigaddress) - _bitcoin_accounts - return 0 - ;; - getbalance|gettxout|importaddress|importprivkey|listreceivedbyaccount|listreceivedbyaddress|listsinceblock) - COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) - return 0 - ;; - esac - fi - - if ((cword > 2)); then - case ${words[cword-2]} in - addnode) - COMPREPLY=( $( compgen -W "add remove onetry" -- "$cur" ) ) - return 0 - ;; - getblock|getrawtransaction|gettransaction|listaccounts|listreceivedbyaccount|listreceivedbyaddress|sendrawtransaction) - COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) - return 0 - ;; - move|setaccount) - _bitcoin_accounts - return 0 - ;; - esac - fi - - case "$prev" in - backupwallet|dumpwallet|importwallet) - _filedir - return 0 - ;; - getmempool|lockunspent|setgenerate) - COMPREPLY=( $( compgen -W "true false" -- "$cur" ) ) - return 0 - ;; - getaccountaddress|getaddressesbyaccount|getbalance|getnewaddress|getreceivedbyaccount|listtransactions|move|sendfrom|sendmany) - _bitcoin_accounts - return 0 - ;; - esac - case "$cur" in - -conf=*|-pid=*|-loadblock=*|-wallet=*|-rpcsslcertificatechainfile=*|-rpcsslprivatekeyfile=*) + -conf=*|-pid=*|-loadblock=*|-rootcertificates=*|-rpccookiefile=*|-wallet=*|-rpcsslcertificatechainfile=*|-rpcsslprivatekeyfile=*) cur="${cur#*=}" _filedir return 0 @@ -110,20 +29,14 @@ _bitcoind() { return 0 ;; *) - local helpopts commands - # only parse --help if senseful + # only parse -help if senseful if [[ -z "$cur" || "$cur" =~ ^- ]]; then - helpopts=$($bitcoind --help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) + local helpopts + helpopts=$($bitcoind -help 2>&1 | awk '$1 ~ /^-/ { sub(/=.*/, "="); print $1 }' ) + COMPREPLY=( $( compgen -W "$helpopts" -- "$cur" ) ) fi - # only parse help if senseful - if [[ -z "$cur" || "$cur" =~ ^[a-z] ]]; then - commands=$(_bitcoin_rpc help 2>/dev/null | awk '$1 ~ /^[a-z]/ { print $1; }') - fi - - COMPREPLY=( $( compgen -W "$helpopts $commands" -- "$cur" ) ) - # Prevent space if an argument is desired if [[ $COMPREPLY == *= ]]; then compopt -o nospace @@ -131,10 +44,8 @@ _bitcoind() { return 0 ;; esac -} - -complete -F _bitcoind bitcoind bitcoin-cli -} +} && +complete -F _zcashd zcashd # Local variables: # mode: shell-script diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 6b83346a744..49c27537da8 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-1.0.3" +name: "zcash-1.0.4" enable_cache: true distro: "debian" suites: diff --git a/contrib/seeds/nodes_main.txt b/contrib/seeds/nodes_main.txt index e69de29bb2d..0096243ea22 100644 --- a/contrib/seeds/nodes_main.txt +++ b/contrib/seeds/nodes_main.txt @@ -0,0 +1,4 @@ +# List of fixed seed nodes for mainnet + +# Onion nodes +q2dmolnsfq6wooor.onion:8133 diff --git a/contrib/seeds/nodes_test.txt b/contrib/seeds/nodes_test.txt index ba45d3b0687..15c386347fb 100644 --- a/contrib/seeds/nodes_test.txt +++ b/contrib/seeds/nodes_test.txt @@ -1,3 +1,4 @@ # List of fixed seed nodes for testnet # Onion nodes +mnsj6hmvtvdwx4xu.onion:18233 diff --git a/depends/packages/miniupnpc.mk b/depends/packages/miniupnpc.mk index 90870ca115b..0d8efbc3f64 100644 --- a/depends/packages/miniupnpc.mk +++ b/depends/packages/miniupnpc.mk @@ -3,6 +3,7 @@ $(package)_version=2.0 $(package)_download_path=http://miniupnp.free.fr/files $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=d434ceb8986efbe199c5ca53f90ed53eab290b1e6d0530b717eb6fa49d61f93b +$(package)_patches=fix-solaris-compilation.patch strlen-before-memcmp.patch patch-strlen-patch.patch define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" @@ -14,7 +15,10 @@ endef define $(package)_preprocess_cmds mkdir dll && \ sed -e 's|MINIUPNPC_VERSION_STRING \"version\"|MINIUPNPC_VERSION_STRING \"$($(package)_version)\"|' -e 's|OS/version|$(host)|' miniupnpcstrings.h.in > miniupnpcstrings.h && \ - sed -i.old "s|miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings|miniupnpcstrings.h: miniupnpcstrings.h.in|" Makefile.mingw + sed -i.old "s|miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings|miniupnpcstrings.h: miniupnpcstrings.h.in|" Makefile.mingw && \ + patch -p2 < $($(package)_patch_dir)/fix-solaris-compilation.patch && \ + patch -p2 < $($(package)_patch_dir)/strlen-before-memcmp.patch && \ + patch -p2 < $($(package)_patch_dir)/patch-strlen-patch.patch endef define $(package)_build_cmds diff --git a/depends/patches/miniupnpc/fix-solaris-compilation.patch b/depends/patches/miniupnpc/fix-solaris-compilation.patch new file mode 100644 index 00000000000..30eb3b106da --- /dev/null +++ b/depends/patches/miniupnpc/fix-solaris-compilation.patch @@ -0,0 +1,42 @@ +From 71ce1d6dfa5424f8fe8633e23494c7638ea2c79e Mon Sep 17 00:00:00 2001 +From: Thomas Bernard +Date: Thu, 10 Nov 2016 21:55:33 +0100 +Subject: [PATCH] fix for Solaris 11 compilation + +see #216 +--- + miniupnpc/Makefile | 2 ++ + miniupnpc/minissdpc.c | 3 +++ + 2 files changed, 5 insertions(+) + +diff --git a/miniupnpc/Makefile b/miniupnpc/Makefile +index 5c23000..72cdc0f 100644 +--- a/miniupnpc/Makefile ++++ b/miniupnpc/Makefile +@@ -43,10 +43,12 @@ CFLAGS += -D_NETBSD_SOURCE + endif + ifneq ($(OS), FreeBSD) + ifneq ($(OS), Darwin) ++ifneq ($(OS), SunOS) + #CFLAGS += -D_POSIX_C_SOURCE=200112L + CFLAGS += -D_XOPEN_SOURCE=600 + endif + endif ++endif + #CFLAGS += -ansi + # -DNO_GETADDRINFO + INSTALL = install +diff --git a/miniupnpc/minissdpc.c b/miniupnpc/minissdpc.c +index f200f07..263160e 100644 +--- a/miniupnpc/minissdpc.c ++++ b/miniupnpc/minissdpc.c +@@ -73,6 +73,9 @@ struct sockaddr_un { + + #if !defined(HAS_IP_MREQN) && !defined(_WIN32) + #include ++#if defined(__sun) ++#include ++#endif + #endif + + #if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN) diff --git a/depends/patches/miniupnpc/patch-strlen-patch.patch b/depends/patches/miniupnpc/patch-strlen-patch.patch new file mode 100644 index 00000000000..df7140234be --- /dev/null +++ b/depends/patches/miniupnpc/patch-strlen-patch.patch @@ -0,0 +1,22 @@ +From 0aa7c46227acd8ddb135c577674ad454bf2fba86 Mon Sep 17 00:00:00 2001 +From: Thomas Bernard +Date: Fri, 11 Nov 2016 17:53:21 +0100 +Subject: [PATCH] remove unsigned/signed comparison + +--- + miniupnpc/portlistingparse.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/miniupnpc/portlistingparse.c b/miniupnpc/portlistingparse.c +index 1bed763..07f3f87 100644 +--- a/miniupnpc/portlistingparse.c ++++ b/miniupnpc/portlistingparse.c +@@ -55,7 +55,7 @@ startelt(void * d, const char * name, int l) + pdata->curelt = PortMappingEltNone; + for(i = 0; elements[i].str; i++) + { +- if(strlen(elements[i].str) == l && memcmp(name, elements[i].str, l) == 0) ++ if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0) + { + pdata->curelt = elements[i].code; + break; diff --git a/depends/patches/miniupnpc/strlen-before-memcmp.patch b/depends/patches/miniupnpc/strlen-before-memcmp.patch new file mode 100644 index 00000000000..8e1f2005e44 --- /dev/null +++ b/depends/patches/miniupnpc/strlen-before-memcmp.patch @@ -0,0 +1,23 @@ +From ec1c49bb0cd5e448e6f0adee7de3a831c4869bdd Mon Sep 17 00:00:00 2001 +From: Thomas Bernard +Date: Fri, 11 Nov 2016 17:24:39 +0100 +Subject: [PATCH] check strlen before memcmp + +1st try to fix #220 +--- + miniupnpc/portlistingparse.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/miniupnpc/portlistingparse.c b/miniupnpc/portlistingparse.c +index 0e09278..1bed763 100644 +--- a/miniupnpc/portlistingparse.c ++++ b/miniupnpc/portlistingparse.c +@@ -55,7 +55,7 @@ startelt(void * d, const char * name, int l) + pdata->curelt = PortMappingEltNone; + for(i = 0; elements[i].str; i++) + { +- if(memcmp(name, elements[i].str, l) == 0) ++ if(strlen(elements[i].str) == l && memcmp(name, elements[i].str, l) == 0) + { + pdata->curelt = elements[i].code; + break; diff --git a/doc/authors.md b/doc/authors.md index c13e6a49950..2933964b58e 100644 --- a/doc/authors.md +++ b/doc/authors.md @@ -1,13 +1,13 @@ Zcash Contributors ================== -Jack Grigg (269) -Simon Liu (199) -Sean Bowe (168) +Jack Grigg (301) +Simon Liu (207) +Sean Bowe (176) Taylor Hornby (65) Daira Hopwood (62) Kevin Gallagher (38) -Jay Graber (32) +Jay Graber (34) Nathan Wilcox (10) Wladimir J. van der Laan (9) Pieter Wuille (8) @@ -15,6 +15,7 @@ Cory Fields (7) ITH4Coinomia (4) David Mercer (4) 4ZEC (4) +lpescher (3) Patrick Strateman (3) Paige Peterson (3) MarcoFalke (3) @@ -27,13 +28,17 @@ kazcw (1) fanquake (1) ayleph (1) Tom Ritter (1) +Scott (1) S. Matthew English (1) Philip Kaufmann (1) Louis Nyffenegger (1) Lars-Magnus Skog (1) +Jeffrey Walton (1) Gaurav Rana (1) Ethan Heilman (1) +Christian von Roques (1) Chirag Davé (1) Cameron Boehmer (1) Bryan Stitt (1) +Bitcoin Error Log (1) Alex (1) diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 9cac2746713..cf836dfbdab 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -102,7 +102,7 @@ CXXFLAGS="-g -ggdb -O0" or whatever debug flags you need. If the code is behaving strangely, take a look in the debug.log file in the data directory; error and debugging messages are written there. -The -debug=... command-line option controls debugging; running with just -debug will turn +The -debug=... command-line option controls debugging; running with just -debug or -debug=1 will turn on all categories (and give you a very large debug.log file). The Qt code routes qDebug() output to debug.log under category "qt": run with -debug=qt diff --git a/doc/payment-api.md b/doc/payment-api.md index 7435acf81eb..3560add649e 100644 --- a/doc/payment-api.md +++ b/doc/payment-api.md @@ -71,7 +71,7 @@ z_importwallet | filename | _Requires an unlocked wallet or an unencrypted walle Command | Parameters | Description --- | --- | --- z_listreceivedbyaddress
| zaddr [minconf=1] | Return a list of amounts received by a zaddr belonging to the node’s wallet.

Optionally set the minimum number of confirmations which a received amount must have in order to be included in the result. Use 0 to count unconfirmed transactions.

Output:
[{
“txid”: “4a0f…”,
“amount”: 0.54,
“memo”:”F0FF…”,}, {...}, {...}
] -z_sendmany
| fromaddress amounts [minconf=1] | _This is an Asynchronous RPC call_

Send funds from an address to multiple outputs. The address can be either a taddr or a zaddr.

Amounts is a list containing key/value pairs corresponding to the addresses and amount to pay. Each output address can be in taddr or zaddr format.

When sending to a zaddr, you also have the option of attaching a memo in hexadecimal format.

**NOTE:**When sending coinbase funds to a zaddr, the node's wallet does not allow any change. Put another way, spending a partial amount of a coinbase utxo is not allowed. This is not a consensus rule but a local wallet rule due to the current implementation of z_sendmany. In future, this rule may be removed.

Example of Outputs parameter:
[{“address”:”t123…”, “amount”:0.005},
,{“address”:”z010…”,”amount”:0.03, “memo”:”f508af…”}]

Optionally set the minimum number of confirmations which a private or transparent transaction must have in order to be used as an input.

The transaction fee will be determined by the node’s wallet. Any transparent change will be sent to a new transparent address. Any private change will be sent back to the zaddr being used as the source of funds.

Returns an operationid. You use the operationid value with z_getoperationstatus and z_getoperationresult to obtain the result of sending funds, which if successful, will be a txid. +z_sendmany
| fromaddress amounts [minconf=1] [fee=0.0001] | _This is an Asynchronous RPC call_

Send funds from an address to multiple outputs. The address can be either a taddr or a zaddr.

Amounts is a list containing key/value pairs corresponding to the addresses and amount to pay. Each output address can be in taddr or zaddr format.

When sending to a zaddr, you also have the option of attaching a memo in hexadecimal format.

**NOTE:**When sending coinbase funds to a zaddr, the node's wallet does not allow any change. Put another way, spending a partial amount of a coinbase utxo is not allowed. This is not a consensus rule but a local wallet rule due to the current implementation of z_sendmany. In future, this rule may be removed.

Example of Outputs parameter:
[{“address”:”t123…”, “amount”:0.005},
,{“address”:”z010…”,”amount”:0.03, “memo”:”f508af…”}]

Optionally set the minimum number of confirmations which a private or transparent transaction must have in order to be used as an input.

Optionally set a transaction fee, which by default is 0.0001 ZEC.

Any transparent change will be sent to a new transparent address. Any private change will be sent back to the zaddr being used as the source of funds.

Returns an operationid. You use the operationid value with z_getoperationstatus and z_getoperationresult to obtain the result of sending funds, which if successful, will be a txid. ### Operations @@ -151,7 +151,7 @@ RPC_WALLET_ERROR (-4) | _Unspecified problem with wallet_ ----------------------| ------------------------------------- "Could not find previous JoinSplit anchor" | Try restarting node with `-reindex`. "Error decrypting output note of previous JoinSplit: __" | -"Could not find witness for note commitment" | Try restarting node with `-reindex`. +"Could not find witness for note commitment" | Try restarting node with `-rescan`. "Witness for note commitment is null" | Missing witness for note commitement. "Witness for spendable note does not have same anchor as change input" | Invalid anchor for spendable note witness. "Not enough funds to pay miners fee" | Retry with sufficient funds. diff --git a/doc/release-notes/release-notes-1.0.4.md b/doc/release-notes/release-notes-1.0.4.md new file mode 100644 index 00000000000..1cb73c3930c --- /dev/null +++ b/doc/release-notes/release-notes-1.0.4.md @@ -0,0 +1,75 @@ +Bitcoin Error Log (1): + Edit for grammar: "block chain" + +Christian von Roques (1): + bash-completion: Adapt for 0.12 and 0.13 + +Jack Grigg (32): + Add getlocalsolps and getnetworksolps RPC calls, show them in getmininginfo + Add benchmark for attempting decryption of notes + Add benchmark for incrementing note witnesses + Add -metricsui flag to toggle between persistent screen and rolling metrics + Add -metricsrefreshtime option + Only show metrics by default if stdout is a TTY + Document metrics screen options + Clarify that metrics options are only useful without -daemon and -printtoconsole + Increase length of metrics divider + Write witness caches when writing the best block + Apply miniupnpc patches to enable compilation on Solaris 11 + Add an upstream miniupnpc patch revision + Address review comments, tweak strings + Change function names to not clash with Bitcoin, apply to correct binaries + Add bash completion files to Debian package + Always bash-complete the default account + Add Zcash RPC commands to CLI argument completion + Document behaviour of CWallet::SetBestChain + Fix indentation + Generate JS for trydecryptnotes, make number of addresses a variable + Add JS to second block to ensure witnesses are incremented + Skip JoinSplit verification before the last checkpoint + Add a reindex test that fails because of a bug in decrementing witness caches + Make the test pass by fixing the bug! + Only check cache validity for witnesses being incremented or decremented + Fix bug in wallet tests + Extract block-generation wallet test code into a function + Rewrite reindex test to check beyond the max witness cache size + Fix bug in IncrementNoteWitness() + Update payment API docs to recommend -rescan for fixing witness errors + Update version to 1.0.4 + Update man pages + +Jay Graber (2): + Replace bitcoin with zcash in rpcprotocol.cpp + Gather release notes from previous release to HEAD + +Jeffrey Walton (1): + Add porter dev overrides for CC, CXX, MAKE, BUILD, HOST + +Scott (1): + Metrics - Don't exclaim unless > 1 + +Sean Bowe (8): + Isolate verification to a `ProofVerifier` context object that allows verification behavior to be tuned by the caller. + Regression test. + Ensure cache contains valid entry when anchor is popped. + Ensure ProofVerifier cannot be accidentally copied. + Rename Dummy to Disabled. + Add more tests for ProofVerifier. + ASSERT_TRUE -> ASSERT_FALSE + Check that E' points are actually in G2 by ensuring they are of order r. + +Simon Liu (8): + Fix stale comment referencing upstream block interval + Add checkpoint at block height 15000 + Closes #1857. Fixes bug where tx spending only notes had priority of 0. + Closes #1901. Increase default settings for the max block size when mining and the amount of space available for priority transactions. + Closes #1903. Add fee parameter to z_sendmany. + Fixes #1823. Witness anchors for input notes no longer cross block boundaries. + Increase timeout as laptops on battery power have cpu throttling. + WitnessAnchorData only needs to store one witness per JSOutPoint. + +lpescher (3): + Make command line option to show all debugging consistent with similar options + Update documentation to match the #4219 change + Update help message to match the #4219 change + diff --git a/doc/release-process.md b/doc/release-process.md index aded05e0f6c..93b97adaf26 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -59,10 +59,9 @@ In `configure.ac` and `clientversion.h`: ### B2. Write release notes -git shortlog helps a lot, for example: +Run the release-notes.py script to generate release notes and update authors.md file. For example: - $ git shortlog --no-merges v${ZCASH_RELEASE_PREV}..HEAD \ - > ./doc/release-notes/release-notes-${ZCASH_RELEASE}.md + $ python zcutil/release-notes.py --version $ZCASH_RELEASE Update the Debian package changelog: diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 13ddc461f28..821a637cd94 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -11,6 +11,7 @@ export BITCOIND=${REAL_BITCOIND} #Run the tests testScripts=( + 'wallet_treestate.py' 'wallet_protectcoinbase.py' 'wallet.py' 'wallet_nullifiers.py' diff --git a/qa/rpc-tests/wallet_protectcoinbase.py b/qa/rpc-tests/wallet_protectcoinbase.py index d072c8ce619..5f0c285c048 100755 --- a/qa/rpc-tests/wallet_protectcoinbase.py +++ b/qa/rpc-tests/wallet_protectcoinbase.py @@ -23,13 +23,15 @@ def setup_network(self, split=False): self.is_network_split=False self.sync_all() + # Returns txid if operation was a success or None def wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): print('waiting for async operation {}'.format(myopid)) opids = [] opids.append(myopid) - timeout = 120 + timeout = 300 status = None errormsg = None + txid = None for x in xrange(1, timeout): results = self.nodes[0].z_getoperationresult(opids) if len(results)==0: @@ -38,6 +40,8 @@ def wait_and_assert_operationid_status(self, myopid, in_status='success', in_err status = results[0]["status"] if status == "failed": errormsg = results[0]['error']['message'] + elif status == "success": + txid = results[0]['result']['txid'] break print('...returned status: {}'.format(status)) assert_equal(in_status, status) @@ -45,6 +49,7 @@ def wait_and_assert_operationid_status(self, myopid, in_status='success', in_err assert(in_errormsg is not None) assert_equal(in_errormsg in errormsg, True) print('...returned error: {}'.format(errormsg)) + return txid def run_test (self): print "Mining blocks..." @@ -116,8 +121,14 @@ def run_test (self): recipients = [] recipients.append({"address":mytaddr, "amount":Decimal('10.0')}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients) - self.wait_and_assert_operationid_status(myopid) + mytxid = self.wait_and_assert_operationid_status(myopid) + assert(mytxid is not None) self.sync_all() + + # check that priority of the tx sending from a zaddr is not 0 + mempool = self.nodes[0].getrawmempool(True) + assert(Decimal(mempool[mytxid]['startingpriority']) >= Decimal('1000000000000')) + self.nodes[1].generate(1) self.sync_all() @@ -190,6 +201,27 @@ def run_test (self): node2balance = amount_per_recipient * num_t_recipients assert_equal(self.nodes[2].getbalance(), node2balance) + # Send will fail because fee is negative + try: + self.nodes[0].z_sendmany(myzaddr, recipients, 1, -1) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Invalid amount" in errorString, True) + + # Send will fail because fee is larger than MAX_MONEY + try: + self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('21000000.00000001')) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("Invalid amount" in errorString, True) + + # Send will fail because fee is larger than sum of outputs + try: + self.nodes[0].z_sendmany(myzaddr, recipients, 1, (amount_per_recipient * num_t_recipients) + Decimal('0.00000001')) + except JSONRPCException,e: + errorString = e.error['message'] + assert_equal("is greater than the sum of outputs" in errorString, True) + # Send will succeed because the balance of non-coinbase utxos is 10.0 try: self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 9) @@ -208,10 +240,14 @@ def run_test (self): recipients = [] num_recipients = 3 amount_per_recipient = Decimal('0.002') + minconf = 1 + send_amount = num_recipients * amount_per_recipient + custom_fee = Decimal('0.00012345') + zbalance = self.nodes[0].z_getbalance(myzaddr) for i in xrange(0,num_recipients): newzaddr = self.nodes[2].z_getnewaddress() recipients.append({"address":newzaddr, "amount":amount_per_recipient}) - myopid = self.nodes[0].z_sendmany(myzaddr, recipients) + myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, custom_fee) self.wait_and_assert_operationid_status(myopid) self.sync_all() self.nodes[1].generate(1) @@ -219,7 +255,9 @@ def run_test (self): # check balances resp = self.nodes[2].z_gettotalbalance() - assert_equal(Decimal(resp["private"]), num_recipients * amount_per_recipient) + assert_equal(Decimal(resp["private"]), send_amount) + resp = self.nodes[0].z_getbalance(myzaddr) + assert_equal(Decimal(resp), zbalance - custom_fee - send_amount) if __name__ == '__main__': WalletProtectCoinbaseTest().main() diff --git a/qa/rpc-tests/wallet_treestate.py b/qa/rpc-tests/wallet_treestate.py new file mode 100755 index 00000000000..ae55368f0ca --- /dev/null +++ b/qa/rpc-tests/wallet_treestate.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python2 +# Copyright (c) 2016 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from time import * + +import sys + +class WalletTreeStateTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 4) + + # Start nodes with -regtestprotectcoinbase to set fCoinbaseMustBeProtected to true. + def setup_network(self, split=False): + self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[['-regtestprotectcoinbase','-debug=zrpc']] * 3 ) + 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 wait_and_assert_operationid_status(self, myopid, in_status='success', in_errormsg=None): + print('waiting for async operation {}'.format(myopid)) + opids = [] + opids.append(myopid) + timeout = 300 + status = None + errormsg = None + for x in xrange(1, timeout): + results = self.nodes[0].z_getoperationresult(opids) + if len(results)==0: + sleep(1) + else: + status = results[0]["status"] + if status == "failed": + errormsg = results[0]['error']['message'] + break + print('...returned status: {}'.format(status)) + print('...error msg: {}'.format(errormsg)) + assert_equal(in_status, status) + if errormsg is not None: + assert(in_errormsg is not None) + assert_equal(in_errormsg in errormsg, True) + print('...returned error: {}'.format(errormsg)) + + def run_test (self): + print "Mining blocks..." + + self.nodes[0].generate(100) + self.sync_all() + self.nodes[1].generate(101) + self.sync_all() + + mytaddr = self.nodes[0].getnewaddress() # where coins were mined + myzaddr = self.nodes[0].z_getnewaddress() + + # Spend coinbase utxos to create three notes of 9.99990000 each + recipients = [] + recipients.append({"address":myzaddr, "amount":Decimal('10.0') - Decimal('0.0001')}) + myopid = self.nodes[0].z_sendmany(mytaddr, recipients) + self.wait_and_assert_operationid_status(myopid) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + myopid = self.nodes[0].z_sendmany(mytaddr, recipients) + self.wait_and_assert_operationid_status(myopid) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + myopid = self.nodes[0].z_sendmany(mytaddr, recipients) + self.wait_and_assert_operationid_status(myopid) + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # Check balance + resp = self.nodes[0].z_getbalance(myzaddr) + assert_equal(Decimal(resp), Decimal('9.9999') * 3 ) + + # We want to test a real-world situation where during the time spent creating a transaction + # with joinsplits, other transactions containing joinsplits have been mined into new blocks, + # which result in the treestate changing whilst creating the transaction. + + # Tx 1 will change the treestate while Tx 2 containing chained joinsplits is still being generated + recipients = [] + recipients.append({"address":self.nodes[2].z_getnewaddress(), "amount":Decimal('10.0') - Decimal('0.0001')}) + myopid = self.nodes[0].z_sendmany(mytaddr, recipients) + self.wait_and_assert_operationid_status(myopid) + + # Tx 2 will consume all three notes, which must take at least two joinsplits. This is regardless of + # the z_sendmany implementation because there are only two inputs per joinsplit. + recipients = [] + recipients.append({"address":self.nodes[2].z_getnewaddress(), "amount":Decimal('18')}) + recipients.append({"address":self.nodes[2].z_getnewaddress(), "amount":Decimal('11.9997') - Decimal('0.0001')}) + myopid = self.nodes[0].z_sendmany(myzaddr, recipients) + + # Wait for Tx 2 to begin executing... + for x in xrange(1, 60): + results = self.nodes[0].z_getoperationstatus([myopid]) + status = results[0]["status"] + if status == "executing": + break + sleep(1) + + # Now mine Tx 1 which will change global treestate before Tx 2's second joinsplit begins processing + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # Wait for Tx 2 to be created + self.wait_and_assert_operationid_status(myopid) + + # Note that a bug existed in v1.0.0-1.0.3 where Tx 2 creation would fail with an error: + # "Witness for spendable note does not have same anchor as change input" + + # Check balance + resp = self.nodes[0].z_getbalance(myzaddr) + assert_equal(Decimal(resp), Decimal('0.0')) + + +if __name__ == '__main__': + WalletTreeStateTest().main() diff --git a/qa/zcash/performance-measurements.sh b/qa/zcash/performance-measurements.sh index 2e99ab2d6cc..851adef09ff 100755 --- a/qa/zcash/performance-measurements.sh +++ b/qa/zcash/performance-measurements.sh @@ -88,6 +88,12 @@ case "$1" in validatelargetx) zcash_rpc zcbenchmark validatelargetx 5 ;; + trydecryptnotes) + zcash_rpc zcbenchmark trydecryptnotes 1000 "${@:3}" + ;; + incnotewitnesses) + zcash_rpc zcbenchmark incnotewitnesses 100 "${@:3}" + ;; *) zcashd_stop echo "Bad arguments." @@ -116,6 +122,12 @@ case "$1" in verifyequihash) zcash_rpc zcbenchmark verifyequihash 1 ;; + trydecryptnotes) + zcash_rpc zcbenchmark trydecryptnotes 1 "${@:3}" + ;; + incnotewitnesses) + zcash_rpc zcbenchmark incnotewitnesses 1 "${@:3}" + ;; *) zcashd_massif_stop echo "Bad arguments." @@ -145,6 +157,12 @@ case "$1" in verifyequihash) zcash_rpc zcbenchmark verifyequihash 1 ;; + trydecryptnotes) + zcash_rpc zcbenchmark trydecryptnotes 1 "${@:3}" + ;; + incnotewitnesses) + zcash_rpc zcbenchmark incnotewitnesses 1 "${@:3}" + ;; *) zcashd_valgrind_stop echo "Bad arguments." diff --git a/src/Makefile.am b/src/Makefile.am index 9da92a9bc54..8f809c4907d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -228,6 +228,8 @@ libbitcoin_server_a_SOURCES = \ # when wallet enabled libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_wallet_a_SOURCES = \ + utiltest.cpp \ + utiltest.h \ zcbenchmarks.cpp \ zcbenchmarks.h \ wallet/asyncrpcoperation_sendmany.cpp \ diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index a66fa9594bf..4db0fe48357 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -18,6 +18,7 @@ zcash_gtest_SOURCES = \ gtest/test_keystore.cpp \ gtest/test_noteencryption.cpp \ gtest/test_merkletree.cpp \ + gtest/test_metrics.cpp \ gtest/test_pow.cpp \ gtest/test_random.cpp \ gtest/test_rpc.cpp \ @@ -26,8 +27,11 @@ zcash_gtest_SOURCES = \ gtest/test_txid.cpp \ gtest/test_libzcash_utils.cpp \ gtest/test_proofs.cpp \ - gtest/test_checkblock.cpp \ + gtest/test_checkblock.cpp +if ENABLE_WALLET +zcash_gtest_SOURCES += \ wallet/gtest/test_wallet.cpp +endif zcash_gtest_CPPFLAGS = -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC diff --git a/src/chainparams.cpp b/src/chainparams.cpp index bc3eea92fb4..ba666d20731 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -96,7 +96,8 @@ class CMainParams : public CChainParams { vFixedSeeds.clear(); vSeeds.clear(); - vSeeds.push_back(CDNSSeedData("zclassic.org", "dnsseed.zclassic.org")); // zclassic + //vSeeds.push_back(CDNSSeedData("zclassic.org", "dnsseed.zclassic.org")); // zclassic + vSeeds.push_back(CDNSSeedData("indieonion.org", "dnsseed.indieonion.org")); // @IndieOnion vSeeds.push_back(CDNSSeedData("rotorproject.org", "dnsseed.rotorproject.org")); // @IndieOnion // guarantees the first 2 characters, when base58 encoded, are "t1" @@ -133,61 +134,61 @@ class CMainParams : public CChainParams { // Founders reward script expects a vector of 2-of-3 multisig addresses vFoundersRewardAddress = { - "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", /* main-index: 0*/ - "t3cL9AucCajm3HXDhb5jBnJK2vapVoXsop3", /* main-index: 1*/ - "t3fqvkzrrNaMcamkQMwAyHRjfDdM2xQvDTR", /* main-index: 2*/ - "t3TgZ9ZT2CTSK44AnUPi6qeNaHa2eC7pUyF", /* main-index: 3*/ - "t3SpkcPQPfuRYHsP5vz3Pv86PgKo5m9KVmx", /* main-index: 4*/ - "t3Xt4oQMRPagwbpQqkgAViQgtST4VoSWR6S", /* main-index: 5*/ - "t3ayBkZ4w6kKXynwoHZFUSSgXRKtogTXNgb", /* main-index: 6*/ - "t3adJBQuaa21u7NxbR8YMzp3km3TbSZ4MGB", /* main-index: 7*/ - "t3K4aLYagSSBySdrfAGGeUd5H9z5Qvz88t2", /* main-index: 8*/ - "t3RYnsc5nhEvKiva3ZPhfRSk7eyh1CrA6Rk", /* main-index: 9*/ - "t3Ut4KUq2ZSMTPNE67pBU5LqYCi2q36KpXQ", /* main-index: 10*/ - "t3ZnCNAvgu6CSyHm1vWtrx3aiN98dSAGpnD", /* main-index: 11*/ - "t3fB9cB3eSYim64BS9xfwAHQUKLgQQroBDG", /* main-index: 12*/ - "t3cwZfKNNj2vXMAHBQeewm6pXhKFdhk18kD", /* main-index: 13*/ - "t3YcoujXfspWy7rbNUsGKxFEWZqNstGpeG4", /* main-index: 14*/ - "t3bLvCLigc6rbNrUTS5NwkgyVrZcZumTRa4", /* main-index: 15*/ - "t3VvHWa7r3oy67YtU4LZKGCWa2J6eGHvShi", /* main-index: 16*/ - "t3eF9X6X2dSo7MCvTjfZEzwWrVzquxRLNeY", /* main-index: 17*/ - "t3esCNwwmcyc8i9qQfyTbYhTqmYXZ9AwK3X", /* main-index: 18*/ - "t3M4jN7hYE2e27yLsuQPPjuVek81WV3VbBj", /* main-index: 19*/ - "t3gGWxdC67CYNoBbPjNvrrWLAWxPqZLxrVY", /* main-index: 20*/ - "t3LTWeoxeWPbmdkUD3NWBquk4WkazhFBmvU", /* main-index: 21*/ - "t3P5KKX97gXYFSaSjJPiruQEX84yF5z3Tjq", /* main-index: 22*/ - "t3f3T3nCWsEpzmD35VK62JgQfFig74dV8C9", /* main-index: 23*/ - "t3Rqonuzz7afkF7156ZA4vi4iimRSEn41hj", /* main-index: 24*/ - "t3fJZ5jYsyxDtvNrWBeoMbvJaQCj4JJgbgX", /* main-index: 25*/ - "t3Pnbg7XjP7FGPBUuz75H65aczphHgkpoJW", /* main-index: 26*/ - "t3WeKQDxCijL5X7rwFem1MTL9ZwVJkUFhpF", /* main-index: 27*/ - "t3Y9FNi26J7UtAUC4moaETLbMo8KS1Be6ME", /* main-index: 28*/ - "t3aNRLLsL2y8xcjPheZZwFy3Pcv7CsTwBec", /* main-index: 29*/ - "t3gQDEavk5VzAAHK8TrQu2BWDLxEiF1unBm", /* main-index: 30*/ - "t3Rbykhx1TUFrgXrmBYrAJe2STxRKFL7G9r", /* main-index: 31*/ - "t3aaW4aTdP7a8d1VTE1Bod2yhbeggHgMajR", /* main-index: 32*/ - "t3YEiAa6uEjXwFL2v5ztU1fn3yKgzMQqNyo", /* main-index: 33*/ - "t3g1yUUwt2PbmDvMDevTCPWUcbDatL2iQGP", /* main-index: 34*/ - "t3dPWnep6YqGPuY1CecgbeZrY9iUwH8Yd4z", /* main-index: 35*/ - "t3QRZXHDPh2hwU46iQs2776kRuuWfwFp4dV", /* main-index: 36*/ - "t3enhACRxi1ZD7e8ePomVGKn7wp7N9fFJ3r", /* main-index: 37*/ - "t3PkLgT71TnF112nSwBToXsD77yNbx2gJJY", /* main-index: 38*/ - "t3LQtHUDoe7ZhhvddRv4vnaoNAhCr2f4oFN", /* main-index: 39*/ - "t3fNcdBUbycvbCtsD2n9q3LuxG7jVPvFB8L", /* main-index: 40*/ - "t3dKojUU2EMjs28nHV84TvkVEUDu1M1FaEx", /* main-index: 41*/ - "t3aKH6NiWN1ofGd8c19rZiqgYpkJ3n679ME", /* main-index: 42*/ - "t3MEXDF9Wsi63KwpPuQdD6by32Mw2bNTbEa", /* main-index: 43*/ - "t3WDhPfik343yNmPTqtkZAoQZeqA83K7Y3f", /* main-index: 44*/ - "t3PSn5TbMMAEw7Eu36DYctFezRzpX1hzf3M", /* main-index: 45*/ - "t3R3Y5vnBLrEn8L6wFjPjBLnxSUQsKnmFpv", /* main-index: 46*/ - "t3Pcm737EsVkGTbhsu2NekKtJeG92mvYyoN", /* main-index: 47*/ -// "t3PZ9PPcLzgL57XRSG5ND4WNBC9UTFb8DXv", /* main-index: 48*/ -// "t3L1WgcyQ95vtpSgjHfgANHyVYvffJZ9iGb", /* main-index: 49*/ -// "t3JtoXqsv3FuS7SznYCd5pZJGU9di15mdd7", /* main-index: 50*/ -// "t3hLJHrHs3ytDgExxr1mD8DYSrk1TowGV25", /* main-index: 51*/ -// "t3fmYHU2DnVaQgPhDs6TMFVmyC3qbWEWgXN", /* main-index: 52*/ -// "t3T4WmAp6nrLkJ24iPpGeCe1fSWTPv47ASG", /* main-index: 53*/ -// "t3fP6GrDM4QVwdjFhmCxGNbe7jXXXSDQ5dv", /* main-index: 54*/ +// "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", /* main-index: 0*/ +// "t3cL9AucCajm3HXDhb5jBnJK2vapVoXsop3", /* main-index: 1*/ +// "t3fqvkzrrNaMcamkQMwAyHRjfDdM2xQvDTR", /* main-index: 2*/ +// "t3TgZ9ZT2CTSK44AnUPi6qeNaHa2eC7pUyF", /* main-index: 3*/ +// "t3SpkcPQPfuRYHsP5vz3Pv86PgKo5m9KVmx", /* main-index: 4*/ +// "t3Xt4oQMRPagwbpQqkgAViQgtST4VoSWR6S", /* main-index: 5*/ +// "t3ayBkZ4w6kKXynwoHZFUSSgXRKtogTXNgb", /* main-index: 6*/ +// "t3adJBQuaa21u7NxbR8YMzp3km3TbSZ4MGB", /* main-index: 7*/ +// "t3K4aLYagSSBySdrfAGGeUd5H9z5Qvz88t2", /* main-index: 8*/ +// "t3RYnsc5nhEvKiva3ZPhfRSk7eyh1CrA6Rk", /* main-index: 9*/ +// "t3Ut4KUq2ZSMTPNE67pBU5LqYCi2q36KpXQ", /* main-index: 10*/ +// "t3ZnCNAvgu6CSyHm1vWtrx3aiN98dSAGpnD", /* main-index: 11*/ +// "t3fB9cB3eSYim64BS9xfwAHQUKLgQQroBDG", /* main-index: 12*/ +// "t3cwZfKNNj2vXMAHBQeewm6pXhKFdhk18kD", /* main-index: 13*/ +// "t3YcoujXfspWy7rbNUsGKxFEWZqNstGpeG4", /* main-index: 14*/ +// "t3bLvCLigc6rbNrUTS5NwkgyVrZcZumTRa4", /* main-index: 15*/ +// "t3VvHWa7r3oy67YtU4LZKGCWa2J6eGHvShi", /* main-index: 16*/ +// "t3eF9X6X2dSo7MCvTjfZEzwWrVzquxRLNeY", /* main-index: 17*/ +// "t3esCNwwmcyc8i9qQfyTbYhTqmYXZ9AwK3X", /* main-index: 18*/ +// "t3M4jN7hYE2e27yLsuQPPjuVek81WV3VbBj", /* main-index: 19*/ +// "t3gGWxdC67CYNoBbPjNvrrWLAWxPqZLxrVY", /* main-index: 20*/ +// "t3LTWeoxeWPbmdkUD3NWBquk4WkazhFBmvU", /* main-index: 21*/ +// "t3P5KKX97gXYFSaSjJPiruQEX84yF5z3Tjq", /* main-index: 22*/ +// "t3f3T3nCWsEpzmD35VK62JgQfFig74dV8C9", /* main-index: 23*/ +// "t3Rqonuzz7afkF7156ZA4vi4iimRSEn41hj", /* main-index: 24*/ +// "t3fJZ5jYsyxDtvNrWBeoMbvJaQCj4JJgbgX", /* main-index: 25*/ +// "t3Pnbg7XjP7FGPBUuz75H65aczphHgkpoJW", /* main-index: 26*/ +// "t3WeKQDxCijL5X7rwFem1MTL9ZwVJkUFhpF", /* main-index: 27*/ +// "t3Y9FNi26J7UtAUC4moaETLbMo8KS1Be6ME", /* main-index: 28*/ +// "t3aNRLLsL2y8xcjPheZZwFy3Pcv7CsTwBec", /* main-index: 29*/ +// "t3gQDEavk5VzAAHK8TrQu2BWDLxEiF1unBm", /* main-index: 30*/ +// "t3Rbykhx1TUFrgXrmBYrAJe2STxRKFL7G9r", /* main-index: 31*/ +// "t3aaW4aTdP7a8d1VTE1Bod2yhbeggHgMajR", /* main-index: 32*/ +// "t3YEiAa6uEjXwFL2v5ztU1fn3yKgzMQqNyo", /* main-index: 33*/ +// "t3g1yUUwt2PbmDvMDevTCPWUcbDatL2iQGP", /* main-index: 34*/ +// "t3dPWnep6YqGPuY1CecgbeZrY9iUwH8Yd4z", /* main-index: 35*/ +// "t3QRZXHDPh2hwU46iQs2776kRuuWfwFp4dV", /* main-index: 36*/ +// "t3enhACRxi1ZD7e8ePomVGKn7wp7N9fFJ3r", /* main-index: 37*/ +// "t3PkLgT71TnF112nSwBToXsD77yNbx2gJJY", /* main-index: 38*/ +// "t3LQtHUDoe7ZhhvddRv4vnaoNAhCr2f4oFN", /* main-index: 39*/ +// "t3fNcdBUbycvbCtsD2n9q3LuxG7jVPvFB8L", /* main-index: 40*/ +// "t3dKojUU2EMjs28nHV84TvkVEUDu1M1FaEx", /* main-index: 41*/ +// "t3aKH6NiWN1ofGd8c19rZiqgYpkJ3n679ME", /* main-index: 42*/ +// "t3MEXDF9Wsi63KwpPuQdD6by32Mw2bNTbEa", /* main-index: 43*/ +// "t3WDhPfik343yNmPTqtkZAoQZeqA83K7Y3f", /* main-index: 44*/ +// "t3PSn5TbMMAEw7Eu36DYctFezRzpX1hzf3M", /* main-index: 45*/ +// "t3R3Y5vnBLrEn8L6wFjPjBLnxSUQsKnmFpv", /* main-index: 46*/ +// "t3Pcm737EsVkGTbhsu2NekKtJeG92mvYyoN", /* main-index: 47*/ +//// "t3PZ9PPcLzgL57XRSG5ND4WNBC9UTFb8DXv", /* main-index: 48*/ +//// "t3L1WgcyQ95vtpSgjHfgANHyVYvffJZ9iGb", /* main-index: 49*/ +//// "t3JtoXqsv3FuS7SznYCd5pZJGU9di15mdd7", /* main-index: 50*/ +//// "t3hLJHrHs3ytDgExxr1mD8DYSrk1TowGV25", /* main-index: 51*/ +//// "t3fmYHU2DnVaQgPhDs6TMFVmyC3qbWEWgXN", /* main-index: 52*/ +//// "t3T4WmAp6nrLkJ24iPpGeCe1fSWTPv47ASG", /* main-index: 53*/ +//// "t3fP6GrDM4QVwdjFhmCxGNbe7jXXXSDQ5dv", /* main-index: 54*/ }; assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight()); } @@ -261,6 +262,7 @@ class CTestNetParams : public CMainParams { // Founders reward script expects a vector of 2-of-3 multisig addresses vFoundersRewardAddress = { + /* "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", "t2N9PH9Wk9xjqYg9iin1Ua3aekJqfAtE543", "t2NGQjYMQhFndDHguvUw4wZdNdsssA6K7x2", "t27ktmq1kbeCWiQ5TZ7w5npSzcdbBmTB7v6", "t2GcBttAKD2WTHka8HyGc2dfvVTKYZUfHmJ", "t2Q3vxWaD9LrdqUE8Xd9Ddjpr9pUQ2aGotK", "t2TTfWDsYu998fHWzVP9Gns4fgxXXRi1Wzu", "t2KS6R4MMWdSBMjLCiw2iMyhWGRQPmyRqDn", "t2Q2ELrgotWv3Eec6LEtMMiiQ8dtW38u8Tj", "t2AEgJA88vTWAKqxJDFUEJWyHUtQAZi5G1D", "t2HCSdmpq1TQKksuwPQevwAzPTgfJ2rkMbG", "t2HQCPFAUQaUdJWHPhg5pPBxit7inaJzubE", @@ -273,6 +275,7 @@ class CTestNetParams : public CMainParams { "t2DN7X6wDFn5hYKBiBmn3Z98st419yaTVTH", "t2QJj8HeCwQ6mHwqekxxDLZntYpZTHNU62t", "t2QdHBR1Yciqn4j8gpS8DcQZZtYetKvfNj3", "t2E5cpLA1ey5VNxFNcuopeQMq2rH2NHiPdu", "t2EVRGtzjFAyz8CF8ndvLuiJu7qZUfDa93H", "t2KoQDk3BSFadBkuaWdLwchFuQamzw9RE4L", "t2FnR3yhTmuiejEJeu6qpidWTghRd1HpjLt", "t2BAuBAAospDc9d1u5nNGEi6x4NRJBD2PQ2", "t2RtKrLCGcyPkm4a4APg1YY9Wu2m4R2PgrB", "t28aUbSteZzBq2pFgj1K1XNZRZP5mMMyakV", "t2Urdy1ERfkvsFuy6Z4BkhvYGzWdmivfAFR", "t2ADinR4JrvCMd4Q1XGALPajzFrirqvhED6", + */ }; assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight()); } @@ -335,7 +338,8 @@ class CRegTestParams : public CTestNetParams { }; // Founders reward script expects a vector of 2-of-3 multisig addresses - vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" }; + //vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" }; + vFoundersRewardAddress = { }; assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight()); } }; diff --git a/src/clientversion.h b/src/clientversion.h index 41de198372e..6ca038285ed 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -16,7 +16,7 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 1 #define CLIENT_VERSION_MINOR 0 -#define CLIENT_VERSION_REVISION 3 +#define CLIENT_VERSION_REVISION 4 #define CLIENT_VERSION_BUILD 50 //! Set to true for release, false for prerelease or test build diff --git a/src/coins.cpp b/src/coins.cpp index c861bb81eb6..6a6855bd6cc 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -6,6 +6,7 @@ #include "memusage.h" #include "random.h" +#include "version.h" #include @@ -176,11 +177,20 @@ void CCoinsViewCache::PopAnchor(const uint256 &newrt) { // case restoring the "old" anchor during a reorg must // have no effect. if (currentRoot != newrt) { - CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(currentRoot, CAnchorsCacheEntry())).first; + // Bring the current best anchor into our local cache + // so that its tree exists in memory. + { + ZCIncrementalMerkleTree tree; + assert(GetAnchorAt(currentRoot, tree)); + } - ret->second.entered = false; - ret->second.flags = CAnchorsCacheEntry::DIRTY; + // Mark the anchor as unentered, removing it from view + cacheAnchors[currentRoot].entered = false; + // Mark the cache entry as dirty so it's propagated + cacheAnchors[currentRoot].flags = CAnchorsCacheEntry::DIRTY; + + // Mark the new root as the best anchor hashAnchor = newrt; } } @@ -433,6 +443,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const { if (tx.IsCoinBase()) return 0.0; + CAmount nTotalIn = 0; double dResult = 0.0; BOOST_FOREACH(const CTxIn& txin, tx.vin) { @@ -441,8 +452,34 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const if (!coins->IsAvailable(txin.prevout.n)) continue; if (coins->nHeight < nHeight) { dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight); + nTotalIn += coins->vout[txin.prevout.n].nValue; } } + + // If a transaction contains a joinsplit, we boost the priority of the transaction. + // Joinsplits do not reveal any information about the value or age of a note, so we + // cannot apply the priority algorithm used for transparent utxos. Instead, we pick a + // very large number and multiply it by the transaction's fee per 1000 bytes of data. + // One trillion, 1000000000000, is equivalent to 1 ZEC utxo * 10000 blocks (~17 days). + if (tx.vjoinsplit.size() > 0) { + unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + nTotalIn += tx.GetJoinSplitValueIn(); + CAmount fee = nTotalIn - tx.GetValueOut(); + CFeeRate feeRate(fee, nTxSize); + CAmount feePerK = feeRate.GetFeePerK(); + + if (feePerK == 0) { + feePerK = 1; + } + + dResult += 1000000000000 * double(feePerK); + // We cast feePerK from int64_t to double because if feePerK is a large number, say + // close to MAX_MONEY, the multiplication operation will result in an integer overflow. + // The variable dResult should never overflow since a 64-bit double in C++ is typically + // a double-precision floating-point number as specified by IEE 754, with a maximum + // value DBL_MAX of 1.79769e+308. + } + return tx.ComputePriority(dResult); } diff --git a/src/consensus/params.h b/src/consensus/params.h index d3e6462b8e3..87a3cb0610a 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -33,7 +33,8 @@ struct Params { int SubsidySlowStartShift() const { return nSubsidySlowStartInterval / 2; } int nSubsidyHalvingInterval; int GetLastFoundersRewardBlockHeight() const { - return nSubsidyHalvingInterval + SubsidySlowStartShift() - 1; + //return nSubsidyHalvingInterval + SubsidySlowStartShift() - 1; + return 1; // Bugfix #14: getblocksubsidy RPC command is incorrect } /** Used to check majorities for block version upgrade */ int nMajorityEnforceBlockUpgrade; diff --git a/src/gtest/test_checkblock.cpp b/src/gtest/test_checkblock.cpp index eab73afadc7..edd8a617dad 100644 --- a/src/gtest/test_checkblock.cpp +++ b/src/gtest/test_checkblock.cpp @@ -3,6 +3,7 @@ #include "consensus/validation.h" #include "main.h" +#include "zcash/Proof.hpp" class MockCValidationState : public CValidationState { public: @@ -22,12 +23,14 @@ class MockCValidationState : public CValidationState { }; TEST(CheckBlock, VersionTooLow) { + auto verifier = libzcash::ProofVerifier::Strict(); + CBlock block; block.nVersion = 1; MockCValidationState state; EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "version-too-low", false)).Times(1); - EXPECT_FALSE(CheckBlock(block, state, false, false)); + EXPECT_FALSE(CheckBlock(block, state, verifier, false, false)); } TEST(ContextualCheckBlock, BadCoinbaseHeight) { diff --git a/src/gtest/test_joinsplit.cpp b/src/gtest/test_joinsplit.cpp index 18f293f1d7e..17ac7ecb22f 100644 --- a/src/gtest/test_joinsplit.cpp +++ b/src/gtest/test_joinsplit.cpp @@ -15,6 +15,9 @@ using namespace libzcash; void test_full_api(ZCJoinSplit* js) { + // Create verification context. + auto verifier = libzcash::ProofVerifier::Strict(); + // The recipient's information. SpendingKey recipient_key = SpendingKey::random(); PaymentAddress recipient_addr = recipient_key.address(); @@ -69,6 +72,7 @@ void test_full_api(ZCJoinSplit* js) // Verify the transaction: ASSERT_TRUE(js->verify( proof, + verifier, pubKeyHash, randomSeed, macs, @@ -143,6 +147,7 @@ void test_full_api(ZCJoinSplit* js) // Verify the transaction: ASSERT_TRUE(js->verify( proof, + verifier, pubKeyHash, randomSeed, macs, diff --git a/src/gtest/test_metrics.cpp b/src/gtest/test_metrics.cpp new file mode 100644 index 00000000000..33c33d4d466 --- /dev/null +++ b/src/gtest/test_metrics.cpp @@ -0,0 +1,30 @@ +#include + +#include "metrics.h" +#include "utiltime.h" + + +TEST(Metrics, GetLocalSolPS) { + SetMockTime(100); + MarkStartTime(); + + // No time has passed + EXPECT_EQ(0, GetLocalSolPS()); + + // Increment time + SetMockTime(101); + EXPECT_EQ(0, GetLocalSolPS()); + + // Increment solutions + solutionTargetChecks.increment(); + EXPECT_EQ(1, GetLocalSolPS()); + + // Increment time + SetMockTime(102); + EXPECT_EQ(0.5, GetLocalSolPS()); + + // Increment solutions + solutionTargetChecks.increment(); + solutionTargetChecks.increment(); + EXPECT_EQ(1.5, GetLocalSolPS()); +} diff --git a/src/gtest/test_proofs.cpp b/src/gtest/test_proofs.cpp index dd77556771e..1e8d30bd7c9 100644 --- a/src/gtest/test_proofs.cpp +++ b/src/gtest/test_proofs.cpp @@ -22,6 +22,67 @@ typedef libsnark::default_r1cs_ppzksnark_pp::Fqe_type curve_Fq2; #include "version.h" #include "utilstrencodings.h" +TEST(proofs, g2_subgroup_check) +{ + // all G2 elements are order r + ASSERT_TRUE(libsnark::alt_bn128_modulus_r * curve_G2::random_element() == curve_G2::zero()); + + // but that doesn't mean all elements that satisfy the curve equation are in G2... + curve_G2 p = curve_G2::one(); + + while (1) { + // This will construct an order r(2q-r) point with high probability + p.X = curve_Fq2::random_element(); + try { + p.Y = ((p.X.squared() * p.X) + libsnark::alt_bn128_twist_coeff_b).sqrt(); + break; + } catch(...) {} + } + + ASSERT_TRUE(p.is_well_formed()); // it's on the curve + ASSERT_TRUE(libsnark::alt_bn128_modulus_r * p != curve_G2::zero()); // but not the order r subgroup.. + + { + // libsnark unfortunately doesn't check, and the pairing will complete + auto e = curve_Fr("149"); + auto a = curve_pp::reduced_pairing(curve_G1::one(), p); + auto b = curve_pp::reduced_pairing(e * curve_G1::one(), p); + + // though it will not preserve bilinearity + ASSERT_TRUE((a^e) != b); + } + + { + // so, our decompression API should not allow you to decompress G2 elements of that form! + CompressedG2 badp(p); + try { + auto newp = badp.to_libsnark_g2(); + FAIL() << "Expected std::runtime_error"; + } catch (std::runtime_error const & err) { + EXPECT_EQ(err.what(), std::string("point is not in G2")); + } catch(...) { + FAIL() << "Expected std::runtime_error"; + } + } + + // educational purposes: showing that E'(Fp2) is of order r(2q-r), + // by multiplying our random point in E' by (2q-r) = (q + q - r) to + // get an element in G2 + { + auto p1 = libsnark::alt_bn128_modulus_q * p; + p1 = p1 + p1; + p1 = p1 - (libsnark::alt_bn128_modulus_r * p); + + ASSERT_TRUE(p1.is_well_formed()); + ASSERT_TRUE(libsnark::alt_bn128_modulus_r * p1 == curve_G2::zero()); + + CompressedG2 goodp(p1); + auto newp = goodp.to_libsnark_g2(); + + ASSERT_TRUE(newp == p1); + } +} + TEST(proofs, sqrt_zero) { ASSERT_TRUE(curve_Fq::zero() == curve_Fq::zero().sqrt()); @@ -336,6 +397,29 @@ TEST(proofs, zksnark_serializes_properly) auto example = libsnark::generate_r1cs_example_with_field_input(250, 4); example.constraint_system.swap_AB_if_beneficial(); auto kp = libsnark::r1cs_ppzksnark_generator(example.constraint_system); + auto vkprecomp = libsnark::r1cs_ppzksnark_verifier_process_vk(kp.vk); + + for (size_t i = 0; i < 20; i++) { + auto badproof = ZCProof::random_invalid(); + auto proof = badproof.to_libsnark_proof>(); + + auto verifierEnabled = ProofVerifier::Strict(); + auto verifierDisabled = ProofVerifier::Disabled(); + // This verifier should catch the bad proof + ASSERT_FALSE(verifierEnabled.check( + kp.vk, + vkprecomp, + example.primary_input, + proof + )); + // This verifier won't! + ASSERT_TRUE(verifierDisabled.check( + kp.vk, + vkprecomp, + example.primary_input, + proof + )); + } for (size_t i = 0; i < 20; i++) { auto proof = libsnark::r1cs_ppzksnark_prover( @@ -345,6 +429,23 @@ TEST(proofs, zksnark_serializes_properly) example.constraint_system ); + { + auto verifierEnabled = ProofVerifier::Strict(); + auto verifierDisabled = ProofVerifier::Disabled(); + ASSERT_TRUE(verifierEnabled.check( + kp.vk, + vkprecomp, + example.primary_input, + proof + )); + ASSERT_TRUE(verifierDisabled.check( + kp.vk, + vkprecomp, + example.primary_input, + proof + )); + } + ASSERT_TRUE(libsnark::r1cs_ppzksnark_verifier_strong_IC( kp.vk, example.primary_input, diff --git a/src/init.cpp b/src/init.cpp index d9660541194..2d9a2e0ba0b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -377,7 +377,7 @@ std::string HelpMessage(HelpMessageMode mode) if (mode == HMM_BITCOIN_QT) debugCategories += ", qt"; strUsage += HelpMessageOpt("-debug=", strprintf(_("Output debugging information (default: %u, supplying is optional)"), 0) + ". " + - _("If is not supplied, output all debugging information.") + _(" can be:") + " " + debugCategories + "."); + _("If is not supplied or if = 1, output all debugging information.") + _(" can be:") + " " + debugCategories + "."); #ifdef ENABLE_WALLET strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), 0)); strUsage += HelpMessageOpt("-genproclimit=", strprintf(_("Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)"), 1)); @@ -447,6 +447,11 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-min", _("Start minimized")); strUsage += HelpMessageOpt("-rootcertificates=", _("Set SSL root certificates for payment request (default: -system-)")); strUsage += HelpMessageOpt("-splash", _("Show splash screen on startup (default: 1)")); + } else if (mode == HMM_BITCOIND) { + strUsage += HelpMessageGroup(_("Metrics Options (only if -daemon and -printtoconsole are not set):")); + strUsage += HelpMessageOpt("-showmetrics", _("Show metrics on stdout (default: 1 if running in a console, 0 otherwise)")); + strUsage += HelpMessageOpt("-metricsui", _("Set to 1 for a persistent metrics screen, 0 for sequential metrics output (default: 1 if running in a console, 0 otherwise)")); + strUsage += HelpMessageOpt("-metricsrefreshtime", strprintf(_("Number of seconds between metrics refreshes (default: %u if running in a console, %u otherwise)"), 1, 600)); } return strUsage; @@ -534,11 +539,6 @@ void ThreadImport(std::vector vImportFiles) RenameThread("zcash-loadblk"); // -reindex if (fReindex) { -#ifdef ENABLE_WALLET - if (pwalletMain) { - pwalletMain->ClearNoteWitnessCache(); - } -#endif CImportingNow imp; int nFile = 0; while (true) { @@ -974,8 +974,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler); threadGroup.create_thread(boost::bind(&TraceThread, "scheduler", serviceLoop)); + // Count uptime + MarkStartTime(); + if ((chainparams.NetworkIDString() != "regtest") && - GetBoolArg("-showmetrics", true) && + GetBoolArg("-showmetrics", isatty(STDOUT_FILENO)) && !fPrintToConsole && !GetBoolArg("-daemon", false)) { // Start the persistent metrics interface ConnectMetricsScreen(); @@ -1374,7 +1377,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) CBlockIndex *pindexRescan = chainActive.Tip(); if (GetBoolArg("-rescan", false)) + { + pwalletMain->ClearNoteWitnessCache(); pindexRescan = chainActive.Genesis(); + } else { CWalletDB walletdb(strWalletFile); diff --git a/src/main.cpp b/src/main.cpp index d2eee508b81..59fb857d631 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -831,7 +831,8 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in return nSigOps; } -bool CheckTransaction(const CTransaction& tx, CValidationState &state) +bool CheckTransaction(const CTransaction& tx, CValidationState &state, + libzcash::ProofVerifier& verifier) { // Don't count coinbase transactions because mining skews the count if (!tx.IsCoinBase()) { @@ -843,7 +844,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) } else { // Ensure that zk-SNARKs verify BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { - if (!joinsplit.Verify(*pzcashParams, tx.joinSplitPubKey)) { + if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) { return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"), REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); } @@ -1053,7 +1054,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (pfMissingInputs) *pfMissingInputs = false; - if (!CheckTransaction(tx, state)) + auto verifier = libzcash::ProofVerifier::Strict(); + if (!CheckTransaction(tx, state, verifier)) return error("AcceptToMemoryPool: CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction @@ -1501,7 +1503,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) } // We define a condition where we should warn the user about as a fork of at least 7 blocks - // with a tip within 72 blocks (+/- 12 hours if no one mines it) of ours + // with a tip within 72 blocks (+/- 3 hours if no one mines it) of ours // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network // hash rate operating on the fork. // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) @@ -2058,8 +2060,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); - // Check it again in case a previous version let a bad block in - if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) + + bool fExpensiveChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); + auto verifier = libzcash::ProofVerifier::Strict(); + 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)) return false; // verify that the view's current state corresponds to the previous block @@ -2078,8 +2085,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return true; } - bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); - // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. BOOST_FOREACH(const CTransaction& tx, block.vtx) { @@ -2105,7 +2110,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CBlockUndo blockundo; - CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + CCheckQueueControl control(fExpensiveChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); int64_t nTimeStart = GetTimeMicros(); CAmount nFees = 0; @@ -2166,7 +2171,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin nFees += view.GetValueIn(tx)-tx.GetValueOut(); std::vector vChecks; - if (!ContextualCheckInputs(tx, state, view, fScriptChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL)) + if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } @@ -2993,7 +2998,9 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f return true; } -bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot) +bool CheckBlock(const CBlock& block, CValidationState& state, + libzcash::ProofVerifier& verifier, + bool fCheckPOW, bool fCheckMerkleRoot) { // These are checks that are independent of context. @@ -3038,7 +3045,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!CheckTransaction(tx, state)) + if (!CheckTransaction(tx, state, verifier)) return error("CheckBlock(): CheckTransaction failed"); unsigned int nSigOps = 0; @@ -3204,7 +3211,9 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (fTooFarAhead) return true; // Block height is too high } - if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { + // 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 (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); @@ -3253,7 +3262,8 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) { // Preliminary checks - bool checked = CheckBlock(*pblock, state); + auto verifier = libzcash::ProofVerifier::Disabled(); + bool checked = CheckBlock(*pblock, state, verifier); { LOCK(cs_main); @@ -3289,11 +3299,13 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; + // JoinSplit proofs are verified in ConnectBlock + auto verifier = libzcash::ProofVerifier::Disabled(); // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, pindexPrev)) return false; - if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot)) + if (!CheckBlock(block, state, verifier, fCheckPOW, fCheckMerkleRoot)) return false; if (!ContextualCheckBlock(block, state, pindexPrev)) return false; @@ -3614,6 +3626,8 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; CValidationState state; + // No need to verify JoinSplits twice + auto verifier = libzcash::ProofVerifier::Disabled(); for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); @@ -3625,7 +3639,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth if (!ReadBlockFromDisk(block, pindex)) 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)) + if (nCheckLevel >= 1 && !CheckBlock(block, state, 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) { diff --git a/src/main.h b/src/main.h index c7f3c5c9fb4..58fb6ec9f7f 100644 --- a/src/main.h +++ b/src/main.h @@ -91,6 +91,10 @@ static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60; /** Maximum length of reject messages. */ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; +// Sanity check the magic numbers when we change them +BOOST_STATIC_ASSERT(DEFAULT_BLOCK_MAX_SIZE <= MAX_BLOCK_SIZE); +BOOST_STATIC_ASSERT(DEFAULT_BLOCK_PRIORITY_SIZE <= DEFAULT_BLOCK_MAX_SIZE); + #define equihash_parameters_acceptable(N, K) \ ((CBlockHeader::HEADER_SIZE + equihash_solution_size(N, K))*MAX_HEADERS_RESULTS < \ MAX_PROTOCOL_MESSAGE_LENGTH-1000) @@ -332,7 +336,7 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight); /** Context-independent validity checks */ -bool CheckTransaction(const CTransaction& tx, CValidationState& state); +bool CheckTransaction(const CTransaction& tx, CValidationState& state, libzcash::ProofVerifier& verifier); bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidationState &state); /** Check for standard transaction types @@ -409,7 +413,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin /** Context-independent validity checks */ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); -bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true); +bool CheckBlock(const CBlock& block, CValidationState& state, + libzcash::ProofVerifier& verifier, + bool fCheckPOW = true, bool fCheckMerkleRoot = true); /** Context-dependent validity checks */ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev); @@ -418,7 +424,13 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn /** 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. If dbp is non-NULL, the file is known to already reside on disk */ +/** + * 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); diff --git a/src/metrics.cpp b/src/metrics.cpp index 521310176db..efa9c0fc890 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -19,6 +19,7 @@ CCriticalSection cs_metrics; +boost::synchronized_value nNodeStartTime; AtomicCounter transactionsValidated; AtomicCounter ehSolverRuns; AtomicCounter solutionTargetChecks; @@ -39,6 +40,26 @@ void TrackMinedBlock(uint256 hash) trackedBlocks->push_back(hash); } +void MarkStartTime() +{ + *nNodeStartTime = GetTime(); +} + +int64_t GetUptime() +{ + return GetTime() - *nNodeStartTime; +} + +double GetLocalSolPS_INTERNAL(int64_t uptime) +{ + return uptime > 0 ? (double)solutionTargetChecks.get() / uptime : 0; +} + +double GetLocalSolPS() +{ + return GetLocalSolPS_INTERNAL(GetUptime()); +} + static bool metrics_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) @@ -118,13 +139,13 @@ int printMiningStatus(bool mining) return lines; } -int printMetrics(size_t cols, int64_t nStart, bool mining) +int printMetrics(size_t cols, bool mining) { // Number of lines that are always displayed int lines = 3; // Calculate uptime - int64_t uptime = GetTime() - nStart; + int64_t uptime = GetUptime(); int days = uptime / (24 * 60 * 60); int hours = (uptime - (days * 24 * 60 * 60)) / (60 * 60); int minutes = (uptime - (((days * 24) + hours) * 60 * 60)) / 60; @@ -145,10 +166,17 @@ int printMetrics(size_t cols, int64_t nStart, bool mining) std::cout << strDuration << std::endl; lines += (strDuration.size() / cols); - std::cout << "- " << strprintf(_("You have validated %d transactions!"), transactionsValidated.get()) << std::endl; + int validatedCount = transactionsValidated.get(); + if (validatedCount > 1) { + std::cout << "- " << strprintf(_("You have validated %d transactions!"), validatedCount) << std::endl; + } else if (validatedCount == 1) { + std::cout << "- " << _("You have validated a transaction!") << std::endl; + } else { + std::cout << "- " << _("You have validated no transactions.") << std::endl; + } if (mining && loaded) { - double solps = uptime > 0 ? (double)solutionTargetChecks.get() / uptime : 0; + double solps = GetLocalSolPS_INTERNAL(uptime); std::string strSolps = strprintf("%.4f Sol/s", solps); std::cout << "- " << strprintf(_("You have contributed %s on average to the network solution rate."), strSolps) << std::endl; std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl; @@ -248,20 +276,24 @@ void ThreadShowMetricsScreen() // Make this thread recognisable as the metrics screen thread RenameThread("zcash-metrics-screen"); - // Clear screen - std::cout << "\e[2J"; + // Determine whether we should render a persistent UI or rolling metrics + bool isTTY = isatty(STDOUT_FILENO); + bool isScreen = GetBoolArg("-metricsui", isTTY); + int64_t nRefresh = GetArg("-metricsrefreshtime", isTTY ? 1 : 600); - // Print art - std::cout << METRICS_ART << std::endl; - std::cout << std::endl; + if (isScreen) { + // Clear screen + std::cout << "\e[2J"; - // Thank you text - std::cout << _("Thank you for running a Zclassic node!") << std::endl; - std::cout << _("You're helping to strengthen the network and contributing to a social good, all coins are created equal :)") << std::endl; - std::cout << std::endl; + // Print art + std::cout << METRICS_ART << std::endl; + std::cout << std::endl; - // Count uptime - int64_t nStart = GetTime(); + // Thank you text + std::cout << _("Thank you for running a Zclassic node!") << std::endl; + std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl; + std::cout << std::endl; + } while (true) { // Number of lines that are always displayed @@ -269,7 +301,7 @@ void ThreadShowMetricsScreen() int cols = 80; // Get current window size - if (isatty(STDOUT_FILENO)) { + if (isTTY) { struct winsize w; w.ws_col = 0; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) { @@ -277,8 +309,10 @@ void ThreadShowMetricsScreen() } } - // Erase below current position - std::cout << "\e[J"; + if (isScreen) { + // Erase below current position + std::cout << "\e[J"; + } // Miner status bool mining = GetBoolArg("-gen", false); @@ -287,17 +321,27 @@ void ThreadShowMetricsScreen() lines += printNetworkStats(); } lines += printMiningStatus(mining); - lines += printMetrics(cols, nStart, mining); + lines += printMetrics(cols, mining); lines += printMessageBox(cols); lines += printInitMessage(); - // Explain how to exit - std::cout << "[" << _("Press Ctrl+C to exit") << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;; + if (isScreen) { + // Explain how to exit + std::cout << "[" << _("Press Ctrl+C to exit") << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl; + } else { + // Print delineator + std::cout << "----------------------------------------" << std::endl; + } - boost::this_thread::interruption_point(); - MilliSleep(1000); + int64_t nWaitEnd = GetTime() + nRefresh; + while (GetTime() < nWaitEnd) { + boost::this_thread::interruption_point(); + MilliSleep(200); + } - // Return to the top of the updating section - std::cout << "\e[" << lines << "A"; + if (isScreen) { + // Return to the top of the updating section + std::cout << "\e[" << lines << "A"; + } } } diff --git a/src/metrics.h b/src/metrics.h index a313f73f4ce..56ce1ba3f05 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -31,6 +31,9 @@ extern AtomicCounter solutionTargetChecks; void TrackMinedBlock(uint256 hash); +void MarkStartTime(); +double GetLocalSolPS(); + void ConnectMetricsScreen(); void ThreadShowMetricsScreen(); diff --git a/src/primitives/block.h b/src/primitives/block.h index 6180fb2ae44..6b3f13a865d 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -200,6 +200,10 @@ struct CBlockLocator { return vHave.empty(); } + + friend bool operator==(const CBlockLocator& a, const CBlockLocator& b) { + return (a.vHave == b.vHave); + } }; #endif // BITCOIN_PRIMITIVES_BLOCK_H diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index d1e9693606c..f6236a2f81d 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -70,10 +70,12 @@ JSDescription JSDescription::Randomized( bool JSDescription::Verify( ZCJoinSplit& params, + libzcash::ProofVerifier& verifier, const uint256& pubKeyHash ) const { return params.verify( proof, + verifier, pubKeyHash, randomSeed, macs, diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 1b144dc45f4..829588371c5 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -95,7 +95,11 @@ class JSDescription ); // Verifies that the JoinSplit proof is correct. - bool Verify(ZCJoinSplit& params, const uint256& pubKeyHash) const; + bool Verify( + ZCJoinSplit& params, + libzcash::ProofVerifier& verifier, + const uint256& pubKeyHash + ) const; // Returns the calculated h_sig uint256 h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const; diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 2841a3793f5..b482a8eb90e 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -103,6 +103,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "z_gettotalbalance", 0}, { "z_sendmany", 1}, { "z_sendmany", 2}, + { "z_sendmany", 3}, { "z_getoperationstatus", 0}, { "z_getoperationresult", 0}, { "z_importkey", 1 } diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index d6bd4dd3e22..d58e70adcb3 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -74,19 +74,60 @@ int64_t GetNetworkHashPS(int lookup, int height) { return (int64_t)(workDiff.getdouble() / timeDiff); } +Value getlocalsolps(const Array& params, bool fHelp) +{ + if (fHelp) + throw runtime_error( + "getlocalsolps\n" + "\nReturns the average local solutions per second since this node was started.\n" + "This is the same information shown on the metrics screen (if enabled).\n" + "\nResult:\n" + "xxx.xxxxx (numeric) Solutions per second average\n" + "\nExamples:\n" + + HelpExampleCli("getlocalsolps", "") + + HelpExampleRpc("getlocalsolps", "") + ); + + LOCK(cs_main); + return GetLocalSolPS(); +} + +Value getnetworksolps(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getnetworksolps ( blocks height )\n" + "\nReturns the estimated network solutions per second based on the last n blocks.\n" + "Pass in [blocks] to override # of blocks, -1 specifies over difficulty averaging window.\n" + "Pass in [height] to estimate the network speed at the time when a certain block was found.\n" + "\nArguments:\n" + "1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks over difficulty averaging window.\n" + "2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n" + "\nResult:\n" + "x (numeric) Solutions per second estimated\n" + "\nExamples:\n" + + HelpExampleCli("getnetworksolps", "") + + HelpExampleRpc("getnetworksolps", "") + ); + + LOCK(cs_main); + return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); +} + Value getnetworkhashps(const Array& params, bool fHelp) { if (fHelp || params.size() > 2) throw runtime_error( "getnetworkhashps ( blocks height )\n" - "\nReturns the estimated network hashes per second based on the last n blocks.\n" + "\nDEPRECATED - left for backwards-compatibility. Use getnetworksolps instead.\n" + "\nReturns the estimated network solutions per second based on the last n blocks.\n" "Pass in [blocks] to override # of blocks, -1 specifies over difficulty averaging window.\n" "Pass in [height] to estimate the network speed at the time when a certain block was found.\n" "\nArguments:\n" "1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks over difficulty averaging window.\n" "2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n" "\nResult:\n" - "x (numeric) Hashes per second estimated\n" + "x (numeric) Solutions per second estimated\n" "\nExamples:\n" + HelpExampleCli("getnetworkhashps", "") + HelpExampleRpc("getnetworkhashps", "") @@ -275,6 +316,8 @@ Value getmininginfo(const Array& params, bool fHelp) " \"errors\": \"...\" (string) Current errors\n" " \"generate\": true|false (boolean) If the generation is on or off (see getgenerate or setgenerate calls)\n" " \"genproclimit\": n (numeric) The processor limit for generation. -1 if no generation. (see getgenerate or setgenerate calls)\n" + " \"localsolps\": xxx.xxxxx (numeric) The average local solution rate in Sol/s since this node was started\n" + " \"networksolps\": x (numeric) The estimated network solution rate in Sol/s\n" " \"pooledtx\": n (numeric) The size of the mem pool\n" " \"testnet\": true|false (boolean) If using testnet or not\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" @@ -294,7 +337,9 @@ Value getmininginfo(const Array& params, bool fHelp) obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty())); obj.push_back(Pair("errors", GetWarnings("statusbar"))); obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); - obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); + obj.push_back(Pair("localsolps" , getlocalsolps(params, false))); + obj.push_back(Pair("networksolps", getnetworksolps(params, false))); + obj.push_back(Pair("networkhashps", getnetworksolps(params, false))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); obj.push_back(Pair("chain", Params().NetworkIDString())); diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 0418d75192c..e354e91a426 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -304,6 +304,8 @@ static const CRPCCommand vRPCCommands[] = /* Mining */ { "mining", "getblocktemplate", &getblocktemplate, true }, { "mining", "getmininginfo", &getmininginfo, true }, + { "mining", "getlocalsolps", &getlocalsolps, true }, + { "mining", "getnetworksolps", &getnetworksolps, true }, { "mining", "getnetworkhashps", &getnetworkhashps, true }, { "mining", "prioritisetransaction", &prioritisetransaction, true }, { "mining", "submitblock", &submitblock, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index b8eac08c480..fd53bf11645 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -169,6 +169,8 @@ extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fH extern json_spirit::Value getgenerate(const json_spirit::Array& params, bool fHelp); // in rpcmining.cpp extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value generate(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getlocalsolps(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getnetworksolps(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getnetworkhashps(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value prioritisetransaction(const json_spirit::Array& params, bool fHelp); diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index 51530c4de5a..c813c9af9ae 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -7,6 +7,7 @@ #include "main.h" #include "test/test_bitcoin.h" #include "utiltime.h" +#include "zcash/Proof.hpp" #include @@ -56,7 +57,8 @@ BOOST_AUTO_TEST_CASE(May15) // After May 15'th, big blocks are OK: forkingBlock.nTime = tMay15; // Invalidates PoW - BOOST_CHECK(CheckBlock(forkingBlock, state, false, false)); + auto verifier = libzcash::ProofVerifier::Strict(); + BOOST_CHECK(CheckBlock(forkingBlock, state, verifier, false, false)); } SetMockTime(0); diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index b638bccfc93..ea7e8185161 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -249,6 +249,81 @@ BOOST_AUTO_TEST_CASE(nullifier_regression_test) } } +BOOST_AUTO_TEST_CASE(anchor_pop_regression_test) +{ + // Correct behavior: + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Create dummy anchor/commitment + ZCIncrementalMerkleTree tree; + uint256 cm = GetRandHash(); + tree.append(cm); + + // Add the anchor + cache1.PushAnchor(tree); + cache1.Flush(); + + // Remove the anchor + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + cache1.Flush(); + + // Add the anchor back + cache1.PushAnchor(tree); + cache1.Flush(); + + // The base contains the anchor, of course! + { + ZCIncrementalMerkleTree checktree; + BOOST_CHECK(cache1.GetAnchorAt(tree.root(), checktree)); + BOOST_CHECK(checktree.root() == tree.root()); + } + } + + // Previously incorrect behavior + { + CCoinsViewTest base; + CCoinsViewCacheTest cache1(&base); + + // Create dummy anchor/commitment + ZCIncrementalMerkleTree tree; + uint256 cm = GetRandHash(); + tree.append(cm); + + // Add the anchor and flush to disk + cache1.PushAnchor(tree); + cache1.Flush(); + + // Remove the anchor, but don't flush yet! + cache1.PopAnchor(ZCIncrementalMerkleTree::empty_root()); + + { + CCoinsViewCacheTest cache2(&cache1); // Build cache on top + cache2.PushAnchor(tree); // Put the same anchor back! + cache2.Flush(); // Flush to cache1 + } + + // cache2's flush kinda worked, i.e. cache1 thinks the + // tree is there, but it didn't bring down the correct + // treestate... + { + ZCIncrementalMerkleTree checktree; + BOOST_CHECK(cache1.GetAnchorAt(tree.root(), checktree)); + BOOST_CHECK(checktree.root() == tree.root()); // Oh, shucks. + } + + // Flushing cache won't help either, just makes the inconsistency + // permanent. + cache1.Flush(); + { + ZCIncrementalMerkleTree checktree; + BOOST_CHECK(cache1.GetAnchorAt(tree.root(), checktree)); + BOOST_CHECK(checktree.root() == tree.root()); // Oh, shucks. + } + } +} + BOOST_AUTO_TEST_CASE(anchor_regression_test) { // Correct behavior: diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index 2eaeb7e611f..a59393ef77f 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -254,8 +254,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) BOOST_CHECK_THROW(CallRPC("getblocksubsidy -1"), runtime_error); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 50000")); Object obj = retValue.get_obj(); - BOOST_CHECK(find_value(obj, "miner") == 10.0); - BOOST_CHECK(find_value(obj, "founders") == 2.5); + BOOST_CHECK(find_value(obj, "miner") == 12.5); + BOOST_CHECK(find_value(obj, "founders") == 0.0); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 1000000")); obj = retValue.get_obj(); BOOST_CHECK(find_value(obj, "miner") == 6.25); @@ -820,7 +820,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) BOOST_CHECK_THROW(CallRPC("z_sendmany"), runtime_error); BOOST_CHECK_THROW(CallRPC("z_sendmany toofewargs"), runtime_error); - BOOST_CHECK_THROW(CallRPC("z_sendmany too many args here"), runtime_error); + BOOST_CHECK_THROW(CallRPC("z_sendmany just too many args here"), runtime_error); // bad from address BOOST_CHECK_THROW(CallRPC("z_sendmany " @@ -841,6 +841,27 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) " {\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":12.0} ]" ), runtime_error); + // invalid fee amount, cannot be negative + BOOST_CHECK_THROW(CallRPC("z_sendmany " + "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " + "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " + "1 -0.0001" + ), runtime_error); + + // invalid fee amount, bigger than MAX_MONEY + BOOST_CHECK_THROW(CallRPC("z_sendmany " + "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " + "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " + "1 21000001" + ), runtime_error); + + // fee amount is bigger than sum of outputs + BOOST_CHECK_THROW(CallRPC("z_sendmany " + "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " + "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " + "1 50.00000001" + ), runtime_error); + // memo bigger than allowed length of ZC_MEMO_SIZE std::vector v (2 * (ZC_MEMO_SIZE+1)); // x2 for hexadecimal string format std::fill(v.begin(),v.end(), 'A'); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 672a20d8adb..64fa4b29871 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -29,6 +29,7 @@ #include "zcash/Note.hpp" #include "zcash/Address.hpp" +#include "zcash/Proof.hpp" using namespace std; using namespace json_spirit; @@ -97,6 +98,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) // verifyFlags is a comma separated list of script verification flags to apply, or "NONE" Array tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid))); + auto verifier = libzcash::ProofVerifier::Strict(); ScriptError err; BOOST_FOREACH(Value& tv, tests) { @@ -141,7 +143,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest); + BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, verifier), strTest); BOOST_CHECK(state.IsValid()); for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -173,6 +175,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) // verifyFlags is a comma separated list of script verification flags to apply, or "NONE" Array tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid))); + auto verifier = libzcash::ProofVerifier::Strict(); ScriptError err; BOOST_FOREACH(Value& tv, tests) { @@ -217,7 +220,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) stream >> tx; CValidationState state; - fValid = CheckTransaction(tx, state) && state.IsValid(); + fValid = CheckTransaction(tx, state, verifier) && state.IsValid(); for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) { @@ -246,11 +249,12 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) CMutableTransaction tx; stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(CheckTransaction(tx, state) && state.IsValid(), "Simple deserialized transaction should be valid."); + auto verifier = libzcash::ProofVerifier::Strict(); + BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, verifier) && state.IsValid(), "Simple deserialized transaction should be valid."); // Check that duplicate txins fail tx.vin.push_back(tx.vin[0]); - BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); + BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state, verifier) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); } // @@ -341,9 +345,11 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification) libzcash::JSOutput(addr, 50) }; + auto verifier = libzcash::ProofVerifier::Strict(); + { JSDescription jsdesc(*p, pubKeyHash, rt, inputs, outputs, 0, 0); - BOOST_CHECK(jsdesc.Verify(*p, pubKeyHash)); + BOOST_CHECK(jsdesc.Verify(*p, verifier, pubKeyHash)); CDataStream ss(SER_DISK, CLIENT_VERSION); ss << jsdesc; @@ -352,7 +358,7 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification) ss >> jsdesc_deserialized; BOOST_CHECK(jsdesc_deserialized == jsdesc); - BOOST_CHECK(jsdesc_deserialized.Verify(*p, pubKeyHash)); + BOOST_CHECK(jsdesc_deserialized.Verify(*p, verifier, pubKeyHash)); } { @@ -365,12 +371,13 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification) // Ensure that it won't verify if the root is changed. auto test = JSDescription(*p, pubKeyHash, rt, inputs, outputs, 0, 0); test.anchor = GetRandHash(); - BOOST_CHECK(!test.Verify(*p, pubKeyHash)); + BOOST_CHECK(!test.Verify(*p, verifier, pubKeyHash)); } } BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) { + auto verifier = libzcash::ProofVerifier::Strict(); CMutableTransaction tx; tx.nVersion = 2; { @@ -422,23 +429,23 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) JSDescription *jsdesc = &newTx.vjoinsplit[0]; jsdesc->vpub_old = -1; - BOOST_CHECK(!CheckTransaction(newTx, state)); + BOOST_CHECK(!CheckTransaction(newTx, state, verifier)); BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_old-negative"); jsdesc->vpub_old = MAX_MONEY + 1; - BOOST_CHECK(!CheckTransaction(newTx, state)); + BOOST_CHECK(!CheckTransaction(newTx, state, verifier)); BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_old-toolarge"); jsdesc->vpub_old = 0; jsdesc->vpub_new = -1; - BOOST_CHECK(!CheckTransaction(newTx, state)); + BOOST_CHECK(!CheckTransaction(newTx, state, verifier)); BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_new-negative"); jsdesc->vpub_new = MAX_MONEY + 1; - BOOST_CHECK(!CheckTransaction(newTx, state)); + BOOST_CHECK(!CheckTransaction(newTx, state, verifier)); BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_new-toolarge"); jsdesc->vpub_new = (MAX_MONEY / 2) + 10; @@ -448,7 +455,7 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) JSDescription *jsdesc2 = &newTx.vjoinsplit[1]; jsdesc2->vpub_new = (MAX_MONEY / 2) + 10; - BOOST_CHECK(!CheckTransaction(newTx, state)); + BOOST_CHECK(!CheckTransaction(newTx, state, verifier)); BOOST_CHECK(state.GetRejectReason() == "bad-txns-txintotal-toolarge"); } { @@ -462,7 +469,7 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) jsdesc->nullifiers[0] = GetRandHash(); jsdesc->nullifiers[1] = jsdesc->nullifiers[0]; - BOOST_CHECK(!CheckTransaction(newTx, state)); + BOOST_CHECK(!CheckTransaction(newTx, state, verifier)); BOOST_CHECK(state.GetRejectReason() == "bad-joinsplits-nullifiers-duplicate"); jsdesc->nullifiers[1] = GetRandHash(); @@ -473,7 +480,7 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) jsdesc2->nullifiers[0] = GetRandHash(); jsdesc2->nullifiers[1] = jsdesc->nullifiers[0]; - BOOST_CHECK(!CheckTransaction(newTx, state)); + BOOST_CHECK(!CheckTransaction(newTx, state, verifier)); BOOST_CHECK(state.GetRejectReason() == "bad-joinsplits-nullifiers-duplicate"); } { @@ -492,7 +499,7 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity) CTransaction finalNewTx(newTx); BOOST_CHECK(finalNewTx.IsCoinBase()); } - BOOST_CHECK(!CheckTransaction(newTx, state)); + BOOST_CHECK(!CheckTransaction(newTx, state, verifier)); BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-joinsplits"); } } diff --git a/src/util.cpp b/src/util.cpp index 1ea5fab3f05..dbee0152261 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -217,6 +217,7 @@ bool LogAcceptCategory(const char* category) // if not debugging everything and not debugging specific category, LogPrint does nothing. if (setCategories.count(string("")) == 0 && + setCategories.count(string("1")) == 0 && setCategories.count(string(category)) == 0) return false; } diff --git a/src/utiltest.cpp b/src/utiltest.cpp new file mode 100644 index 00000000000..5cebc1a5dd6 --- /dev/null +++ b/src/utiltest.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "utiltest.h" + +CWalletTx GetValidReceive(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, CAmount value, + bool randomInputs) { + CMutableTransaction mtx; + mtx.nVersion = 2; // Enable JoinSplits + mtx.vin.resize(2); + if (randomInputs) { + mtx.vin[0].prevout.hash = GetRandHash(); + mtx.vin[1].prevout.hash = GetRandHash(); + } else { + mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001"); + mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002"); + } + mtx.vin[0].prevout.n = 0; + mtx.vin[1].prevout.n = 0; + + // Generate an ephemeral keypair. + uint256 joinSplitPubKey; + unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; + crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); + mtx.joinSplitPubKey = joinSplitPubKey; + + boost::array inputs = { + libzcash::JSInput(), // dummy input + libzcash::JSInput() // dummy input + }; + + boost::array outputs = { + libzcash::JSOutput(sk.address(), value), + libzcash::JSOutput(sk.address(), value) + }; + + boost::array output_notes; + + // Prepare JoinSplits + uint256 rt; + JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, + inputs, outputs, 2*value, 0, false}; + mtx.vjoinsplit.push_back(jsdesc); + + // Empty output script. + CScript scriptCode; + CTransaction signTx(mtx); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + + // Add the signature + assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, + dataToBeSigned.begin(), 32, + joinSplitPrivKey + ) == 0); + + CTransaction tx {mtx}; + CWalletTx wtx {NULL, tx}; + return wtx; +} + +libzcash::Note GetNote(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, + const CTransaction& tx, size_t js, size_t n) { + ZCNoteDecryption decryptor {sk.viewing_key()}; + auto hSig = tx.vjoinsplit[js].h_sig(params, tx.joinSplitPubKey); + auto note_pt = libzcash::NotePlaintext::decrypt( + decryptor, + tx.vjoinsplit[js].ciphertexts[n], + tx.vjoinsplit[js].ephemeralKey, + hSig, + (unsigned char) n); + return note_pt.note(sk.address()); +} + +CWalletTx GetValidSpend(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, + const libzcash::Note& note, CAmount value) { + CMutableTransaction mtx; + mtx.vout.resize(2); + mtx.vout[0].nValue = value; + mtx.vout[1].nValue = 0; + + // Generate an ephemeral keypair. + uint256 joinSplitPubKey; + unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; + crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); + mtx.joinSplitPubKey = joinSplitPubKey; + + // Fake tree for the unused witness + ZCIncrementalMerkleTree tree; + + libzcash::JSOutput dummyout; + libzcash::JSInput dummyin; + + { + if (note.value > value) { + libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); + libzcash::PaymentAddress dummyaddr = dummykey.address(); + dummyout = libzcash::JSOutput(dummyaddr, note.value - value); + } else if (note.value < value) { + libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); + libzcash::PaymentAddress dummyaddr = dummykey.address(); + libzcash::Note dummynote(dummyaddr.a_pk, (value - note.value), uint256(), uint256()); + tree.append(dummynote.cm()); + dummyin = libzcash::JSInput(tree.witness(), dummynote, dummykey); + } + } + + tree.append(note.cm()); + + boost::array inputs = { + libzcash::JSInput(tree.witness(), note, sk), + dummyin + }; + + boost::array outputs = { + dummyout, // dummy output + libzcash::JSOutput() // dummy output + }; + + boost::array output_notes; + + // Prepare JoinSplits + uint256 rt = tree.root(); + JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, + inputs, outputs, 0, value, false}; + mtx.vjoinsplit.push_back(jsdesc); + + // Empty output script. + CScript scriptCode; + CTransaction signTx(mtx); + uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); + + // Add the signature + assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, + dataToBeSigned.begin(), 32, + joinSplitPrivKey + ) == 0); + CTransaction tx {mtx}; + CWalletTx wtx {NULL, tx}; + return wtx; +} diff --git a/src/utiltest.h b/src/utiltest.h new file mode 100644 index 00000000000..8cfa60d0617 --- /dev/null +++ b/src/utiltest.h @@ -0,0 +1,18 @@ +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet/wallet.h" +#include "zcash/JoinSplit.hpp" +#include "zcash/Note.hpp" +#include "zcash/NoteEncryption.hpp" + +CWalletTx GetValidReceive(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, CAmount value, + bool randomInputs); +libzcash::Note GetNote(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, + const CTransaction& tx, size_t js, size_t n); +CWalletTx GetValidSpend(ZCJoinSplit& params, + const libzcash::SpendingKey& sk, + const libzcash::Note& note, CAmount value); diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 9e27b971a83..c65664eaeeb 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -50,9 +50,12 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( std::string fromAddress, std::vector tOutputs, std::vector zOutputs, - int minDepth) : - fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), mindepth_(minDepth) + int minDepth, + CAmount fee) : + fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), mindepth_(minDepth), fee_(fee) { + assert(fee_ > 0); + if (minDepth < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Minconf cannot be negative"); } @@ -147,8 +150,8 @@ bool AsyncRPCOperation_sendmany::main_impl() { bool isSingleZaddrOutput = (t_outputs_.size()==0 && z_outputs_.size()==1); bool isMultipleZaddrOutput = (t_outputs_.size()==0 && z_outputs_.size()>=1); bool isPureTaddrOnlyTx = (isfromtaddr_ && z_outputs_.size() == 0); - CAmount minersFee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE; - + CAmount minersFee = fee_; + // When spending coinbase utxos, you can only specify a single zaddr as the change must go somewhere // and if there are multiple zaddrs, we don't know where to send it. if (isfromtaddr_) { @@ -320,7 +323,22 @@ bool AsyncRPCOperation_sendmany::main_impl() { zOutputsDeque.push_back(o); } - + // When spending notes, take a snapshot of note witnesses and anchors as the treestate will + // change upon arrival of new blocks which contain joinsplit transactions. This is likely + // to happen as creating a chained joinsplit transaction can take longer than the block interval. + if (z_inputs_.size() > 0) { + LOCK2(cs_main, pwalletMain->cs_wallet); + for (auto t : z_inputs_) { + JSOutPoint jso = std::get<0>(t); + std::vector vOutPoints = { jso }; + uint256 inputAnchor; + std::vector> vInputWitnesses; + pwalletMain->GetNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor); + jsopWitnessAnchorMap[ jso.ToString() ] = WitnessAnchorData{ vInputWitnesses[0], inputAnchor }; + } + } + + /** * SCENARIO #2 * @@ -570,6 +588,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { // std::vector vInputNotes; std::vector vOutPoints; + std::vector> vInputWitnesses; uint256 inputAnchor; int numInputsNeeded = (jsChange>0) ? 1 : 0; while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { @@ -579,6 +598,14 @@ bool AsyncRPCOperation_sendmany::main_impl() { CAmount noteFunds = std::get<2>(t); zInputsDeque.pop_front(); + WitnessAnchorData wad = jsopWitnessAnchorMap[ jso.ToString() ]; + vInputWitnesses.push_back(wad.witness); + if (inputAnchor.IsNull()) { + inputAnchor = wad.anchor; + } else if (inputAnchor != wad.anchor) { + throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); + } + vOutPoints.push_back(jso); vInputNotes.push_back(note); @@ -595,12 +622,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { // Add history of previous commitments to witness if (vInputNotes.size() > 0) { - std::vector> vInputWitnesses; - { - LOCK(cs_main); - pwalletMain->GetNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor); - } - + if (vInputWitnesses.size()==0) { throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); } @@ -926,8 +948,11 @@ Object AsyncRPCOperation_sendmany::perform_joinsplit( info.vpub_new, !this->testmode); - if (!(jsdesc.Verify(*pzcashParams, joinSplitPubKey_))) { - throw std::runtime_error("error verifying joinsplit"); + { + auto verifier = libzcash::ProofVerifier::Strict(); + if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { + throw std::runtime_error("error verifying joinsplit"); + } } mtx.vjoinsplit.push_back(jsdesc); diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index df9c3d8d2bf..0382f024173 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -14,9 +14,10 @@ #include "json/json_spirit_value.h" #include "wallet.h" +#include #include -// TODO: Compute fee based on a heuristic, e.g. (num tx output * dust threshold) + joinsplit bytes * ? +// Default transaction fee if caller does not specify one. #define ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE 10000 using namespace libzcash; @@ -41,9 +42,15 @@ struct AsyncJoinSplitInfo CAmount vpub_new = 0; }; +// A struct to help us track the witness and anchor for a given JSOutPoint +struct WitnessAnchorData { + boost::optional witness; + uint256 anchor; +}; + class AsyncRPCOperation_sendmany : public AsyncRPCOperation { public: - AsyncRPCOperation_sendmany(std::string fromAddress, std::vector tOutputs, std::vector zOutputs, int minDepth); + AsyncRPCOperation_sendmany(std::string fromAddress, std::vector tOutputs, std::vector zOutputs, int minDepth, CAmount fee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE); virtual ~AsyncRPCOperation_sendmany(); // We don't want to be copied or moved around @@ -59,6 +66,7 @@ class AsyncRPCOperation_sendmany : public AsyncRPCOperation { private: friend class TEST_FRIEND_AsyncRPCOperation_sendmany; // class for unit testing + CAmount fee_; int mindepth_; std::string fromaddress_; bool isfromtaddr_; @@ -70,7 +78,9 @@ class AsyncRPCOperation_sendmany : public AsyncRPCOperation { uint256 joinSplitPubKey_; unsigned char joinSplitPrivKey_[crypto_sign_SECRETKEYBYTES]; - + // The key is the result string from calling JSOutPoint::ToString() + std::unordered_map jsopWitnessAnchorMap; + std::vector t_outputs_; std::vector z_outputs_; std::vector t_inputs_; diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index d3094495416..9bcc5f533dd 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -5,7 +5,9 @@ #include "base58.h" #include "chainparams.h" #include "main.h" +#include "primitives/block.h" #include "random.h" +#include "utiltest.h" #include "wallet/wallet.h" #include "zcash/JoinSplit.hpp" #include "zcash/Note.hpp" @@ -29,9 +31,11 @@ class MockWalletDB { MOCK_METHOD2(WriteTx, bool(uint256 hash, const CWalletTx& wtx)); MOCK_METHOD1(WriteWitnessCacheSize, bool(int64_t nWitnessCacheSize)); + MOCK_METHOD1(WriteBestBlock, bool(const CBlockLocator& loc)); }; -template void CWallet::WriteWitnessCache(MockWalletDB& walletdb); +template void CWallet::SetBestChainINTERNAL( + MockWalletDB& walletdb, const CBlockLocator& loc); class TestWallet : public CWallet { public: @@ -47,14 +51,14 @@ class TestWallet : public CWallet { void IncrementNoteWitnesses(const CBlockIndex* pindex, const CBlock* pblock, - ZCIncrementalMerkleTree tree) { + ZCIncrementalMerkleTree& tree) { CWallet::IncrementNoteWitnesses(pindex, pblock, tree); } void DecrementNoteWitnesses(const CBlockIndex* pindex) { CWallet::DecrementNoteWitnesses(pindex); } - void WriteWitnessCache(MockWalletDB& walletdb) { - CWallet::WriteWitnessCache(walletdb); + void SetBestChain(MockWalletDB& walletdb, const CBlockLocator& loc) { + CWallet::SetBestChainINTERNAL(walletdb, loc); } bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx) { return CWallet::UpdatedNoteData(wtxIn, wtx); @@ -65,138 +69,39 @@ class TestWallet : public CWallet { }; CWalletTx GetValidReceive(const libzcash::SpendingKey& sk, CAmount value, bool randomInputs) { - CMutableTransaction mtx; - mtx.nVersion = 2; // Enable JoinSplits - mtx.vin.resize(2); - if (randomInputs) { - mtx.vin[0].prevout.hash = GetRandHash(); - mtx.vin[1].prevout.hash = GetRandHash(); - } else { - mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001"); - mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002"); - } - mtx.vin[0].prevout.n = 0; - mtx.vin[1].prevout.n = 0; - - // Generate an ephemeral keypair. - uint256 joinSplitPubKey; - unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; - crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); - mtx.joinSplitPubKey = joinSplitPubKey; - - boost::array inputs = { - libzcash::JSInput(), // dummy input - libzcash::JSInput() // dummy input - }; - - boost::array outputs = { - libzcash::JSOutput(sk.address(), value), - libzcash::JSOutput(sk.address(), value) - }; - - boost::array output_notes; - - // Prepare JoinSplits - uint256 rt; - JSDescription jsdesc {*params, mtx.joinSplitPubKey, rt, - inputs, outputs, 2*value, 0, false}; - mtx.vjoinsplit.push_back(jsdesc); - - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); - - // Add the signature - assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey - ) == 0); - - CTransaction tx {mtx}; - CWalletTx wtx {NULL, tx}; - return wtx; + return GetValidReceive(*params, sk, value, randomInputs); } libzcash::Note GetNote(const libzcash::SpendingKey& sk, const CTransaction& tx, size_t js, size_t n) { - ZCNoteDecryption decryptor {sk.viewing_key()}; - auto hSig = tx.vjoinsplit[js].h_sig(*params, tx.joinSplitPubKey); - auto note_pt = libzcash::NotePlaintext::decrypt( - decryptor, - tx.vjoinsplit[js].ciphertexts[n], - tx.vjoinsplit[js].ephemeralKey, - hSig, - (unsigned char) n); - return note_pt.note(sk.address()); + return GetNote(*params, sk, tx, js, n); } CWalletTx GetValidSpend(const libzcash::SpendingKey& sk, const libzcash::Note& note, CAmount value) { - CMutableTransaction mtx; - mtx.vout.resize(2); - mtx.vout[0].nValue = value; - mtx.vout[1].nValue = 0; - - // Generate an ephemeral keypair. - uint256 joinSplitPubKey; - unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; - crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); - mtx.joinSplitPubKey = joinSplitPubKey; - - // Fake tree for the unused witness - ZCIncrementalMerkleTree tree; + return GetValidSpend(*params, sk, note, value); +} - libzcash::JSOutput dummyout; - libzcash::JSInput dummyin; +JSOutPoint CreateValidBlock(TestWallet& wallet, + const libzcash::SpendingKey& sk, + const CBlockIndex& index, + CBlock& block, + ZCIncrementalMerkleTree& tree) { + auto wtx = GetValidReceive(sk, 50, true); + auto note = GetNote(sk, wtx, 0, 1); + auto nullifier = note.nullifier(sk); - { - if (note.value > value) { - libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); - libzcash::PaymentAddress dummyaddr = dummykey.address(); - dummyout = libzcash::JSOutput(dummyaddr, note.value - value); - } else if (note.value < value) { - libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); - libzcash::PaymentAddress dummyaddr = dummykey.address(); - libzcash::Note dummynote(dummyaddr.a_pk, (value - note.value), uint256(), uint256()); - tree.append(dummynote.cm()); - dummyin = libzcash::JSInput(tree.witness(), dummynote, dummykey); - } - } + mapNoteData_t noteData; + JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; + CNoteData nd {sk.address(), nullifier}; + noteData[jsoutpt] = nd; + wtx.SetNoteData(noteData); + wallet.AddToWallet(wtx, true, NULL); + + block.vtx.push_back(wtx); + wallet.IncrementNoteWitnesses(&index, &block, tree); - tree.append(note.cm()); - - boost::array inputs = { - libzcash::JSInput(tree.witness(), note, sk), - dummyin - }; - - boost::array outputs = { - dummyout, // dummy output - libzcash::JSOutput() // dummy output - }; - - boost::array output_notes; - - // Prepare JoinSplits - uint256 rt = tree.root(); - JSDescription jsdesc {*params, mtx.joinSplitPubKey, rt, - inputs, outputs, 0, value, false}; - mtx.vjoinsplit.push_back(jsdesc); - - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); - - // Add the signature - assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, - dataToBeSigned.begin(), 32, - joinSplitPrivKey - ) == 0); - CTransaction tx {mtx}; - CWalletTx wtx {NULL, tx}; - return wtx; + return jsoutpt; } TEST(wallet_tests, setup_datadir_location_run_as_first_test) { @@ -689,27 +594,14 @@ TEST(wallet_tests, cached_witnesses_chain_tip) { wallet.AddSpendingKey(sk); { - // First transaction (case tested in _empty_chain) - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - mapNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - CNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - wtx.SetNoteData(noteData); - wallet.AddToWallet(wtx, true, NULL); - - std::vector notes {jsoutpt}; - std::vector> witnesses; - // First block (case tested in _empty_chain) - block1.vtx.push_back(wtx); CBlockIndex index1(block1); index1.nHeight = 1; - wallet.IncrementNoteWitnesses(&index1, &block1, tree); + auto jsoutpt = CreateValidBlock(wallet, sk, index1, block1, tree); + // Called to fetch anchor + std::vector notes {jsoutpt}; + std::vector> witnesses; wallet.GetNoteWitnesses(notes, witnesses, anchor1); } @@ -784,47 +676,21 @@ TEST(wallet_tests, CachedWitnessesDecrementFirst) { wallet.AddSpendingKey(sk); { - // First transaction (case tested in _empty_chain) - auto wtx = GetValidReceive(sk, 10, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - mapNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - CNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - wtx.SetNoteData(noteData); - wallet.AddToWallet(wtx, true, NULL); - // First block (case tested in _empty_chain) CBlock block1; - block1.vtx.push_back(wtx); CBlockIndex index1(block1); index1.nHeight = 1; - wallet.IncrementNoteWitnesses(&index1, &block1, tree); + CreateValidBlock(wallet, sk, index1, block1, tree); } { - // Second transaction (case tested in _chain_tip) - auto wtx = GetValidReceive(sk, 50, true); - auto note = GetNote(sk, wtx, 0, 1); - auto nullifier = note.nullifier(sk); - - mapNoteData_t noteData; - JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; - CNoteData nd {sk.address(), nullifier}; - noteData[jsoutpt] = nd; - wtx.SetNoteData(noteData); - wallet.AddToWallet(wtx, true, NULL); - - std::vector notes {jsoutpt}; - std::vector> witnesses; - // Second block (case tested in _chain_tip) - block2.vtx.push_back(wtx); index2.nHeight = 2; - wallet.IncrementNoteWitnesses(&index2, &block2, tree); + auto jsoutpt = CreateValidBlock(wallet, sk, index2, block2, tree); + // Called to fetch anchor + std::vector notes {jsoutpt}; + std::vector> witnesses; wallet.GetNoteWitnesses(notes, witnesses, anchor2); } @@ -868,6 +734,82 @@ TEST(wallet_tests, CachedWitnessesDecrementFirst) { } } +TEST(wallet_tests, CachedWitnessesCleanIndex) { + TestWallet wallet; + std::vector blocks; + std::vector indices; + std::vector notes; + std::vector anchors; + ZCIncrementalMerkleTree tree; + ZCIncrementalMerkleTree riTree = tree; + std::vector> witnesses; + + auto sk = libzcash::SpendingKey::random(); + wallet.AddSpendingKey(sk); + + // Generate a chain + size_t numBlocks = WITNESS_CACHE_SIZE + 10; + blocks.resize(numBlocks); + indices.resize(numBlocks); + for (size_t i = 0; i < numBlocks; i++) { + indices[i].nHeight = i; + auto old = tree.root(); + auto jsoutpt = CreateValidBlock(wallet, sk, indices[i], blocks[i], tree); + EXPECT_NE(old, tree.root()); + notes.push_back(jsoutpt); + + witnesses.clear(); + uint256 anchor; + wallet.GetNoteWitnesses(notes, witnesses, anchor); + for (size_t j = 0; j <= i; j++) { + EXPECT_TRUE((bool) witnesses[j]); + } + anchors.push_back(anchor); + } + + // Now pretend we are reindexing: the chain is cleared, and each block is + // used to increment witnesses again. + for (size_t i = 0; i < numBlocks; i++) { + ZCIncrementalMerkleTree riPrevTree {riTree}; + wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), riTree); + witnesses.clear(); + uint256 anchor; + wallet.GetNoteWitnesses(notes, witnesses, anchor); + for (size_t j = 0; j < numBlocks; j++) { + EXPECT_TRUE((bool) witnesses[j]); + } + // Should equal final anchor because witness cache unaffected + EXPECT_EQ(anchors.back(), anchor); + + if ((i == 5) || (i == 50)) { + // Pretend a reorg happened that was recorded in the block files + { + wallet.DecrementNoteWitnesses(&(indices[i])); + witnesses.clear(); + uint256 anchor; + wallet.GetNoteWitnesses(notes, witnesses, anchor); + for (size_t j = 0; j < numBlocks; j++) { + EXPECT_TRUE((bool) witnesses[j]); + } + // Should equal final anchor because witness cache unaffected + EXPECT_EQ(anchors.back(), anchor); + } + + { + wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), riPrevTree); + witnesses.clear(); + uint256 anchor; + wallet.GetNoteWitnesses(notes, witnesses, anchor); + for (size_t j = 0; j < numBlocks; j++) { + EXPECT_TRUE((bool) witnesses[j]); + } + // Should equal final anchor because witness cache unaffected + EXPECT_EQ(anchors.back(), anchor); + } + } + } +} + TEST(wallet_tests, ClearNoteWitnessCache) { TestWallet wallet; @@ -918,6 +860,7 @@ TEST(wallet_tests, ClearNoteWitnessCache) { TEST(wallet_tests, WriteWitnessCache) { TestWallet wallet; MockWalletDB walletdb; + CBlockLocator loc; auto sk = libzcash::SpendingKey::random(); wallet.AddSpendingKey(sk); @@ -928,7 +871,7 @@ TEST(wallet_tests, WriteWitnessCache) { // TxnBegin fails EXPECT_CALL(walletdb, TxnBegin()) .WillOnce(Return(false)); - wallet.WriteWitnessCache(walletdb); + wallet.SetBestChain(walletdb, loc); EXPECT_CALL(walletdb, TxnBegin()) .WillRepeatedly(Return(true)); @@ -937,14 +880,14 @@ TEST(wallet_tests, WriteWitnessCache) { .WillOnce(Return(false)); EXPECT_CALL(walletdb, TxnAbort()) .Times(1); - wallet.WriteWitnessCache(walletdb); + wallet.SetBestChain(walletdb, loc); // WriteTx throws EXPECT_CALL(walletdb, WriteTx(wtx.GetHash(), wtx)) .WillOnce(ThrowLogicError()); EXPECT_CALL(walletdb, TxnAbort()) .Times(1); - wallet.WriteWitnessCache(walletdb); + wallet.SetBestChain(walletdb, loc); EXPECT_CALL(walletdb, WriteTx(wtx.GetHash(), wtx)) .WillRepeatedly(Return(true)); @@ -953,26 +896,42 @@ TEST(wallet_tests, WriteWitnessCache) { .WillOnce(Return(false)); EXPECT_CALL(walletdb, TxnAbort()) .Times(1); - wallet.WriteWitnessCache(walletdb); + wallet.SetBestChain(walletdb, loc); // WriteWitnessCacheSize throws EXPECT_CALL(walletdb, WriteWitnessCacheSize(0)) .WillOnce(ThrowLogicError()); EXPECT_CALL(walletdb, TxnAbort()) .Times(1); - wallet.WriteWitnessCache(walletdb); + wallet.SetBestChain(walletdb, loc); EXPECT_CALL(walletdb, WriteWitnessCacheSize(0)) .WillRepeatedly(Return(true)); + // WriteBestBlock fails + EXPECT_CALL(walletdb, WriteBestBlock(loc)) + .WillOnce(Return(false)); + EXPECT_CALL(walletdb, TxnAbort()) + .Times(1); + wallet.SetBestChain(walletdb, loc); + + // WriteBestBlock throws + EXPECT_CALL(walletdb, WriteBestBlock(loc)) + .WillOnce(ThrowLogicError()); + EXPECT_CALL(walletdb, TxnAbort()) + .Times(1); + wallet.SetBestChain(walletdb, loc); + EXPECT_CALL(walletdb, WriteBestBlock(loc)) + .WillRepeatedly(Return(true)); + // TxCommit fails EXPECT_CALL(walletdb, TxnCommit()) .WillOnce(Return(false)); - wallet.WriteWitnessCache(walletdb); + wallet.SetBestChain(walletdb, loc); EXPECT_CALL(walletdb, TxnCommit()) .WillRepeatedly(Return(true)); // Everything succeeds - wallet.WriteWitnessCache(walletdb); + wallet.SetBestChain(walletdb, loc); } TEST(wallet_tests, UpdateNullifierNoteMap) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b10a08efec0..6b0f5f52831 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2480,6 +2480,12 @@ Value zc_benchmark(const json_spirit::Array& params, bool fHelp) sample_times.push_back(benchmark_verify_equihash()); } else if (benchmarktype == "validatelargetx") { sample_times.push_back(benchmark_large_tx()); + } else if (benchmarktype == "trydecryptnotes") { + int nAddrs = params[2].get_int(); + sample_times.push_back(benchmark_try_decrypt_notes(nAddrs)); + } else if (benchmarktype == "incnotewitnesses") { + int nTxs = params[2].get_int(); + sample_times.push_back(benchmark_increment_note_witnesses(nTxs)); } else { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid benchmarktype"); } @@ -2707,7 +2713,10 @@ Value zc_raw_joinsplit(const json_spirit::Array& params, bool fHelp) vpub_old, vpub_new); - assert(jsdesc.Verify(*pzcashParams, joinSplitPubKey)); + { + auto verifier = libzcash::ProofVerifier::Strict(); + assert(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey)); + } mtx.vjoinsplit.push_back(jsdesc); @@ -3182,9 +3191,9 @@ Value z_sendmany(const Array& params, bool fHelp) if (!EnsureWalletIsAvailable(fHelp)) return Value::null; - if (fHelp || params.size() < 2 || params.size() > 3) + if (fHelp || params.size() < 2 || params.size() > 4) throw runtime_error( - "z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf )\n" + "z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee )\n" "\nSend multiple times. Amounts are double-precision floating point numbers." "\nChange from a taddr flows to a new taddr address, while change from zaddr returns to itself." "\nWhen sending coinbase UTXOs to a zaddr, change is not allowed. The entire value of the UTXO(s) must be consumed." @@ -3199,6 +3208,8 @@ Value z_sendmany(const Array& params, bool fHelp) " \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format\n" " }, ... ]\n" "3. minconf (numeric, optional, default=1) Only use funds confirmed at least this many times.\n" + "4. fee (numeric, optional, default=" + + strprintf("%s", FormatMoney(ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" "\nResult:\n" "\"operationid\" (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" ); @@ -3239,6 +3250,7 @@ Value z_sendmany(const Array& params, bool fHelp) // Recipients std::vector taddrRecipients; std::vector zaddrRecipients; + CAmount nTotalOut = 0; BOOST_FOREACH(Value& output, outputs) { @@ -3294,6 +3306,8 @@ Value z_sendmany(const Array& params, bool fHelp) } else { taddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) ); } + + nTotalOut += nAmount; } // Check the number of zaddr outputs does not exceed the limit. @@ -3329,9 +3343,19 @@ Value z_sendmany(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); } + // Fee in Zatoshis, not currency format) + CAmount nFee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE; + if (params.size() > 3) { + nFee = AmountFromValue( params[3] ); + // Check that the user specified fee is sane. + if (nFee > nTotalOut) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the sum of outputs %s", FormatMoney(nFee), FormatMoney(nTotalOut))); + } + } + // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); - std::shared_ptr operation( new AsyncRPCOperation_sendmany(fromaddress, taddrRecipients, zaddrRecipients, nMinDepth) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(fromaddress, taddrRecipients, zaddrRecipients, nMinDepth, nFee) ); q->addOperation(operation); AsyncRPCOperationId operationId = operation->getId(); return operationId; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 51ed76f3a84..8c32dcb1a30 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -380,7 +380,7 @@ void CWallet::ChainTip(const CBlockIndex *pindex, const CBlock *pblock, void CWallet::SetBestChain(const CBlockLocator& loc) { CWalletDB walletdb(strWalletFile); - walletdb.WriteBestBlock(loc); + SetBestChainINTERNAL(walletdb, loc); } bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) @@ -645,10 +645,14 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex, for (std::pair& wtxItem : mapWallet) { for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) { CNoteData* nd = &(item.second); - // Check the validity of the cache - assert(nWitnessCacheSize >= nd->witnesses.size()); // Only increment witnesses that are behind the current height if (nd->witnessHeight < pindex->nHeight) { + // Check the validity of the cache + // The only time a note witnessed above the current height + // would be invalid here is during a reindex when blocks + // have been decremented, and we are incrementing the blocks + // immediately after. + assert(nWitnessCacheSize >= nd->witnesses.size()); // Witnesses being incremented should always be either -1 // (never incremented or decremented) or one below pindex assert((nd->witnessHeight == -1) || @@ -687,10 +691,11 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex, for (std::pair& wtxItem : mapWallet) { for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) { CNoteData* nd = &(item.second); - // Check the validity of the cache - assert(nWitnessCacheSize >= nd->witnesses.size()); if (nd->witnessHeight < pindex->nHeight && nd->witnesses.size() > 0) { + // Check the validity of the cache + // See earlier comment about validity. + assert(nWitnessCacheSize >= nd->witnesses.size()); nd->witnesses.front().append(note_commitment); } } @@ -699,7 +704,8 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex, // If this is our note, witness it if (txIsOurs) { JSOutPoint jsoutpt {hash, i, j}; - if (mapWallet[hash].mapNoteData.count(jsoutpt)) { + if (mapWallet[hash].mapNoteData.count(jsoutpt) && + mapWallet[hash].mapNoteData[jsoutpt].witnessHeight < pindex->nHeight) { CNoteData* nd = &(mapWallet[hash].mapNoteData[jsoutpt]); if (nd->witnesses.size() > 0) { // We think this can happen because we write out the @@ -735,16 +741,16 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex, CNoteData* nd = &(item.second); if (nd->witnessHeight < pindex->nHeight) { nd->witnessHeight = pindex->nHeight; + // Check the validity of the cache + // See earlier comment about validity. + assert(nWitnessCacheSize >= nd->witnesses.size()); } - // Check the validity of the cache - assert(nWitnessCacheSize >= nd->witnesses.size()); } } - if (fFileBacked) { - CWalletDB walletdb(strWalletFile); - WriteWitnessCache(walletdb); - } + // For performance reasons, we write out the witness cache in + // CWallet::SetBestChain() (which also ensures that overall consistency + // of the wallet.dat is maintained). } } @@ -755,18 +761,23 @@ void CWallet::DecrementNoteWitnesses(const CBlockIndex* pindex) for (std::pair& wtxItem : mapWallet) { for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) { CNoteData* nd = &(item.second); - // Check the validity of the cache - assert(nWitnessCacheSize >= nd->witnesses.size()); - // Witnesses being decremented should always be either -1 - // (never incremented or decremented) or equal to pindex - assert((nd->witnessHeight == -1) || - (nd->witnessHeight == pindex->nHeight)); - if (nd->witnesses.size() > 0) { - nd->witnesses.pop_front(); + // Only increment witnesses that are not above the current height + if (nd->witnessHeight <= pindex->nHeight) { + // Check the validity of the cache + // See comment below (this would be invalid if there was a + // prior decrement). + assert(nWitnessCacheSize >= nd->witnesses.size()); + // Witnesses being decremented should always be either -1 + // (never incremented or decremented) or equal to pindex + assert((nd->witnessHeight == -1) || + (nd->witnessHeight == pindex->nHeight)); + if (nd->witnesses.size() > 0) { + nd->witnesses.pop_front(); + } + // pindex is the block being removed, so the new witness cache + // height is one below it. + nd->witnessHeight = pindex->nHeight - 1; } - // pindex is the block being removed, so the new witness cache - // height is one below it. - nd->witnessHeight = pindex->nHeight - 1; } } nWitnessCacheSize -= 1; @@ -774,15 +785,26 @@ void CWallet::DecrementNoteWitnesses(const CBlockIndex* pindex) for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) { CNoteData* nd = &(item.second); // Check the validity of the cache - assert(nWitnessCacheSize >= nd->witnesses.size()); + // Technically if there are notes witnessed above the current + // height, their cache will now be invalid (relative to the new + // value of nWitnessCacheSize). However, this would only occur + // during a reindex, and by the time the reindex reaches the tip + // of the chain again, the existing witness caches will be valid + // again. + // We don't set nWitnessCacheSize to zero at the start of the + // reindex because the on-disk blocks had already resulted in a + // chain that didn't trigger the assertion below. + if (nd->witnessHeight < pindex->nHeight) { + assert(nWitnessCacheSize >= nd->witnesses.size()); + } } } // TODO: If nWitnessCache is zero, we need to regenerate the caches (#1302) assert(nWitnessCacheSize > 0); - if (fFileBacked) { - CWalletDB walletdb(strWalletFile); - WriteWitnessCache(walletdb); - } + + // For performance reasons, we write out the witness cache in + // CWallet::SetBestChain() (which also ensures that overall consistency + // of the wallet.dat is maintained). } } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 85f960eb1be..3d721fc403b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -636,35 +636,40 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void DecrementNoteWitnesses(const CBlockIndex* pindex); template - void WriteWitnessCache(WalletDB& walletdb) { + void SetBestChainINTERNAL(WalletDB& walletdb, const CBlockLocator& loc) { if (!walletdb.TxnBegin()) { // This needs to be done atomically, so don't do it at all - LogPrintf("WriteWitnessCache(): Couldn't start atomic write\n"); + LogPrintf("SetBestChain(): Couldn't start atomic write\n"); return; } try { for (std::pair& wtxItem : mapWallet) { if (!walletdb.WriteTx(wtxItem.first, wtxItem.second)) { - LogPrintf("WriteWitnessCache(): Failed to write CWalletTx, aborting atomic write\n"); + LogPrintf("SetBestChain(): Failed to write CWalletTx, aborting atomic write\n"); walletdb.TxnAbort(); return; } } if (!walletdb.WriteWitnessCacheSize(nWitnessCacheSize)) { - LogPrintf("WriteWitnessCache(): Failed to write nWitnessCacheSize, aborting atomic write\n"); + LogPrintf("SetBestChain(): Failed to write nWitnessCacheSize, aborting atomic write\n"); + walletdb.TxnAbort(); + return; + } + if (!walletdb.WriteBestBlock(loc)) { + LogPrintf("SetBestChain(): Failed to write best block, aborting atomic write\n"); walletdb.TxnAbort(); return; } } catch (const std::exception &exc) { // Unexpected failure - LogPrintf("WriteWitnessCache(): Unexpected error during atomic write:\n"); + LogPrintf("SetBestChain(): Unexpected error during atomic write:\n"); LogPrintf("%s\n", exc.what()); walletdb.TxnAbort(); return; } if (!walletdb.TxnCommit()) { // Couldn't commit all to db, but in-memory state is fine - LogPrintf("WriteWitnessCache(): Couldn't commit atomic write\n"); + LogPrintf("SetBestChain(): Couldn't commit atomic write\n"); return; } } @@ -954,6 +959,7 @@ 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, ZCIncrementalMerkleTree tree, bool added); + /** Saves witness caches and best block locator to disk. */ void SetBestChain(const CBlockLocator& loc); DBErrors LoadWallet(bool& fFirstRunRet); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index d4966b18637..ec6c55ee30e 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -14,6 +14,7 @@ #include "util.h" #include "utiltime.h" #include "wallet/wallet.h" +#include "zcash/Proof.hpp" #include #include @@ -411,7 +412,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CWalletTx wtx; ssValue >> wtx; CValidationState state; - if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid())) + auto verifier = libzcash::ProofVerifier::Strict(); + if (!(CheckTransaction(wtx, state, verifier) && (wtx.GetHash() == hash) && state.IsValid())) return false; // Undo serialize changes in 31600 diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index dad15316ede..590700cd9ab 100644 --- a/src/zcash/JoinSplit.cpp +++ b/src/zcash/JoinSplit.cpp @@ -5,7 +5,6 @@ #include "zcash/util.h" #include -#include #include #include @@ -25,8 +24,6 @@ namespace libzcash { #include "zcash/circuit/gadget.tcc" -std::once_flag init_public_params_once_flag; - CCriticalSection cs_ParamsIO; CCriticalSection cs_LoadKeys; @@ -80,10 +77,6 @@ class JoinSplitCircuit : public JoinSplit { JoinSplitCircuit() {} ~JoinSplitCircuit() {} - static void initialize() { - std::call_once (init_public_params_once_flag, ppzksnark_ppT::init_public_params); - } - void setProvingKeyPath(std::string path) { pkPath = path; } @@ -151,6 +144,7 @@ class JoinSplitCircuit : public JoinSplit { bool verify( const ZCProof& proof, + ProofVerifier& verifier, const uint256& pubKeyHash, const uint256& randomSeed, const boost::array& macs, @@ -179,7 +173,12 @@ class JoinSplitCircuit : public JoinSplit { vpub_new ); - return r1cs_ppzksnark_online_verifier_strong_IC(*vk_precomp, witness, r1cs_proof); + return verifier.check( + *vk, + *vk_precomp, + witness, + r1cs_proof + ); } catch (...) { return false; } @@ -358,7 +357,7 @@ class JoinSplitCircuit : public JoinSplit { template JoinSplit* JoinSplit::Generate() { - JoinSplitCircuit::initialize(); + initialize_curve_params(); auto js = new JoinSplitCircuit(); js->generate(); @@ -368,7 +367,7 @@ JoinSplit* JoinSplit::Generate() template JoinSplit* JoinSplit::Unopened() { - JoinSplitCircuit::initialize(); + initialize_curve_params(); return new JoinSplitCircuit(); } diff --git a/src/zcash/JoinSplit.hpp b/src/zcash/JoinSplit.hpp index 8f17a4c4698..a8c08d21b02 100644 --- a/src/zcash/JoinSplit.hpp +++ b/src/zcash/JoinSplit.hpp @@ -83,6 +83,7 @@ class JoinSplit { virtual bool verify( const ZCProof& proof, + ProofVerifier& verifier, const uint256& pubKeyHash, const uint256& randomSeed, const boost::array& hmacs, diff --git a/src/zcash/Proof.cpp b/src/zcash/Proof.cpp index ab7c644832b..1b2199407f9 100644 --- a/src/zcash/Proof.cpp +++ b/src/zcash/Proof.cpp @@ -1,6 +1,7 @@ #include "Proof.hpp" #include +#include #include "crypto/common.h" #include "libsnark/common/default_types/r1cs_ppzksnark_pp.hpp" @@ -162,6 +163,10 @@ curve_G2 CompressedG2::to_libsnark_g2() const assert(r.is_well_formed()); + if (alt_bn128_modulus_r * r != curve_G2::zero()) { + throw std::runtime_error("point is not in G2"); + } + return r; } @@ -211,4 +216,36 @@ ZCProof ZCProof::random_invalid() return p; } +std::once_flag init_public_params_once_flag; + +void initialize_curve_params() +{ + std::call_once (init_public_params_once_flag, curve_pp::init_public_params); +} + +ProofVerifier ProofVerifier::Strict() { + initialize_curve_params(); + return ProofVerifier(true); +} + +ProofVerifier ProofVerifier::Disabled() { + initialize_curve_params(); + return ProofVerifier(false); +} + +template<> +bool ProofVerifier::check( + const r1cs_ppzksnark_verification_key& vk, + const r1cs_ppzksnark_processed_verification_key& pvk, + const r1cs_primary_input& primary_input, + const r1cs_ppzksnark_proof& proof +) +{ + if (perform_verification) { + return r1cs_ppzksnark_online_verifier_strong_IC(pvk, primary_input, proof); + } else { + return true; + } +} + } diff --git a/src/zcash/Proof.hpp b/src/zcash/Proof.hpp index b4fdf93b669..3b6b5e568b6 100644 --- a/src/zcash/Proof.hpp +++ b/src/zcash/Proof.hpp @@ -235,6 +235,42 @@ class ZCProof { } }; +void initialize_curve_params(); + +class ProofVerifier { +private: + bool perform_verification; + + ProofVerifier(bool perform_verification) : perform_verification(perform_verification) { } + +public: + // ProofVerifier should never be copied + ProofVerifier(const ProofVerifier&) = delete; + ProofVerifier& operator=(const ProofVerifier&) = delete; + ProofVerifier(ProofVerifier&&); + ProofVerifier& operator=(ProofVerifier&&); + + // Creates a verification context that strictly verifies + // all proofs using libsnark's API. + static ProofVerifier Strict(); + + // Creates a verification context that performs no + // verification, used when avoiding duplicate effort + // such as during reindexing. + static ProofVerifier Disabled(); + + template + bool check( + const VerificationKey& vk, + const ProcessedVerificationKey& pvk, + const PrimaryInput& pi, + const Proof& p + ); +}; } diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 1d60fdde726..b88ebb9e13a 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -17,6 +17,7 @@ #include "script/sign.h" #include "sodium.h" #include "streams.h" +#include "utiltest.h" #include "wallet/wallet.h" #include "zcbenchmarks.h" @@ -89,7 +90,8 @@ double benchmark_create_joinsplit() 0); double ret = timer_stop(tv_start); - assert(jsdesc.Verify(*pzcashParams, pubKeyHash)); + auto verifier = libzcash::ProofVerifier::Strict(); + assert(jsdesc.Verify(*pzcashParams, verifier, pubKeyHash)); return ret; } @@ -98,7 +100,8 @@ double benchmark_verify_joinsplit(const JSDescription &joinsplit) struct timeval tv_start; timer_start(tv_start); uint256 pubKeyHash; - joinsplit.Verify(*pzcashParams, pubKeyHash); + auto verifier = libzcash::ProofVerifier::Strict(); + joinsplit.Verify(*pzcashParams, verifier, pubKeyHash); return timer_stop(tv_start); } @@ -223,3 +226,76 @@ double benchmark_large_tx() return timer_stop(tv_start); } +double benchmark_try_decrypt_notes(size_t nAddrs) +{ + CWallet wallet; + for (int i = 0; i < nAddrs; i++) { + auto sk = libzcash::SpendingKey::random(); + wallet.AddSpendingKey(sk); + } + + auto sk = libzcash::SpendingKey::random(); + auto tx = GetValidReceive(*pzcashParams, sk, 10, true); + + struct timeval tv_start; + timer_start(tv_start); + auto nd = wallet.FindMyNotes(tx); + return timer_stop(tv_start); +} + +double benchmark_increment_note_witnesses(size_t nTxs) +{ + CWallet wallet; + ZCIncrementalMerkleTree tree; + + auto sk = libzcash::SpendingKey::random(); + wallet.AddSpendingKey(sk); + + // First block + CBlock block1; + for (int i = 0; i < nTxs; i++) { + auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); + auto note = GetNote(*pzcashParams, sk, wtx, 0, 1); + auto nullifier = note.nullifier(sk); + + mapNoteData_t noteData; + JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; + CNoteData nd {sk.address(), nullifier}; + noteData[jsoutpt] = nd; + + wtx.SetNoteData(noteData); + wallet.AddToWallet(wtx, true, NULL); + block1.vtx.push_back(wtx); + } + CBlockIndex index1(block1); + index1.nHeight = 1; + + // Increment to get transactions witnessed + wallet.ChainTip(&index1, &block1, tree, true); + + // Second block + CBlock block2; + block2.hashPrevBlock = block1.GetHash(); + { + auto wtx = GetValidReceive(*pzcashParams, sk, 10, true); + auto note = GetNote(*pzcashParams, sk, wtx, 0, 1); + auto nullifier = note.nullifier(sk); + + mapNoteData_t noteData; + JSOutPoint jsoutpt {wtx.GetHash(), 0, 1}; + CNoteData nd {sk.address(), nullifier}; + noteData[jsoutpt] = nd; + + wtx.SetNoteData(noteData); + wallet.AddToWallet(wtx, true, NULL); + block2.vtx.push_back(wtx); + } + CBlockIndex index2(block2); + index2.nHeight = 2; + + struct timeval tv_start; + timer_start(tv_start); + wallet.ChainTip(&index2, &block2, tree, true); + return timer_stop(tv_start); +} + diff --git a/src/zcbenchmarks.h b/src/zcbenchmarks.h index b81d4a7072f..318921002d3 100644 --- a/src/zcbenchmarks.h +++ b/src/zcbenchmarks.h @@ -12,5 +12,7 @@ extern std::vector benchmark_solve_equihash_threaded(int nThreads); extern double benchmark_verify_joinsplit(const JSDescription &joinsplit); extern double benchmark_verify_equihash(); extern double benchmark_large_tx(); +extern double benchmark_try_decrypt_notes(size_t nAddrs); +extern double benchmark_increment_note_witnesses(size_t nTxs); #endif diff --git a/zcutil/build-debian-package.sh b/zcutil/build-debian-package.sh index 5691f237209..8d063399eb1 100755 --- a/zcutil/build-debian-package.sh +++ b/zcutil/build-debian-package.sh @@ -23,10 +23,11 @@ if [ -d $BUILD_DIR ]; then rm -R $BUILD_DIR fi +DEB_CMP=$BUILD_DIR/etc/bash_completion.d DEB_BIN=$BUILD_DIR/usr/bin DEB_DOC=$BUILD_DIR/usr/share/doc/$PACKAGE_NAME DEB_MAN=$BUILD_DIR/usr/share/man/man1 -mkdir -p $BUILD_DIR/DEBIAN $DEB_BIN $DEB_DOC $DEB_MAN +mkdir -p $BUILD_DIR/DEBIAN $DEB_CMP $DEB_BIN $DEB_DOC $DEB_MAN chmod 0755 -R $BUILD_DIR/* # Copy control file @@ -48,6 +49,9 @@ cp -r $SRC_DEB/examples $DEB_DOC # Copy manpages cp $SRC_DEB/manpages/zcashd.1 $DEB_MAN cp $SRC_DEB/manpages/zcash-cli.1 $DEB_MAN +# Copy bash completion files +cp $SRC_PATH/contrib/bitcoind.bash-completion $DEB_CMP/zcashd +cp $SRC_PATH/contrib/bitcoin-cli.bash-completion $DEB_CMP/zcash-cli # Gzip files gzip --best -n $DEB_DOC/changelog gzip --best -n $DEB_DOC/changelog.Debian diff --git a/zcutil/build.sh b/zcutil/build.sh index c10be652fcc..c4823ed6199 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -1,7 +1,30 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail +# Allow user overrides to $MAKE. Typical usage for users who need it: +# MAKE=gmake ./zcutil/build.sh -j$(nproc) +if [[ -z "${MAKE-}" ]]; then + MAKE=make +fi + +# Allow overrides to $BUILD and $HOST for porters. Most users will not need it. +# BUILD=i686-pc-linux-gnu ./zcutil/build.sh +if [[ -z "${BUILD-}" ]]; then + BUILD=x86_64-unknown-linux-gnu +fi +if [[ -z "${HOST-}" ]]; then + HOST=x86_64-unknown-linux-gnu +fi + +# Allow override to $CC and $CXX for porters. Most users will not need it. +if [[ -z "${CC-}" ]]; then + CC=gcc +fi +if [[ -z "${CXX-}" ]]; then + CXX=g++ +fi + if [ "x$*" = 'x--help' ] then cat <