diff --git a/Cargo.lock b/Cargo.lock index 2b20b6cae..f428d2f1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,9 +69,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.55" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" [[package]] name = "arrayref" @@ -387,18 +387,18 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" dependencies = [ "libc", ] [[package]] name = "crossbeam-channel" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -417,10 +417,11 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" dependencies = [ + "autocfg", "cfg-if 1.0.0", "crossbeam-utils", "lazy_static", @@ -430,9 +431,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if 1.0.0", "lazy_static", @@ -527,9 +528,9 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", @@ -702,18 +703,18 @@ dependencies = [ [[package]] name = "gumdrop" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46571f5d540478cf70d2a42dd0d6d8e9f4b9cc7531544b93311e657b86568a0b" +checksum = "5bc700f989d2f6f0248546222d9b4258f5b02a171a431f8285a81c08142629e3" dependencies = [ "gumdrop_derive", ] [[package]] name = "gumdrop_derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05" +checksum = "729f9bd3449d77e7831a18abfb7ba2f99ee813dfd15b8c2167c9a54ba20aa99d" dependencies = [ "proc-macro2", "quote", @@ -865,9 +866,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" [[package]] name = "itoa" @@ -912,9 +913,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.119" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" [[package]] name = "libm" @@ -979,9 +980,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" dependencies = [ "cfg-if 1.0.0", ] @@ -1102,14 +1103,15 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" +checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" dependencies = [ "libc", "log", "miow", "ntapi", + "wasi 0.11.0+wasi-snapshot-preview1", "winapi", ] @@ -1211,9 +1213,9 @@ dependencies = [ [[package]] name = "num_threads" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" +checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" dependencies = [ "libc", ] @@ -1229,9 +1231,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "opaque-debug" @@ -1458,9 +1460,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57" dependencies = [ "proc-macro2", ] @@ -1564,9 +1566,9 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "10.2.0" +version = "10.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929f54e29691d4e6a9cc558479de70db7aa3d98cd6fe7ab86d7507aa2886b9d2" +checksum = "738bc47119e3eeccc7e94c4a506901aea5e7b4944ecd0829cbebf4af04ceda12" dependencies = [ "bitflags", ] @@ -1625,19 +1627,20 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55" dependencies = [ "getrandom 0.2.5", "redox_syscall", + "thiserror", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", @@ -1766,9 +1769,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "sketches-ddsketch" @@ -1823,9 +1826,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" dependencies = [ "proc-macro2", "quote", @@ -1958,9 +1961,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c650a8ef0cd2dd93736f033d21cbd1224c5a967aa0c258d00fcf7dafef9b9f" +checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -1970,9 +1973,9 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab026b18a46ac429e5c98bec10ca06424a97b3ad7b3949d9b4a102fff6623c4" +checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" dependencies = [ "crossbeam-channel", "time", @@ -1981,9 +1984,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" dependencies = [ "proc-macro2", "quote", @@ -1992,9 +1995,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" +checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" dependencies = [ "lazy_static", "valuable", @@ -2094,6 +2097,12 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.79" @@ -2160,9 +2169,9 @@ dependencies = [ [[package]] name = "which" -version = "4.2.4" +version = "4.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" dependencies = [ "either", "lazy_static", diff --git a/README.md b/README.md index 5a276efa5..6e9d87e2d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zcash 4.6.0-1 +Zcash 4.7.0-rc1 =========== diff --git a/configure.ac b/configure.ac index d9181b344..134a4a94a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,9 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) -define(_CLIENT_VERSION_MINOR, 6) +define(_CLIENT_VERSION_MINOR, 7) define(_CLIENT_VERSION_REVISION, 0) -define(_CLIENT_VERSION_BUILD, 51) +define(_CLIENT_VERSION_BUILD, 25) define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50))) define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1))) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/debian/changelog b/contrib/debian/changelog index 84754abf5..b8be90536 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,9 @@ +zcash (4.7.0~rc1) stable; urgency=medium + + * 4.7.0-rc1 release. + + -- Electric Coin Company Tue, 22 Mar 2022 21:14:02 +0000 + zcash (4.6.0+1) stable; urgency=medium * 4.6.0-1 release. diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh index 37b75ebf7..fb9496b54 100755 --- a/contrib/devtools/gen-manpages.sh +++ b/contrib/devtools/gen-manpages.sh @@ -6,6 +6,7 @@ SRCDIR=${SRCDIR:-$TOPDIR/src} MANDIR=${MANDIR:-$TOPDIR/doc/man} ZCASHD=${ZCASHD:-$SRCDIR/zcashd} +ZCASHD_WALLET_TOOL=${ZCASHD_WALLET_TOOL:-$SRCDIR/zcashd-wallet-tool} ZCASHCLI=${ZCASHCLI:-$SRCDIR/zcash-cli} ZCASHTX=${ZCASHTX:-$SRCDIR/zcash-tx} @@ -21,6 +22,7 @@ read -r -a ZECCOMMIT <<< "$(echo $ZECVERSTR | awk -F- '{ print $NF }')" # but has different outcomes for zcash-cli. echo "[COPYRIGHT]" > footer.h2m $ZCASHD --version | sed -n '1!p' >> footer.h2m +grep -v "Bitcoin Core Developers" footer.h2m >footer-no-bitcoin-copyright.h2m for cmd in $ZCASHD $ZCASHCLI $ZCASHTX; do cmdname="${cmd##*/}" @@ -28,4 +30,11 @@ for cmd in $ZCASHD $ZCASHCLI $ZCASHTX; do sed -i "s/\\\-$ZECCOMMIT//g" ${MANDIR}/${cmdname}.1 done +for cmd in $ZCASHD_WALLET_TOOL; do + cmdname="${cmd##*/}" + help2man -N --version-string=$ZECVER --include=footer-no-bitcoin-copyright.h2m -o ${MANDIR}/${cmdname}.1 ${cmd} + sed -i "s/\\\-$ZECCOMMIT//g" ${MANDIR}/${cmdname}.1 +done + rm -f footer.h2m +rm -f footer-no-bitcoin-copyright.h2m diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index f92f5a253..69b657ab1 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-4.6.0-1" +name: "zcash-4.7.0-rc1" enable_cache: true distro: "debian" suites: diff --git a/doc/book/src/SUMMARY.md b/doc/book/src/SUMMARY.md index 7bf90da81..f2ece211f 100644 --- a/doc/book/src/SUMMARY.md +++ b/doc/book/src/SUMMARY.md @@ -5,6 +5,7 @@ - [Metrics](user/metrics.md) - [Developer Documentation](dev.md) - [Rust in `zcashd`](dev/rust.md) + - [Regtest tips and hints](dev/regtest.md) - [Design](design.md) - [Chain state](design/chain-state.md) - ["Coins" view](design/coins-view.md) diff --git a/doc/book/src/dev/regtest.md b/doc/book/src/dev/regtest.md new file mode 100644 index 000000000..649d9107c --- /dev/null +++ b/doc/book/src/dev/regtest.md @@ -0,0 +1,165 @@ +# Regtest + +_Regtest_ ("regression test") is one of the three _network types_ +supported by Zcash, the others being `testnet` and `mainnet`. +Regtest is an entirely local, self-contained mode -- your node or nodes +do not talk with peers out in the world. It gives you complete +control of the state of the blockchain and the sequence of events. +You start with an empty blockchain (just the genesis block, block 0). +Blocks can be mined instantly, and must be created explicitly. + +You can run one or more regtest nodes on the same system. +The [RPC tests](https://github.com/zcash/zcash/tree/master/qa/rpc-tests) +use `regtest` mode (usually starting multiple nodes), but you may use it +manually and interactively to learn, test, and experiment. Most often +just one node is used in this case. + +## Example session + +Here's a sample session (after you've built `zcashd` and `zcash-cli`): + +``` +$ mkdir /tmp/regtest-datadir +$ cat </tmp/regtest-datadir/zcash.conf +regtest=1 +rpcuser=u +rpcpassword=p +rpcport=18132 +EOF +$ src/zcashd -daemon -datadir=/tmp/regtest-datadir +``` + +Watch `tmp/regtest-datadir/regtest/debug.log` to see what `zcashd` is doing. +It may also be useful to start `zcashd` with `-debug` to generate much +more logging. Now we can send RPCs to the node: + +``` +$ src/zcash-cli -datadir=/tmp/regtest-datadir getblockchaininfo +{ + "chain": "regtest", + "blocks": 0, + "initial_block_download_complete": false, + "headers": 0, + (...) +} +# Generate (mine) three blocks: +$ src/zcash-cli -datadir=/tmp/regtest-datadir generate 3 +[ + "05040271f43f78e3870a88697eba201aa361ea5802c69eadaf920ff376787242", + "0469f2df43dda69d521c482679b2db3c637b1721222511302584ac75e057c859", + "0ab7a26e7b3b5dfca077728de90da0cfd1c49e1edbc130a52de4062b1aecac75" +] +$ src/zcash-cli -datadir=/tmp/regtest-datadir getblockchaininfo +{ + "chain": "regtest", + "blocks": 3, + "initial_block_download_complete": true, + "headers": 3, + (...) +} +$ src/zcash-cli -datadir=/tmp/regtest-datadir stop +Zcash server stopping +$ +``` +## Network upgrades + +Zcash has adopted a series of +[network upgrades](https://github.com/zcash/zcash/blob/master/src/consensus/upgrades.cpp). +On `mainnet` and `testnet`, these activate at +fixed, known block heights ([example](https://github.com/zcash/zcash/blob/master/src/chainparams.cpp#L117)). +In `regtest` mode, you determine the activation heights. Upgrades may occur at +any height greater than 0, and multiple upgrades can occur at the same height. The upgrades +have a strict ordering (as shown in the upgrades source file); for example, Canopy can't +be activated before Blossom. + +You specify the upgrade heights using multiple `-nuparams=`_\_ arguments. +(The branch IDs are available in the +[upgrades.cpp file](https://github.com/zcash/zcash/blob/master/src/consensus/upgrades.cpp)) +It's convenient to add these to the configuration file, for example: +``` +$ cat <>/tmp/regtest-datadir/zcash.conf +nuparams=76b809bb:1 +nuparams=f5b9230b:5 +EOF +``` +(Alternatively, you can specify these on the `zcashd` command line.) +You need not activate every upgrade explicitly. The example activates Sapling +(branchID 76b809bb) at height 1; activating Sapling implies activating Overwinter, so this +is done automatically. Similarly, activating Heartwood at height 5 +also simultaneously activates Blossom. Since no later upgrades are specified, none +of them will activate, regardless of height reached. + +**IMPORTANT**: if you change the network upgrade heights from one +test run to the next, it's almost always necessary to start fresh +by removing the data directory, otherwise you'll encounter strange errors. + +You can see which network upgrades are currently active and which are pending +(using the above `nuparams` settings as an example): +``` +$ src/zcash-cli -datadir=/tmp/regtest-datadir generate 2 +$ src/zcash-cli -datadir=/tmp/regtest-datadir getblockchaininfo +{ + "blocks": 2, + (...) + "upgrades": { + "5ba81b19": { + "name": "Overwinter", + "activationheight": 1, + "status": "active", + "info": "See https://z.cash/upgrade/overwinter/ for details." + }, + "76b809bb": { + "name": "Sapling", + "activationheight": 1, + "status": "active", + "info": "See https://z.cash/upgrade/sapling/ for details." + }, + "2bb40e60": { + "name": "Blossom", + "activationheight": 5, + "status": "pending", + "info": "See https://z.cash/upgrade/blossom/ for details." + }, + "f5b9230b": { + "name": "Heartwood", + "activationheight": 5, + "status": "pending", + "info": "See https://z.cash/upgrade/heartwood/ for details." + } + }, + (...) +} +``` + +## Manual testing within an RPC (Python) test run + +It is often useful, either when debugging an issue or simply when you want to put +the node into a certain state, to use the RPC test framework to produce the desired +state and then be able to manually interrogate and modify that state using `zcash-cli` +to execute RPC calls. An easy way to do that is as follows: + +Add the line `import time; time.sleep(999999)` (the units are seconds) somewhere +within an RPC test to pause its execution at that point. Start the test, and then: + +``` +$ ps alx | grep zcashd +0 1000 96247 96246 20 0 1426304 123952 futex_ SLl+ pts/12 0:18 /g/zcash/src/zcashd -datadir=/tmp/test9d907s8a/96246/node0 -keypool=1 -discover=0 -rest -nuparams=5ba81b19:1 -nuparams=76b809bb:1 -debug=mempool -mempooltxcostlimit=8000 +0 1000 96274 96246 20 0 744092 85568 - RLl+ pts/12 0:05 /g/zcash/src/zcashd -datadir=/tmp/test9d907s8a/96246/node1 -keypool=1 -discover=0 -rest -nuparams=5ba81b19:1 -nuparams=76b809bb:1 -debug=mempool -mempooltxcostlimit=8000 +$ +``` +Now you can interact with the running test node by copy-and-pasting its +`-datadir` argument, for example: + +``` +$ src/zcash-cli -datadir=/tmp/test9d907s8a/96246/node0 getblockchaininfo +``` +(The other `zcashd` command-line arguments are generally not needed by +`zcash-cli`.) You can see the running node's debug log file: +``` +$ cat /tmp/test9d907s8a/96246/node0/regtest/debug.log +``` +or look at its configuration file. +``` +$ cat /tmp/test9d907s8a/96246/node0/zcash.conf +``` +In this way, the RPC test framework can teach us more about running `regtest` nodes. \ No newline at end of file diff --git a/doc/man/zcash-cli.1 b/doc/man/zcash-cli.1 index 51ebcfcf1..9f43c8edc 100644 --- a/doc/man/zcash-cli.1 +++ b/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-CLI "1" "January 2022" "zcash-cli v4.6.0-1" "User Commands" +.TH ZCASH-CLI "1" "March 2022" "zcash-cli v4.7.0-rc1" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v4.6.0-1 +zcash-cli \- manual page for zcash-cli v4.7.0-rc1 .SH DESCRIPTION -Zcash RPC client version v4.6.0\-1 +Zcash RPC client version v4.7.0\-rc1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . @@ -29,7 +29,7 @@ Specify configuration file (default: zcash.conf) .HP \fB\-datadir=\fR .IP -Specify data directory +Specify data directory (this path cannot use '~') .HP \fB\-stdin\fR .IP @@ -79,8 +79,8 @@ Timeout in seconds during HTTP requests, or 0 for no timeout. (default: In order to ensure you are adequately protecting your privacy when using Zcash, please see . -Copyright (C) 2009-2021 The Bitcoin Core Developers -Copyright (C) 2015-2021 The Zcash Developers +Copyright (C) 2009-2022 The Bitcoin Core Developers +Copyright (C) 2015-2022 The Zcash Developers This is experimental software. diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index ddb563fcf..fc2670f79 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASH-TX "1" "January 2022" "zcash-tx v4.6.0-1" "User Commands" +.TH ZCASH-TX "1" "March 2022" "zcash-tx v4.7.0-rc1" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v4.6.0-1 +zcash-tx \- manual page for zcash-tx v4.7.0-rc1 .SH DESCRIPTION -Zcash zcash\-tx utility version v4.6.0\-1 +Zcash zcash\-tx utility version v4.7.0\-rc1 .SS "Usage:" .TP zcash\-tx [options] [commands] @@ -91,8 +91,8 @@ Set register NAME to given JSON\-STRING In order to ensure you are adequately protecting your privacy when using Zcash, please see . -Copyright (C) 2009-2021 The Bitcoin Core Developers -Copyright (C) 2015-2021 The Zcash Developers +Copyright (C) 2009-2022 The Bitcoin Core Developers +Copyright (C) 2015-2022 The Zcash Developers This is experimental software. diff --git a/doc/man/zcashd-wallet-tool.1 b/doc/man/zcashd-wallet-tool.1 new file mode 100644 index 000000000..e7c7e4927 --- /dev/null +++ b/doc/man/zcashd-wallet-tool.1 @@ -0,0 +1,53 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. +.TH ZCASHD-WALLET-TOOL "1" "March 2022" "zcashd-wallet-tool v4.6.0-1-9d5762e47" "User Commands" +.SH NAME +zcashd-wallet-tool \- manual page for zcashd-wallet-tool v4.6.0-1-9d5762e47 +.SH SYNOPSIS +.B zcashd-wallet-tool +[\fI\,OPTIONS\/\fR] +.SH DESCRIPTION +.SS "Optional arguments:" +.TP +\fB\-\-help\fR +Print this help output +.TP +\fB\-\-conf\fR FILENAME +Specify configuration filename, relative to the data directory (default: zcash.conf) +.TP +\fB\-\-datadir\fR PATH +Specify data directory (this path cannot use '~') +.TP +\fB\-\-testnet\fR +Use the test chain +.TP +\fB\-\-rpcconnect\fR IPADDR +Send commands to node running on IPADDR (default: 127.0.0.1) +.TP +\fB\-\-rpcport\fR PORT +Connect to JSON\-RPC on PORT (default: 8232 or testnet: 18232) +.TP +\fB\-\-rpcuser\fR USERNAME +Username for JSON\-RPC connections +.TP +\fB\-\-rpcpassword\fR PASSWORD +Password for JSON\-RPC connections +.TP +\fB\-\-rpcclienttimeout\fR SECONDS +Timeout in seconds during HTTP requests, or 0 for no timeout. (default: 900) +.PP +Options can be given in GNU style (`\-\-conf=CONF` or `\-\-conf CONF`), +or in Bitcoin style with a single hyphen (`\-conf=CONF`). +.PP +The environment variable RUST_LOG controls debug output, e.g. +`RUST_LOG=debug`. +.SH COPYRIGHT + +In order to ensure you are adequately protecting your privacy when using Zcash, +please see . + +Copyright (C) 2015-2022 The Zcash Developers + +This is experimental software. + +Distributed under the MIT software license, see the accompanying file COPYING +or . diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index cb512e41c..7747cf1b2 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13. -.TH ZCASHD "1" "January 2022" "zcashd v4.6.0-1" "User Commands" +.TH ZCASHD "1" "March 2022" "zcashd v4.7.0-rc1" "User Commands" .SH NAME -zcashd \- manual page for zcashd v4.6.0-1 +zcashd \- manual page for zcashd v4.7.0-rc1 .SH DESCRIPTION -Zcash Daemon version v4.6.0\-1 +Zcash Daemon version v4.7.0\-rc1 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . @@ -49,7 +49,7 @@ Run in the background as a daemon and accept commands .HP \fB\-datadir=\fR .IP -Specify data directory +Specify data directory (this path cannot use '~') .HP \fB\-paramsdir=\fR .IP @@ -352,6 +352,14 @@ by TxID) Delete all wallet transactions and only recover those parts of the blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) +.HP +\fB\-walletrequirebackup=\fR +.IP +By default, the wallet will not allow generation of new spending keys & +addresses from the mnemonic seed until the backup of that seed has been +confirmed with the `zcashd\-wallet\-tool` utility. A user may start zcashd +with `\-walletrequirebackup=false` to allow generation of spending keys +even if the backup has not yet been confirmed. .PP ZeroMQ notification options: .HP @@ -558,8 +566,8 @@ console, 600 otherwise) In order to ensure you are adequately protecting your privacy when using Zcash, please see . -Copyright (C) 2009-2021 The Bitcoin Core Developers -Copyright (C) 2015-2021 The Zcash Developers +Copyright (C) 2009-2022 The Bitcoin Core Developers +Copyright (C) 2015-2022 The Zcash Developers This is experimental software. diff --git a/doc/release-notes/release-notes-4.7.0-rc1.md b/doc/release-notes/release-notes-4.7.0-rc1.md new file mode 100644 index 000000000..d428fa1d1 --- /dev/null +++ b/doc/release-notes/release-notes-4.7.0-rc1.md @@ -0,0 +1,538 @@ +Notable changes +=============== + +Mnemonic Recovery Phrases +------------------------- + +The zcashd wallet has been modified to support BIP 39, which describes how to +derive the wallet's HD seed from a mnemonic phrase. The mnemonic phrase will +be generated on load of the wallet, or the first time the wallet is unlocked, +and is available via the `z_exportwallet` RPC call. All new addresses produced +by the wallet are now derived from this seed using the HD wallet functionality +described in ZIP 32 and ZIP 316. For users upgrading an existing Zcashd wallet, +it is recommended that the wallet be backed up prior to upgrading to the 4.7.0 +Zcashd release. + +Following the upgrade to 4.7.0, Zcashd will require that the user confirm that +they have backed up their new emergency recovery phrase, which may be obtained +from the output of the `z_exportwallet` RPC call. This confirmation can be +performed manually using the `zcashd-wallet-tool` utility that is supplied +with this release (built or installed in the same directory as `zcashd`). +The wallet will not allow the generation of new addresses until this +confirmation has been performed. It is recommended that after this upgrade, +that funds tied to preexisting addresses be migrated to newly generated +addresses so that all wallet funds are recoverable using the emergency +recovery phrase going forward. If you choose not to migrate funds in this +fashion, you will continue to need to securely back up the entire `wallet.dat` +file to ensure that you do not lose access to existing funds; EXISTING FUNDS +WILL NOT BE RECOVERABLE USING THE EMERGENCY RECOVERY PHRASE UNLESS THEY HAVE +BEEN MOVED TO A NEWLY GENERATED ADDRESS FOLLOWING THE 4.7.0 UPGRADE. + +New RPC Methods +--------------- + +- 'walletconfirmbackup' This newly created API checks a provided emergency + recovery phrase against the wallet's emergency recovery phrase; if the phrases + match then it updates the wallet state to allow the generation of new addresses. + This backup confirmation workflow can be disabled by starting zcashd with + `-requirewalletbackup=false` but this is not recommended unless you know what + you're doing (and have otherwise backed up the wallet's recovery phrase anyway). + For security reasons, this RPC method is not intended for use via zcash-cli + but is provided to enable `zcashd-wallet-tool` and other third-party wallet + interfaces to satisfy the backup confirmation requirement. Use of the + `walletconfirmbackup` API via zcash-cli would risk that the recovery phrase + being confirmed might be leaked via the user's shell history or the system + process table; `zcashd-wallet-tool` is specifically provided to avoid this + problem. +- 'z_getbalanceforviewingkey' This newly created API allows a user to obtain + balance information for funds visible to a Sapling or Unified full + viewing key; if a Sprout viewing key is provided, this method allows + retrieval of the balance only in the case that the wallet controls the + corresponding spending key. + +RPC Changes +----------- + +- The results of the 'dumpwallet' and 'z_exportwallet' RPC methods have been modified + to now include the wallet's newly generated emergency recovery phrase as part of the + exported data. + +- The results of the 'getwalletinfo' RPC have been modified to return two new fields: + `mnemonic_seedfp` and `legacy_seedfp`, the latter of which replaces the field that + was previously named `seedfp`. + +Wallet +------ + +'z_sendmany' +------------ + +- The 'z_sendmany' RPC call no longer permits Sprout recipients in the + list of recipient addresses. Transactions spending Sprout funds will + still result in change being sent back into the Sprout pool, but no + other `Sprout->Sprout` transactions will be constructed by the Zcashd + wallet. + +- The restriction that prohibited `Sprout->Sapling` transactions has been + lifted; however, since such transactions reveal the amount crossing + pool boundaries, they must be explicitly enabled via a parameter to + the 'z_sendmany' call. + +- A new string parameter, `privacyPolicy`, has been added to the list of + arguments accepted by `z_sendmany`. This parameter enables the caller to + control what kind of information they permit `zcashd` to reveal when creating + the transaction. If the transaction can only be created by revealing more + information than the given strategy permits, `z_sendmany` will return an + error. The parameter defaults to `LegacyCompat`, which applies the most + restrictive strategy `FullPrivacy` when a Unified Address is present as the + sender or a recipient, and otherwise preserves existing behaviour (which + corresponds to the `AllowFullyTransparent` policy). + +- Since Sprout outputs are no longer created (with the exception of change) + 'z_sendmany' no longer generates payment disclosures (which were only + available for Sprout outputs) when the `-paymentdisclosure` experimental + feature flag is set. + +Changelog +========= + +Charlie O'Keefe (2): + Update base image used by Dockerfile from debian 10 to debian 11 + Remove stretch (debian 9), add bullseye (debian 11) in gitian descriptor + +Conrado Gouvea (1): + Add comment to zcash_script_new_precomputed_tx about references to input buffers + +Daira Hopwood (25): + Avoid a warning by explicitly calling drop. + Replace call to drop with zeroization. + contrib/devtools/rust-deps-graph.sh: allow overriding the arguments to `cargo deps`. + Cosmetic whitespace change + Make a zcashd-wallet-tool executable. + Attempt to fix linking problem on ARM. + Add some text about choosing location of the physical backup. Add TODO for better handling of file not found and permission errors. + Move `wallet_tool.rs` from `src/rust/src` into `src/rust/bin`. Also add a brief description of `zcashd-wallet-tool` to `src/rust/README.md`. + Improved error handling. + The recovery phrase confirmation and `zcashd-wallet-tool` are being introduced in zcashd v4.7.0. + Refactor use of `export_path` as suggested. + Tweak the wording of the fallback messages when the terminal cannot be automatically cleared. + Simplify extraction of recovery phrase. + Improve memory hygiene, and use -stdin to pass the recovery phrase to the child zcash-cli process. + Cleanups to error handling for the first invocation of zcash-cli. + Use the tracing crate for debugging. + Improve error message when the config file cannot be found, taking into account -conf and -datadir. + Ensure the buffer used in `prompt` is zeroized even on error. + Document that '~' cannot be used in `-datadir` (see #5661). + Set `meta` for `-datadir` option. + Simplify debug tracing. + Correct the fallback instruction for how to clear the terminal on macOS: pressing Command + K also clears scrollback. + Include $(bin_SCRIPTS) in `check-symbols`, `check-security`, and `clean` targets. Checking for stack canaries in `check-security` is disabled for Rust executables (Rust does support `-Z stack-protector=all` but only for the nightly compiler). + qa/zcash/full_test_suite.py: enable `test_rpath_runpath` for Rust binaries, and reenable `test_fortify_source` for C++ binaries. + Tweaks to message text. + +Dimitris Apostolou (1): + Fix typos + +Jack Grigg (65): + wallet: Implement `z_getnewaccount` + wallet: Implement `z_getaddressforaccount` + wallet: Implement `z_listunifiedreceivers` + wallet: Show UAs owned by the wallet in `z_viewtransaction` + wallet: Reverse order of arguments to z_getaddressforaccount + rust: Add missing Orchard receiver check in UA parsing + rust: Add missing checks for shielded UFVK items + rust: Add missing checks for transparent UFVK items + wallet: Implement `z_getbalanceforaccount` + wallet: Fix account generation bug + wallet: Implement `z_getbalanceforaddress` + wallet: Don't show Sapling receivers from UAs in `z_listaddresses` + wallet: Show UAs instead of Sapling receivers in `z_listunspent` + wallet: Remove `CWallet::GetKeyFromPool` + wallet: Store internal transparent keys in the keypool + wallet: Separate counters for external and internal transparent keys + Add Orchard recipient support to the transaction builder + Make `TransactionBuilder::AddOrchardOutput` memo optional + Return UFVK from `CWallet::GenerateNewUnifiedSpendingKey` + Rename to `ZcashdUnifiedSpendingKey::GetSaplingKey` for consistency + Trial-decrypt candidate Sapling receivers with all wallet UFVKs + Add mappings from Orchard receivers to IVKs to the wallet + Add Orchard to default UA receiver types + Fix semantic merge conflicts + Select Orchard receivers preferentially from UAs + Regenerate `TxDigests` after rebuilding tx with Orchard bundle + qa: Bump all postponed dependencies + qa: Postpone recent CCache release + depends: Update Rust to 1.59.0 + depends: Update Clang / libcxx to LLVM 13.0.1 + cargo update + rust: Fix clippy lint + Ensure the view's best Orchard anchor matches the previous block + Add missing `view.PopAnchor(_, ORCHARD)` in `DisconnectBlock` + Add RPC test for the Orchard commitment tree bug on first NU5 testnet + Use `std::optional` in `CValidationInterface::GetAddressForMining` + Select Orchard receivers from UA miner addresses once NU5 activates + miner: Manually add dummy Orchard output with Orchard miner address + rpc: Handle keypool exhaustion separately in `generate` RPC + depends: Revert to `libc++ 13.0.0-3` for Windows cross-compile + Add Orchard spend support to the transaction builder + wallet: Alter `OrchardWallet::GetSpendInfo` to return the spending key + Improve FFI interface documentation + Refactor `CWallet::GenerateChangeAddressForAccount` + Add unit tests for `SpendableInputs::LimitToAmount` + Fix bug in `SpendableInputs::LimitToAmount` + Select spendable inputs based on recipient and change pool types + Implement opportunistic shielding in `SpendableInputs::LimitToAmount` + Add Orchard cases to note selection logic + Add Orchard to change address generation + Add support for sending Orchard funds in `z_sendmany` + Set default Orchard anchor confirmations to 1 + build: Fix `zcash/address/orchard.hpp` filename in `src/Makefile.am` + z_sendmany: Replace `allowRevealedAmount` with `privacyStrategy` + z_sendmany: Expand `privacyPolicy` cases + build: Add missing `util/match.h` to `src/Makefile.am` + wallet: Add seedfp to transparent keys in dumpwallet / z_exportwallet + Fix bugs in wallet_addresses RPC test + wallet: Fix bugs in `listaddresses` + wallet: Fix Sapling address bug in `listaddresses` + wallet: Fix expected `listaddresses` sources in `rpc_wallet_tests` + qa: Exclude `native_libtinfo` from dependency update checks + cargo update + make-release.py: Versioning changes for 4.7.0-rc1. + make-release.py: Updated manpages for 4.7.0-rc1. + +John Newbery (1): + Log calls to getblocktemplate + +Jonas Schnelli (1): + [Wallet] add HD xpriv to dumpwallet + +Kris Nuttycombe (243): + Derive random HD seeds from ZIP-339 seed phrases. + Add support for externally searching for valid Sapling diversifiers. + Adds basic unified spending key derivation. + Add unified full viewing keys and Zcash-internal unified addresses. + Use the default UA-based Sapling address for the saplingmigration tool. + Fix tests for wallet operations on legacy Sapling keys. + Remove unused forward declaration. + Update librustzcash dependency version. + Apply suggestions from code review + Derive transparent keys from mnemonic seed. + Generate legacy Sapling addresses from the mnemonic seed. + Replace account ID with ZIP-0032 key path in listaddresses output. + Use legacy address for Sapling migration until UA functionality is available to the RPC tests. + Revert change to the type of GenerateNewKey + Fix wallet import/export test + Use 0x7FFFFFFF for the legacy account ID. + Require backup of the emergency recovery phrase. + Use hardened derivation for the legacy Sapling key at the address index level. + Address comments from code review. + Restore legacy HD seed storage & retrieval tests. + Fix spurious test passage. + Move CKeyMetadata back to wallet.h + Clean up format of recovery information in the wallet dump. + Use SecureString for mnemonic phrase. + Apply suggestions from code review + Fix diversifier_index_t less than operator. + Restore FindAddress comment + Fix transparent BIP-44 keypaths. + Fix naming of unified spending & full viewing keys + Fix max transparent diversifier index. + Clean up handling of mnemonic seed metadata. + Restore legacy HDSeed encryption tests. + Style fix in BIP 32 path account parsing test. + Do not strip quotes when verifying mnemonic seed. + Apply suggestions from code review + Fix name of menmonic entropy length constant. + Fix polymorphism of string serialization. + Document mnemonic-seed-related RPC method changes & update changelog. + Minor cleanup of the code that searches for a valid transparent key. + Generalize keypath parsing over account id. + Update documentation for GenerateNewSeed. + Use MnemonicSeed instead of HDSeed where appropriate. + Add diversifier_index_t::ToTransparentChildIndex + Only maintain CKeyID for the transparent part of ZcashdUnifiedAddress + Minor naming fixes + Parameterize HD keypath parsing by coin type. + Fix error message text to refer to zcashd-wallet-tool rather than the RPC method. + Apply suggestions from code review + Minor textual fixes to release notes. + Improve documentation of the `-walletrequirebackup` zcashd wallet configuration option. + Add libzcash::AccountId type. + Adds Orchard Address, IncomingViewingKey, FullViewingKey, and SpendingKey types. + Apply suggestions from code review + Update orchard & librustzcash dependency versions. + Remove incorrect FFI method documentation. + Remove Orchard spending key equality implementation. + Refine structure of Zcash address generation. + Use CKeyID and CScriptID instead of new P2PKH/P2SHAddress classes. + Remove ZcashdUnifiedAddress in favor of UnifiedAddress + Update to ufvk zcash_address build. + Adds SaplingDiversifiableFullViewingKey + Add Rust FFI components for unified full viewing keys. + Add UnifiedFullViewingKey type. + Apply suggestions from code review + Add tests for ufvk roundtrip serialization. + Apply suggestions from code review + Apply suggestions from code review + Apply suggestions from code review + Add functions for generating BIP-44 and ZIP-32 keypaths + Check the output of zip339_phrase_to_seed in MnemonicSeed initialization. + Compute key id for UFVKs. + Add ZcashdUnifiedKeyMetadata and libzcash::ReceiverType + Add unified key components to the transparent & Sapling wallet parts. + Store ufvks to the wallet database. + Add unified address tracking to KeyStore + Load unified full viewing keys from the walletdb. + Add key metadata to walletdb. + Add unified address generation. + AddTransparentSecretKey does not need to take a CExtKey + Add newly generated transparent UA receivers to the wallet. + Add CWallet::GetUnifiedForReceiver + Add tests for keystore storage and retrieval of UFVKs. + Add test for wallet UA generation & detection. + Add test for CKeyStore::AddUnifiedAddress + Fix handling of unified full viewing key metadata. + Apply suggestions from code review + Only derive ZcashdUnifiedFullViewingKey from UnifiedFullViewingKey + Rename `ZcashdUnifiedSpendingKeyMetadata` -> `ZcashdUnifiedAccount` + Remove unused ufvkid argument from AddTransparentSecretKey + Ensure that unified address metadata is always correctly populated. + Apply suggestions from code review + Make `FindAddress` correctly check for maximum transparent child index. + Use Bip44TransparentAccountKeyPath() for Bip44AccountChains keypath construction. + Improve documentation of UFVK/UA metadata storage semantics. + Apply suggestions from code review + Fix encoding order of unified addresses. + Remove the `InvalidEncoding` type from key & address variants. + Use CKeyID and CScriptID instead of new P2PKH/P2SHAddress classes. + Remove spurious uses of HaveSpendingKeyForPaymentAddress + Add raw transparent address types to PaymentAddress + Remove spurious variant from asyncrpcoperation_sendmany + Remove `RawAddress` + Replace `DecodeDestination` in `GetMinerAddress` with `DecodePaymentAddress` + Remove uses of KeyIO::DecodeDestination + Remove a use of KeyIO::DecodeDestination in z_shieldcoinbase + Use libzcash::PaymentAddress instead of std::string in mergetoaddress + Improve error messages in the case of taddr decoding failure. + Apply suggestions from code review + Apply suggestions from code review + Use transaction builder for asyncrpcoperation_sendmany. + Transaction builder must not set consensus branch ID for V4 transactions. + Fix conditions around dust thresholds. + Require an explicit flag to allow cross-pool transfers in z_sendmany. + Apply suggestions from code review + Return z_sendmany errors synchronously when possible. + Update release notes to reflect z_sendmany changes + Move FindSpendableInputs to CWallet + Replace the badly-named `PaymentSource` with `ZTXOSelector` + Add support for unified addresses to CWallet::ToZTXOSelector + Replace `HaveSpendingKeyForAddress` with checks at ZTXOSelector construction. + Modify CWallet::FindSpendableInputs to use ZTXOSelector + Add CWallet::FindAccountForSelector + Add RecipientAddress type. + Use libzcash::RecipientAddress for z_sendmany recipients. + Apply suggestions from code review + Rename ZTXOSelector::SpendingKeysAvailable -> RequireSpendingKeys + Make `FindSpendableInputs` respect `ZTXOSelector::RequireSpendingKeys()` + Clarify documentation of CWallet::FindAccountForSelector + Use `ZTXOSelector::IncludesSapling` rather than selector internals. + Add documentation for `UnifiedAddress::GetPreferredRecipientAddress` + Add correct selection of change addresses to z_sendmany + Add a first-class type for transparent full viewing keys. + Implement OVK selection for z_sendmany. + Implement derivation of the internal Sapling spending key. + Use a BIP 44 change address for change when sending from legacy t-addrs. + Add a check for internal vs. external outputs to wallet_listreceived test. + Fix z_sendmany handling of transparent OVK derivation in the ANY_TADDR case. + Simplify determination of valid change types. + Add failing tests for z_sendmany ANY_TADDR -> UA and UA -> + Add gtest for change address derivation. + Fix variable shadowing in change address derivation & add change IVK to the keystore. + GenerateLegacySaplingZKey only needs to return an address, not an extfvk. + Rename AddSaplingIncomingViewingKey -> AddSaplingPaymentAddress + Do not add Sapling addresses to the wallet by default when adding extfvks. + Fix a bug in the generation of addresses from UFVKs + Add accessor method for Sapling IVKs to SaplingDiversifiableFullViewingKey + Address TODOs in rpc-tests/wallet-accounts.py + Add a few additional cases to z_sendmany RPC tests. + Update librustzcash dependency. + Fix nondeterministic test error (checking for the wrong error case). + Use z_shieldcoinbase for Sprout funds in wallet_listreceived tests. + Apply suggestions from code review + Apply suggestions from code review. + Fix nondeterministic test failure in wallet_listreceivedby.py + Fix locking in z_getbalanceforaddress and z_getbalanceforaccount + Replace z_getbalanceforaddress with z_getbalanceforviewingkey + Clarify documentation of z_getbalanceforviewingkey for Sprout viewing keys. + Implement PaymentAddressBelongsToWallet for unified addresses. + Add unified address support to GetSourceForPaymentAddress + Address comments from review. + Add change field to z_listreceivedbyaddress for transparent addrs. + Fix missing std::variant header that was breaking Darwin builds. + Add test vectors for UFVK derivation + Rename sapling-specific zip32 FFI methods. + Make SaveRecipientMappings polymorphic in RecipientMapping type. + Add Rust backend for Orchard components of the wallet. + Add GetFilteredNotes to Orchard wallet. + Add test for Orchard wallet note detection. + Move parsing of unified addresses to UnifiedAddress. + Remove txid field from TxNotes + Apply suggestions from code review + Add various bits of documentation + Add Orchard components to unified address + Add Orchard components to unified full viewing keys + Add Orchard components to unified spending keys + Remove OrchardSpendingKey serialization code + Select Orchard notes in FindSpendableInputs + GenerateNewKey must be guarded by a cs_wallet lock + Filter returned Orchard notes by minimum confirmations. + Log outpoint for failed Sapling witness lookup. + Add a roundtrip test for Orchard merkle frontier serialization from the C++ side. + Add test for Orchard contribution to z_gettotalbalance + Respect minDepth argument for Orchard notes in GetFilteredNotes + Update MSRV for lints. + Update incrementalmerkletree version + Split LoadWalletTx from AddToWallet + Fix missing locks for GenerateNewUnifiedSpendingKey tests. + Record when notes are detected as being spent in the Orchard wallet. + Reset Orchard wallet state when rescanning from below NU5 activation. + Check wallet latest anchor against hashFinalOrchardRoot in ChainTip. + Remove assertions that require Orchard wallet persistence to satisfy. + Add a test for Orchard note detection. + Assert we never attempt to checkpoint the Orchard wallet at a negative block height. + Apply suggestions from code review + Add an `InPoint` type to the Orchard wallet to fix incorrect conflict data. + Apply suggestions from code review + Fix missing update to `last_checkpoint` on rewind. + Track mined-ness instead of spent-ness of notes in the Orchard wallet. + Apply suggestions from code review + Respect maxDepth for Orchard notes in GetFilteredNotes + Set number of confirmations for Orchard notes returned by FindSpendableInputs + Update walletTx with decrypted Orchard action metadata. + Persist Orchard action index/IVK mappings in CWalletTx + Restore decrypted notes to the wallet. + Update tests with rollback checks. + Apply suggestions from code review + Restore mined block heights when restoring decrypted notes. + Apply suggestions from code review + Return std::optional from CExtKey::Master + Ensure that Orchard spentness information is repopulated by LoadUnifiedCaches. + Modify `join_network` in tests to skip mempool resync. + Address suggestions from code review on #5637 + Apply suggestions from code review + Serialize the Orchard note commitment tree to the wallet. + Update orchard_wallet_add_notes_from_bundle documentation. + Derive the new mnemonic seed from the legacy HD seed, if one is available. + Fix indentation. + Apply suggestions from code review + Explicitly specify the change address in the Sapling migration. + Add TODO for Orchard z_listunspent integration. + Document that z_getnewaccount and z_getaddressforaccount replace z_getnewaddress. + Add orchard support to z_getnotescount + Document that Orchard keys are not supported in z_importkey help + Return the correct error for `z_getbalance` if a UA does not correspond to an account in the wallet.. + Update documentation of z_importviewingkey and z_exportviewingkey to address unified keys. + Add UnifiedAddress variant to ZTXOSelector + Eliminate redundancy between the wallet and the keystore. + Simplify retrieval of unified account by address. + Add OrchardWallet::GetTxActions + Apply suggestions from code review + Update z_viewtransaction documentation to correctly represent field names. + Add debug printing for receivers and recipient addresses. + Correctly report change outputs in z_viewtransaction. + Return the default unified address if we have the UFVK but no address metadata. + Fix missing AllowRevealedSenders flag in test. + Uncomment addtional tests that depend on z_viewtransaction. + Minor rename & documentation improvement. + Add RecipientType to GetPaymentAddressForRecipient result. + Make CWallet::DefaultReceiverTypes height-dependent. + Return failure rather than asserting on WriteRecipientMapping failure. + Lock cs_main for accesses to chainActive in GetPaymentAddressForRecipient. + Fix legacy address handling in CWallet::GetPaymentAddressForRecipient + Documentation fix for UnifiedAddressForReciever (+ spelling corrections.) + +Larry Ruane (12): + add ParseArbitraryInt() for diversifier index + add -orchardwallet experimental feature flag + Add new and modify existing Orchard RPCs, non-functional + mining: submitblock: log detailed equihash solution error + allow UA as z_shieldcoinbase destination + fix minconf parsing for z_getbalanceforaccount and z_getbalanceforaddress + Update z_listreceivedbyaddress to support unified addresses (5467) + fix wallet_listreceived.py, add blockdata to taddr output + z_listreceivedbyaddress: reject UA component addr (#5537) + add functional test + document global variables + update listaddresses RPC for UAs, Orchard + +Marius Kjærstad (1): + Update copyright year to 2022 + +Pieter Wuille (2): + Fix csBestBlock/cvBlockChange waiting in rpc/mining + Modernize best block mutex/cv/hash variable naming + +Sean Bowe (5): + wallet: consolidate unified key/address/account map reconstruction + wallet: restore Orchard secret keys from mnemonic seed + wallet: restore orchard address to IVK mappings during wallet loading + wallet: rather than assert, error in case of inconsistency between FVK and address + wallet: add logging for failure cases in unified cache loading + +Steven Smith (8): + Lock cs_main prior to calling blockToJSON + Mark z_gettotalbalance and dumpwallet as deprecated + Add Orchard support to the z_gettreestate RPC + Update transaction size estimation to include V5 transactions + Extend uniqueness check in z_sendmany to UA receivers + Load previously persisted sent transaction recipient metadata back into the wallet. + Add Orchard & unified address support to z_viewtransaction. + Ensure z_viewtransaction returns Orchard details + +Taylor Hornby (1): + Untested, not working yet, use libtinfo from the debian packages + +sasha (12): + on Arch only, use Debian's libtinfo5_6.0 to satisfy clang + explain the 0x0f0f[..]0f0f powLimit constant for regtest + remove superfluous space at end of native_packages line + gtests ordering: change wallet filename in WriteZkeyDirectToDb + gtests ordering: ContextualCheckBlockTest's TearDown deactivates Blossom + gtests ordering: CheckBlock.VersionTooLow calls SelectParams(MAIN) + implement AtomicTimer::zeroize() that resets start_time and total_time + gtests ordering: make Metrics.GetLocalSolPS idempotent + gtests ordering: clean up wallet files before each WalletZkeysTest + make librustzcash_init_zksnark_params idempotent + move proof parameter loading out of gtest/main.cpp and into utiltest.cpp + Call LoadProofParameters() in gtests that need proofs + +Ying Tong Lai (18): + Move SendManyRecipient to wallet.h and introduce optional ua field. + SendTransaction: Introduce recipients argument. + Implement read and write for (txid, recipient) -> ua mapping. + z_sendmany: Only get ua if decoded is ua variant. + ShieldToAddress: Factor out static shieldToAddress() helper. + Docfixes. + CSerializeRecipientAddress: add Read method and make constructor private. + WriteRecipientMapping: Check that receiver exists in UA. + wallet_sendmany_any_taddr.py: Test sending from a change taddr. + wallet_sendmany_any_taddr.py: Test sending output from expired tx. + FindSpendableInputs: Add nDepth < 0 check. + wallet_sendmany_any_taddr.py: Expect expired tx to be ignored. + Orchard: invalidate mempool transactions that use orphaned anchors. + coins_tests.cpp: Add Orchard nullifier to TxWithNullifiers(). + coins_tests: Update tests to include Orchard case. + CWallet::GetConflictS: Handle conflicting Orchard spends. + z_getbalance: Handle Unified Address case. + Adapt RPC tests to use z_getbalance for UAs. + +ying tong (2): + Apply docfixes from code review + Style improvements in RPC tests. + +Zancas Wilcox (4): + blake2b/s is integrated into hashlib, drop external python package dependency + update doctest in gtest suite to prefer hashlib + enforce usage of the get_tests comptool interface as ComparisonTestFramework method + All implementations of ComparisonTestFramework were overriding num_nodes + diff --git a/qa/rpc-tests/mining_shielded_coinbase.py b/qa/rpc-tests/mining_shielded_coinbase.py index 9d9fc517f..c2b57b0ea 100755 --- a/qa/rpc-tests/mining_shielded_coinbase.py +++ b/qa/rpc-tests/mining_shielded_coinbase.py @@ -127,8 +127,8 @@ class ShieldCoinbaseTest (BitcoinTestFramework): self.nodes[1].z_getnewaccount() node1_addr0 = self.nodes[1].z_getaddressforaccount(0) assert_equal(node1_addr0['account'], 0) - assert_equal(set(node1_addr0['pools']), set(['transparent', 'sapling', 'orchard'])) - node1_ua = node1_addr0['unifiedaddress'] + assert_equal(set(node1_addr0['receiver_types']), set(['p2pkh', 'sapling', 'orchard'])) + node1_ua = node1_addr0['address'] # Set node 1's miner address to the UA self.nodes[1].stop() diff --git a/qa/rpc-tests/orchard_reorg.py b/qa/rpc-tests/orchard_reorg.py index 58d0bf8d9..b9c4b5a0c 100755 --- a/qa/rpc-tests/orchard_reorg.py +++ b/qa/rpc-tests/orchard_reorg.py @@ -51,8 +51,8 @@ class OrchardReorgTest(BitcoinTestFramework): account = self.nodes[0].z_getnewaccount()['account'] addr = self.nodes[0].z_getaddressforaccount(account, ['orchard']) assert_equal(addr['account'], account) - assert_equal(set(addr['pools']), set(['orchard'])) - ua = addr['unifiedaddress'] + assert_equal(set(addr['receiver_types']), set(['orchard'])) + ua = addr['address'] # Before mining any Orchard notes, finalorchardroot should be the empty Orchard root. assert_equal( diff --git a/qa/rpc-tests/remove_sprout_shielding.py b/qa/rpc-tests/remove_sprout_shielding.py index e57ef3003..5d8411e6e 100755 --- a/qa/rpc-tests/remove_sprout_shielding.py +++ b/qa/rpc-tests/remove_sprout_shielding.py @@ -85,7 +85,7 @@ class RemoveSproutShieldingTest (BitcoinTestFramework): sprout_addr = self.nodes[1].z_getnewaddress('sprout') assert_raises_message( JSONRPCException, - "Sending funds into the Sprout pool is not supported by z_sendmany", + "Sending funds into the Sprout value pool is not supported by z_sendmany", self.nodes[0].z_sendmany, taddr_0, [{"address": sprout_addr, "amount": 1}]) print("taddr -> Sprout z_sendmany tx rejected at Canopy activation on node 0") diff --git a/qa/rpc-tests/wallet_accounts.py b/qa/rpc-tests/wallet_accounts.py index fc9f5905a..9e0eb4335 100755 --- a/qa/rpc-tests/wallet_accounts.py +++ b/qa/rpc-tests/wallet_accounts.py @@ -64,8 +64,8 @@ class WalletAccountsTest(BitcoinTestFramework): # Generate the first address for account 0. addr0 = self.nodes[0].z_getaddressforaccount(0) assert_equal(addr0['account'], 0) - assert_equal(set(addr0['pools']), set(['transparent', 'sapling', 'orchard'])) - ua0 = addr0['unifiedaddress'] + assert_equal(set(addr0['receiver_types']), set(['p2pkh', 'sapling', 'orchard'])) + ua0 = addr0['address'] # We pick mnemonic phrases to ensure that we can always generate the default # address in account 0; this is however not necessarily at diversifier index 0. @@ -82,35 +82,35 @@ class WalletAccountsTest(BitcoinTestFramework): # The second address for account 0 is different to the first address. addr0_2 = self.nodes[0].z_getaddressforaccount(0) assert_equal(addr0_2['account'], 0) - assert_equal(set(addr0_2['pools']), set(['transparent', 'sapling', 'orchard'])) - ua0_2 = addr0_2['unifiedaddress'] + assert_equal(set(addr0_2['receiver_types']), set(['p2pkh', 'sapling', 'orchard'])) + ua0_2 = addr0_2['address'] assert(ua0 != ua0_2) # We can generate a fully-shielded address. addr0_3 = self.nodes[0].z_getaddressforaccount(0, ['sapling', 'orchard']) assert_equal(addr0_3['account'], 0) - assert_equal(set(addr0_3['pools']), set(['sapling', 'orchard'])) - ua0_3 = addr0_3['unifiedaddress'] + assert_equal(set(addr0_3['receiver_types']), set(['sapling', 'orchard'])) + ua0_3 = addr0_3['address'] # We can generate an address without a Sapling receiver. - addr0_4 = self.nodes[0].z_getaddressforaccount(0, ['transparent', 'orchard']) + addr0_4 = self.nodes[0].z_getaddressforaccount(0, ['p2pkh', 'orchard']) assert_equal(addr0_4['account'], 0) - assert_equal(set(addr0_4['pools']), set(['transparent', 'orchard'])) - ua0_4 = addr0_4['unifiedaddress'] + assert_equal(set(addr0_4['receiver_types']), set(['p2pkh', 'orchard'])) + ua0_4 = addr0_4['address'] # The first address for account 1 is different to account 0. addr1 = self.nodes[0].z_getaddressforaccount(1) assert_equal(addr1['account'], 1) - assert_equal(set(addr1['pools']), set(['transparent', 'sapling', 'orchard'])) - ua1 = addr1['unifiedaddress'] + assert_equal(set(addr1['receiver_types']), set(['p2pkh', 'sapling', 'orchard'])) + ua1 = addr1['address'] assert(ua0 != ua1) # The UA contains the expected receiver kinds. - self.check_receiver_types(ua0, ['transparent', 'sapling', 'orchard']) - self.check_receiver_types(ua0_2, ['transparent', 'sapling', 'orchard']) + self.check_receiver_types(ua0, ['p2pkh', 'sapling', 'orchard']) + self.check_receiver_types(ua0_2, ['p2pkh', 'sapling', 'orchard']) self.check_receiver_types(ua0_3, [ 'sapling', 'orchard']) - self.check_receiver_types(ua0_4, ['transparent', 'orchard']) - self.check_receiver_types(ua1, ['transparent', 'sapling', 'orchard']) + self.check_receiver_types(ua0_4, ['p2pkh', 'orchard']) + self.check_receiver_types(ua1, ['p2pkh', 'sapling', 'orchard']) # The balances of the accounts are all zero. self.check_balance(0, 0, ua0, {}) @@ -195,7 +195,7 @@ class WalletAccountsTest(BitcoinTestFramework): # Send Orchard funds from the UA. print('Sending account funds to Orchard-only UA') node1account = self.nodes[1].z_getnewaccount()['account'] - node1orchard = self.nodes[1].z_getaddressforaccount(node1account, ['orchard'])['unifiedaddress'] + node1orchard = self.nodes[1].z_getaddressforaccount(node1account, ['orchard'])['address'] recipients = [{'address': node1orchard, 'amount': Decimal('1')}] opid = self.nodes[0].z_sendmany(ua0, recipients, 1, 0) txid = wait_and_assert_operationid_status(self.nodes[0], opid) diff --git a/qa/rpc-tests/wallet_addresses.py b/qa/rpc-tests/wallet_addresses.py index 23041d2b6..591c43000 100755 --- a/qa/rpc-tests/wallet_addresses.py +++ b/qa/rpc-tests/wallet_addresses.py @@ -68,7 +68,7 @@ class WalletAddressesTest(BitcoinTestFramework): account = self.nodes[0].z_getnewaccount()['account'] sprout_1 = self.nodes[0].z_getnewaddress('sprout') sapling_1 = self.nodes[0].z_getnewaddress('sapling') - unified_1 = self.nodes[0].z_getaddressforaccount(account)['unifiedaddress'] + unified_1 = self.nodes[0].z_getaddressforaccount(account)['address'] types_and_addresses = [ ('sprout', sprout_1), ('sapling', sapling_1), @@ -111,7 +111,7 @@ class WalletAddressesTest(BitcoinTestFramework): assert_equal(self.nodes[0].getblockcount(), 2) # Sprout address generation is no longer allowed sapling_2 = self.nodes[0].z_getnewaddress('sapling') - unified_2 = self.nodes[0].z_getaddressforaccount(account)['unifiedaddress'] + unified_2 = self.nodes[0].z_getaddressforaccount(account)['address'] types_and_addresses = [ ('sapling', sapling_2), ('unified', unified_2), diff --git a/qa/rpc-tests/wallet_listreceived.py b/qa/rpc-tests/wallet_listreceived.py index 86a2ab6f0..7ae6b22ad 100755 --- a/qa/rpc-tests/wallet_listreceived.py +++ b/qa/rpc-tests/wallet_listreceived.py @@ -375,7 +375,7 @@ class ListReceivedTest (BitcoinTestFramework): # Create a unified address on one node, try z_listreceivedbyaddress on another node account = self.nodes[0].z_getnewaccount()['account'] r = self.nodes[0].z_getaddressforaccount(account) - unified_addr = r['unifiedaddress'] + unified_addr = r['address'] # this address isn't in node1's wallet assert_raises_message( JSONRPCException, @@ -386,16 +386,16 @@ class ListReceivedTest (BitcoinTestFramework): r = node.z_getnewaccount() account = r['account'] r = node.z_getaddressforaccount(account) - unified_addr = r['unifiedaddress'] + unified_addr = r['address'] receivers = node.z_listunifiedreceivers(unified_addr) assert_equal(len(receivers), 3) - assert 'transparent' in receivers + assert 'p2pkh' in receivers assert 'sapling' in receivers assert 'orchard' in receivers assert_raises_message( JSONRPCException, "The provided address is a bare receiver from a Unified Address in this wallet.", - node.z_listreceivedbyaddress, receivers['transparent'], 0) + node.z_listreceivedbyaddress, receivers['p2pkh'], 0) assert_raises_message( JSONRPCException, "The provided address is a bare receiver from a Unified Address in this wallet.", @@ -411,7 +411,7 @@ class ListReceivedTest (BitcoinTestFramework): self.generate_and_sync(height+5) # Create a UTXO that unified_address's transparent component references, on node1 - outputs = {receivers['transparent']: 0.2} + outputs = {receivers['p2pkh']: 0.2} txid_taddr = node.sendmany("", outputs) r = node.z_listreceivedbyaddress(unified_addr, 0) @@ -449,18 +449,18 @@ class ListReceivedTest (BitcoinTestFramework): acct2 = self.nodes[1].z_getnewaccount()['account'] addrResO = self.nodes[1].z_getaddressforaccount(acct1, ['orchard']) - assert_equal(addrResO['pools'], ['orchard']) - uao = addrResO['unifiedaddress'] + assert_equal(addrResO['receiver_types'], ['orchard']) + uao = addrResO['address'] addrResSO = self.nodes[1].z_getaddressforaccount(acct2, ['sapling', 'orchard']) - assert_equal(addrResSO['pools'], ['sapling', 'orchard']) - uaso = addrResSO['unifiedaddress'] + assert_equal(addrResSO['receiver_types'], ['sapling', 'orchard']) + uaso = addrResSO['address'] self.nodes[0].sendtoaddress(taddr, 4.0) self.generate_and_sync(height+2) acct_node0 = self.nodes[0].z_getnewaccount()['account'] - ua_node0 = self.nodes[0].z_getaddressforaccount(acct_node0, ['sapling', 'orchard'])['unifiedaddress'] + ua_node0 = self.nodes[0].z_getaddressforaccount(acct_node0, ['sapling', 'orchard'])['address'] opid = self.nodes[1].z_sendmany(taddr, [ {'address': uao, 'amount': 1, 'memo': my_memo}, diff --git a/qa/rpc-tests/wallet_orchard.py b/qa/rpc-tests/wallet_orchard.py index 2a1ee85a6..19806dfdb 100755 --- a/qa/rpc-tests/wallet_orchard.py +++ b/qa/rpc-tests/wallet_orchard.py @@ -36,8 +36,8 @@ class WalletOrchardTest(BitcoinTestFramework): acct1 = self.nodes[1].z_getnewaccount()['account'] addrRes1 = self.nodes[1].z_getaddressforaccount(acct1, ['orchard']) assert_equal(acct1, addrRes1['account']) - assert_equal(addrRes1['pools'], ['orchard']) - ua1 = addrRes1['unifiedaddress'] + assert_equal(addrRes1['receiver_types'], ['orchard']) + ua1 = addrRes1['address'] # Verify that we have only an Orchard component receiver_types = self.nodes[0].z_listunifiedreceivers(ua1) @@ -50,7 +50,7 @@ class WalletOrchardTest(BitcoinTestFramework): acct2 = self.nodes[2].z_getnewaccount()['account'] addrRes2 = self.nodes[2].z_getaddressforaccount(acct2, ['sapling', 'orchard']) assert_equal(acct2, addrRes2['account']) - ua2 = addrRes2['unifiedaddress'] + ua2 = addrRes2['address'] saplingAddr2 = self.nodes[2].z_listunifiedreceivers(ua2)['sapling'] recipients = [{"address": saplingAddr2, "amount": Decimal('10')}] @@ -102,7 +102,7 @@ class WalletOrchardTest(BitcoinTestFramework): acct3 = self.nodes[3].z_getnewaccount()['account'] addrRes3 = self.nodes[3].z_getaddressforaccount(acct3, ['sapling', 'orchard']) assert_equal(acct3, addrRes3['account']) - ua3 = addrRes3['unifiedaddress'] + ua3 = addrRes3['address'] recipients = [{"address": ua3, "amount": Decimal('1')}] myopid = self.nodes[2].z_sendmany(ua2, recipients, 1, 0) @@ -177,5 +177,54 @@ class WalletOrchardTest(BitcoinTestFramework): {'pools': {'orchard': {'valueZat': Decimal('100000000')}}, 'minimum_confirmations': 1}, self.nodes[3].z_getbalanceforaccount(acct3)) + # Split the network again + self.split_network() + + # Spend some of acct3's funds on the 2/3 side of the split + recipients = [{"address": ua2, "amount": Decimal('0.5')}] + myopid = self.nodes[3].z_sendmany(ua3, recipients, 1, 0) + rollback_tx = wait_and_assert_operationid_status(self.nodes[3], myopid) + + self.sync_all() + self.nodes[2].generate(1) + self.sync_all() + + assert_equal( + {'pools': {'orchard': {'valueZat': Decimal('950000000')}}, 'minimum_confirmations': 1}, + self.nodes[2].z_getbalanceforaccount(acct2)) + + # Generate a majority chain on the 0/1 side of the split, then + # re-join the network. + self.nodes[1].generate(10) + self.join_network() + + # split 2/3's chain should have been rolled back, so their txn should have been + # un-mined and returned to the mempool + assert_equal(set([rollback_tx]), set(self.nodes[3].getrawmempool())) + + # acct2's balance is back to not contain the Orchard->Orchard value + assert_equal( + {'pools': {'orchard': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 1}, + self.nodes[2].z_getbalanceforaccount(acct2)) + + # acct3's sole Orchard note is spent by a transaction in the mempool, so our + # confirmed balance is currently 0 + assert_equal( + {'pools': {}, 'minimum_confirmations': 1}, + self.nodes[3].z_getbalanceforaccount(acct3)) + + # Manually resend the transaction in node 3's mempool + self.nodes[2].resendwallettransactions() + + # Sync the network + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + + # The un-mined transaction should now have been re-mined + assert_equal( + {'pools': {'orchard': {'valueZat': Decimal('950000000')}}, 'minimum_confirmations': 1}, + self.nodes[2].z_getbalanceforaccount(acct2)) + if __name__ == '__main__': WalletOrchardTest().main() diff --git a/qa/rpc-tests/wallet_sapling.py b/qa/rpc-tests/wallet_sapling.py index 19f88739d..28c86cae0 100755 --- a/qa/rpc-tests/wallet_sapling.py +++ b/qa/rpc-tests/wallet_sapling.py @@ -169,7 +169,7 @@ class WalletSaplingTest(BitcoinTestFramework): ) raise AssertionError("Should have thrown an exception") except JSONRPCException as e: - assert_equal("Sending funds into the Sprout pool is not supported by z_sendmany", e.error['message']) + assert_equal("Sending funds into the Sprout value pool is not supported by z_sendmany", e.error['message']) if __name__ == '__main__': WalletSaplingTest().main() diff --git a/qa/rpc-tests/wallet_shieldcoinbase_ua_nu5.py b/qa/rpc-tests/wallet_shieldcoinbase_ua_nu5.py index 30a7f22fb..6d472462d 100755 --- a/qa/rpc-tests/wallet_shieldcoinbase_ua_nu5.py +++ b/qa/rpc-tests/wallet_shieldcoinbase_ua_nu5.py @@ -15,23 +15,28 @@ class WalletShieldCoinbaseUANU5(WalletShieldCoinbaseTest): # this function may be called no more than once assert(self.account is None) self.account = node.z_getnewaccount()['account'] - self.addr = node.z_getaddressforaccount(self.account)['unifiedaddress'] + self.addr = node.z_getaddressforaccount(self.account)['address'] return self.addr def test_check_balance_zaddr(self, node, expected): balances = node.z_getbalanceforaccount(self.account) assert('transparent' not in balances['pools']) assert('sprout' not in balances['pools']) - # assert('sapling' not in balances['pools']) + # Remove the following after Orchard support is added to z_shieldcoinbase sapling_balance = balances['pools']['sapling']['valueZat'] assert_equal(sapling_balance, expected * COIN) - # assert_equal(balances['pools']['orchard']['valueZat'], expected * COIN) + # TODO: Uncomment after Orchard support is added to z_shieldcoinbase + #assert('sapling' not in balances['pools']) + #orchard_balance = balances['pools']['orchard']['valueZat'] + #assert_equal(orchard_balance, expected * COIN) # While we're at it, check that z_listunspent only shows outputs with # the Unified Address (not the Orchard receiver), and of the expected # type. unspent = node.z_listunspent(1, 999999, False, [self.addr]) assert_equal( + # TODO: Fix after Orchard support is added to z_shieldcoinbase + #[{'type': 'orchard', 'address': self.addr} for _ in unspent], [{'type': 'sapling', 'address': self.addr} for _ in unspent], [{'type': x['type'], 'address': x['address']} for x in unspent], ) diff --git a/qa/rpc-tests/wallet_shieldcoinbase_ua_sapling.py b/qa/rpc-tests/wallet_shieldcoinbase_ua_sapling.py index f07ef656e..3f1b88e4d 100755 --- a/qa/rpc-tests/wallet_shieldcoinbase_ua_sapling.py +++ b/qa/rpc-tests/wallet_shieldcoinbase_ua_sapling.py @@ -14,7 +14,7 @@ class WalletShieldCoinbaseUASapling(WalletShieldCoinbaseTest): # this function may be called no more than once assert(self.account is None) self.account = node.z_getnewaccount()['account'] - self.addr = node.z_getaddressforaccount(self.account)['unifiedaddress'] + self.addr = node.z_getaddressforaccount(self.account)['address'] return self.addr def test_check_balance_zaddr(self, node, expected): diff --git a/qa/rpc-tests/wallet_z_sendmany.py b/qa/rpc-tests/wallet_z_sendmany.py index dd1559305..196331c09 100755 --- a/qa/rpc-tests/wallet_z_sendmany.py +++ b/qa/rpc-tests/wallet_z_sendmany.py @@ -167,7 +167,7 @@ class WalletZSendmanyTest(BitcoinTestFramework): # Get a new unified account on node 2 & generate a UA n0account0 = self.nodes[0].z_getnewaccount()['account'] - n0ua0 = self.nodes[0].z_getaddressforaccount(n0account0)['unifiedaddress'] + n0ua0 = self.nodes[0].z_getaddressforaccount(n0account0)['address'] # Change went to a fresh address, so use `ANY_TADDR` which # should hold the rest of our transparent funds. @@ -297,12 +297,12 @@ class WalletZSendmanyTest(BitcoinTestFramework): # Generate a new account with two new addresses. n1account = self.nodes[1].z_getnewaccount()['account'] - n1ua0 = self.nodes[1].z_getaddressforaccount(n1account)['unifiedaddress'] - n1ua1 = self.nodes[1].z_getaddressforaccount(n1account)['unifiedaddress'] + n1ua0 = self.nodes[1].z_getaddressforaccount(n1account)['address'] + n1ua1 = self.nodes[1].z_getaddressforaccount(n1account)['address'] # Send funds to the transparent receivers of both addresses. for ua in [n1ua0, n1ua1]: - taddr = self.nodes[1].z_listunifiedreceivers(ua)['transparent'] + taddr = self.nodes[1].z_listunifiedreceivers(ua)['p2pkh'] self.nodes[0].sendtoaddress(taddr, 2) self.sync_all() diff --git a/qa/zcash/updatecheck.py b/qa/zcash/updatecheck.py index cf1206c99..311fc0994 100755 --- a/qa/zcash/updatecheck.py +++ b/qa/zcash/updatecheck.py @@ -328,7 +328,9 @@ def main(): # packages.mk is not a dependency, it just specifies the list of them all. "packages", # This package doesn't have conventional version numbers - "native_cctools" + "native_cctools", + # This package is pinned specifically for Arch. + "native_libtinfo", ] print_row("NAME", "STATUS", "CURRENT VERSION", "NEWER VERSIONS") diff --git a/src/Makefile.am b/src/Makefile.am index a8824b4e8..efeb9f0f3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -259,6 +259,7 @@ BITCOIN_CORE_H = \ wallet/asyncrpcoperation_shieldcoinbase.h \ wallet/crypter.h \ wallet/db.h \ + wallet/orchard.h \ wallet/paymentdisclosure.h \ wallet/paymentdisclosuredb.h \ wallet/rpcwallet.h \ diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 4db484960..ae41a806a 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -53,6 +53,7 @@ zcash_gtest_SOURCES += \ gtest/test_timedata.cpp \ gtest/test_transaction.cpp \ gtest/test_transaction_builder.cpp \ + gtest/test_transaction_builder.h \ gtest/test_txid.cpp \ gtest/test_upgrades.cpp \ gtest/test_validation.cpp \ diff --git a/src/clientversion.h b/src/clientversion.h index ccb4acdd6..0a4a0e222 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -16,9 +16,9 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 4 -#define CLIENT_VERSION_MINOR 6 +#define CLIENT_VERSION_MINOR 7 #define CLIENT_VERSION_REVISION 0 -#define CLIENT_VERSION_BUILD 51 +#define CLIENT_VERSION_BUILD 25 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true diff --git a/src/gtest/test_checktransaction.cpp b/src/gtest/test_checktransaction.cpp index f95bd124b..c1b126ca4 100644 --- a/src/gtest/test_checktransaction.cpp +++ b/src/gtest/test_checktransaction.cpp @@ -1109,7 +1109,7 @@ TEST(ChecktransactionTests, BadTxReceivedOverNetwork) } } -TEST(ChecktransactionTests, InvalidShieldedCoinbase) { +TEST(ChecktransactionTests, InvalidSaplingShieldedCoinbase) { RegtestActivateSapling(); CMutableTransaction mtx = GetValidTransaction(); @@ -1140,7 +1140,7 @@ TEST(ChecktransactionTests, InvalidShieldedCoinbase) { RegtestDeactivateHeartwood(); } -TEST(ChecktransactionTests, HeartwoodAcceptsShieldedCoinbase) { +TEST(ChecktransactionTests, HeartwoodAcceptsSaplingShieldedCoinbase) { LoadProofParameters(); RegtestActivateHeartwood(false, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); @@ -1358,3 +1358,266 @@ TEST(ChecktransactionTests, CanopyAcceptsZeroVPubOld) { RegtestDeactivateCanopy(); } + +TEST(ChecktransactionTests, InvalidOrchardShieldedCoinbase) { + LoadProofParameters(); + RegtestActivateCanopy(); + + CMutableTransaction mtx; + mtx.fOverwintered = true; + mtx.nVersionGroupId = ZIP225_VERSION_GROUP_ID; + mtx.nVersion = ZIP225_TX_VERSION; + mtx.nConsensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_NU5].nBranchId; + + // Make it an invalid shielded coinbase, by creating an all-dummy Orchard bundle. + mtx.vin.resize(1); + mtx.vin[0].prevout.SetNull(); + mtx.orchardBundle = orchard::Builder(false, true, uint256()) + .Build().value() + .ProveAndSign({}, uint256()).value(); + + CTransaction tx(mtx); + EXPECT_TRUE(tx.IsCoinBase()); + + // Before NU5, v5 transactions are rejected. + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-sapling-tx-version-group-id", false, "")).Times(1); + ContextualCheckTransaction(tx, state, Params(), 10, 57); + + RegtestActivateNU5(); + + // From NU5, the Orchard actions are allowed but invalid (undecryptable). + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-action-invalid-ciphertext", false, "")).Times(1); + ContextualCheckTransaction(tx, state, Params(), 10, 57); + + RegtestDeactivateNU5(); +} + +TEST(ChecktransactionTests, NU5AcceptsOrchardShieldedCoinbase) { + LoadProofParameters(); + RegtestActivateNU5(); + auto chainparams = Params(); + + uint256 orchardAnchor; + auto builder = orchard::Builder(false, true, orchardAnchor); + + // Shielded coinbase outputs must be recoverable with an all-zeroes ovk. + RawHDSeed rawSeed(32, 0); + GetRandBytes(rawSeed.data(), 32); + auto to = libzcash::OrchardSpendingKey::ForAccount(HDSeed(rawSeed), Params().BIP44CoinType(), 0) + .ToFullViewingKey() + .ToIncomingViewingKey() + .Address(0); + uint256 ovk; + builder.AddOutput(ovk, to, CAmount(123456), std::nullopt); + + // orchard::Builder pads to two Actions, but does so using a "no OVK" policy for + // dummy outputs, which violates coinbase rules requiring all shielded outputs to + // be recoverable. We manually add a dummy output to sidestep this issue. + // TODO: If/when we have funding streams going to Orchard recipients, this dummy + // output can be removed. + builder.AddOutput(ovk, to, 0, std::nullopt); + + auto bundle = builder + .Build().value() + .ProveAndSign({}, uint256()).value(); + + CMutableTransaction mtx; + mtx.fOverwintered = true; + mtx.nVersionGroupId = ZIP225_VERSION_GROUP_ID; + mtx.nVersion = ZIP225_TX_VERSION; + mtx.nConsensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_NU5].nBranchId; + + mtx.vin.resize(1); + mtx.vin[0].prevout.SetNull(); + mtx.orchardBundle = bundle; + + CTransaction tx(mtx); + EXPECT_TRUE(tx.IsCoinBase()); + + // Write the transaction bytes out so we can modify them to test failure cases. + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << tx; + + // Define some constants to use when calculating offsets to modify below. + const size_t HEADER_SIZE = 4 + 4 + 4 + 4 + 4; + const size_t TRANSPARENT_BUNDLE_SIZE = 1 + 32 + 4 + 1 + 4 + 1; + const size_t SAPLING_BUNDLE_SIZE = 1 + 1; + const size_t ORCHARD_BUNDLE_START = (HEADER_SIZE + TRANSPARENT_BUNDLE_SIZE + SAPLING_BUNDLE_SIZE); + const size_t ORCHARD_BUNDLE_CMX_OFFSET = (ORCHARD_BUNDLE_START + ZC_ZIP225_ORCHARD_NUM_ACTIONS_SIZE + 32 + 32 + 32); + const size_t ORCHARD_CMX_SIZE = 32; + + // Verify the transaction is the expected size. + size_t txsize = ORCHARD_BUNDLE_START + ZC_ZIP225_ORCHARD_BASE_SIZE + ZC_ZIP225_ORCHARD_MARGINAL_SIZE * 2; + EXPECT_EQ(ss.size(), txsize); + + // Transaction should fail with a bad public cmx. + { + auto cmxBad = uint256S("1234"); + std::vector txBytes(ss.begin(), ss.end()); + std::copy(cmxBad.begin(), cmxBad.end(), txBytes.data() + ORCHARD_BUNDLE_CMX_OFFSET); + + CDataStream ssBad(txBytes, SER_DISK, PROTOCOL_VERSION); + CTransaction tx; + ssBad >> tx; + EXPECT_TRUE(tx.IsCoinBase()); + + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-action-invalid-ciphertext", false, "")).Times(1); + ContextualCheckTransaction(tx, state, chainparams, 10, 57); + } + + // Transaction should fail with a bad encCiphertext. + { + std::vector txBytes(ss.begin(), ss.end()); + for (int i = 0; i < ZC_SAPLING_ENCCIPHERTEXT_SIZE; i++) { + txBytes[ORCHARD_BUNDLE_CMX_OFFSET + ORCHARD_CMX_SIZE + i] = 0; + } + + CDataStream ssBad(txBytes, SER_DISK, PROTOCOL_VERSION); + CTransaction tx; + ssBad >> tx; + EXPECT_TRUE(tx.IsCoinBase()); + + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-action-invalid-ciphertext", false, "")).Times(1); + ContextualCheckTransaction(tx, state, chainparams, 10, 57); + } + + // Transaction should fail with a bad outCiphertext. + { + std::vector txBytes(ss.begin(), ss.end()); + for (int i = 0; i < ZC_SAPLING_OUTCIPHERTEXT_SIZE; i++) { + txBytes[ORCHARD_BUNDLE_CMX_OFFSET + ORCHARD_CMX_SIZE + ZC_SAPLING_ENCCIPHERTEXT_SIZE + i] = 0; + } + + CDataStream ssBad(txBytes, SER_DISK, PROTOCOL_VERSION); + CTransaction tx; + ssBad >> tx; + EXPECT_TRUE(tx.IsCoinBase()); + + MockCValidationState state; + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-action-invalid-ciphertext", false, "")).Times(1); + ContextualCheckTransaction(tx, state, chainparams, 10, 57); + } + + // Test the success case. + { + MockCValidationState state; + EXPECT_TRUE(ContextualCheckTransaction(tx, state, chainparams, 10, 57)); + } + + RegtestDeactivateNU5(); +} + +// Check that the consensus rules relevant to valueBalanceOrchard, and +// vOrchardActions from https://zips.z.cash/protocol/protocol.pdf#txnencoding +// are applied to coinbase transactions. +TEST(ChecktransactionTests, NU5EnforcesOrchardRulesOnShieldedCoinbase) { + LoadProofParameters(); + RegtestActivateNU5(); + auto chainparams = Params(); + + uint256 orchardAnchor; + auto builder = orchard::Builder(false, true, orchardAnchor); + + // Shielded coinbase outputs must be recoverable with an all-zeroes ovk. + RawHDSeed rawSeed(32, 0); + GetRandBytes(rawSeed.data(), 32); + auto to = libzcash::OrchardSpendingKey::ForAccount(HDSeed(rawSeed), Params().BIP44CoinType(), 0) + .ToFullViewingKey() + .ToIncomingViewingKey() + .Address(0); + uint256 ovk; + builder.AddOutput(ovk, to, CAmount(1000), std::nullopt); + + // orchard::Builder pads to two Actions, but does so using a "no OVK" policy for + // dummy outputs, which violates coinbase rules requiring all shielded outputs to + // be recoverable. We manually add a dummy output to sidestep this issue. + // TODO: If/when we have funding streams going to Orchard recipients, this dummy + // output can be removed. + builder.AddOutput(ovk, to, 0, std::nullopt); + + auto bundle = builder + .Build().value() + .ProveAndSign({}, uint256()).value(); + + CMutableTransaction mtx; + mtx.fOverwintered = true; + mtx.nVersionGroupId = ZIP225_VERSION_GROUP_ID; + mtx.nVersion = ZIP225_TX_VERSION; + mtx.nConsensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_NU5].nBranchId; + + mtx.vin.resize(1); + mtx.vin[0].prevout.SetNull(); + mtx.vin[0].scriptSig << 123; + mtx.orchardBundle = bundle; + + CTransaction tx(mtx); + EXPECT_TRUE(tx.IsCoinBase()); + + // Write the transaction bytes out so we can modify them to test failure cases. + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << tx; + + // Define some constants to use when calculating offsets to modify below. + const size_t HEADER_SIZE = 4 + 4 + 4 + 4 + 4; + const size_t TRANSPARENT_BUNDLE_SIZE = 1 + 32 + 4 + 1 + 2 + 4 + 1; + const size_t SAPLING_BUNDLE_SIZE = 1 + 1; + const size_t ORCHARD_BUNDLE_START = (HEADER_SIZE + TRANSPARENT_BUNDLE_SIZE + SAPLING_BUNDLE_SIZE); + const size_t ORCHARD_BUNDLE_VALUEBALANCE_OFFSET = ( + ORCHARD_BUNDLE_START + + ZC_ZIP225_ORCHARD_NUM_ACTIONS_SIZE + + ZC_ZIP225_ORCHARD_ACTION_SIZE * 2 + + ZC_ZIP225_ORCHARD_FLAGS_SIZE); + + // Verify the transaction is the expected size. + size_t txsize = ORCHARD_BUNDLE_START + ZC_ZIP225_ORCHARD_BASE_SIZE + ZC_ZIP225_ORCHARD_MARGINAL_SIZE * 2; + EXPECT_EQ(ss.size(), txsize); + + // Coinbase transaction should fail non-contextual checks with valueBalanceSapling + // out of range. + { + std::vector txBytes(ss.begin(), ss.end()); + uint64_t valueBalanceBad = htole64(MAX_MONEY + 1); + std::copy((char*)&valueBalanceBad, (char*)&valueBalanceBad + 8, txBytes.data() + ORCHARD_BUNDLE_VALUEBALANCE_OFFSET); + + CDataStream ssBad(txBytes, SER_DISK, PROTOCOL_VERSION); + CTransaction tx; + EXPECT_THROW((ssBad >> tx), std::ios_base::failure); + + // We can't actually reach the CheckTransactionWithoutProofVerification + // consensus rule, because Rust is doing this validation at parse time. + // MockCValidationState state; + // EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false, "")).Times(1); + // EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state)); + } + { + std::vector txBytes(ss.begin(), ss.end()); + uint64_t valueBalanceBad = htole64(-MAX_MONEY - 1); + std::copy((char*)&valueBalanceBad, (char*)&valueBalanceBad + 8, txBytes.data() + ORCHARD_BUNDLE_VALUEBALANCE_OFFSET); + + CDataStream ssBad(txBytes, SER_DISK, PROTOCOL_VERSION); + CTransaction tx; + EXPECT_THROW((ssBad >> tx), std::ios_base::failure); + + // We can't actually reach the CheckTransactionWithoutProofVerification + // consensus rule, because Rust is doing this validation at parse time. + // MockCValidationState state; + // EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false, "")).Times(1); + // EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state)); + } + + // Test the success case. + { + // The unmodified coinbase transaction should pass non-contextual checks. + MockCValidationState state; + EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state)); + + // Coinbase transaction should pass contextual checks, as bindingSigOrchard + // consensus rule is not enforced here. + EXPECT_TRUE(ContextualCheckTransaction(tx, state, chainparams, 10, 57)); + } + + RegtestDeactivateNU5(); +} diff --git a/src/main.cpp b/src/main.cpp index aabd26d2e..3fe983b00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6850,11 +6850,12 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string for (uint256 hash : vEraseQueue) EraseOrphanTx(hash); } - // TODO: currently, prohibit joinsplits and shielded spends/outputs from entering mapOrphans + // TODO: currently, prohibit joinsplits and shielded spends/outputs/actions from entering mapOrphans else if (fMissingInputs && tx.vJoinSplit.empty() && tx.vShieldedSpend.empty() && - tx.vShieldedOutput.empty()) + tx.vShieldedOutput.empty() && + !tx.GetOrchardBundle().IsPresent()) { AddOrphanTx(tx, pfrom->GetId()); diff --git a/src/net.cpp b/src/net.cpp index cbb967017..6f0ab4577 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1912,17 +1912,23 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.InitMessage(_("Loading banlist...")); // Load addresses from banlist.dat nStart = GetTimeMillis(); - CBanDB bandb; - banmap_t banmap; - if (bandb.Read(banmap)) { - CNode::SetBanned(banmap); // thread save setter - CNode::SetBannedSetDirty(false); // no need to write down, just read data - CNode::SweepBanned(); // sweep out unused entries + if (!GetBoolArg("-reindex", false)) { + CBanDB bandb; + banmap_t banmap; + if (bandb.Read(banmap)) { + CNode::SetBanned(banmap); // thread save setter + CNode::SetBannedSetDirty(false); // no need to write down, just read data + CNode::SweepBanned(); // sweep out unused entries - LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", - banmap.size(), GetTimeMillis() - nStart); + LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", + banmap.size(), GetTimeMillis() - nStart); + } else { + LogPrintf("Invalid or missing banlist.dat; recreating\n"); + CNode::SetBannedSetDirty(true); // force write + DumpBanlist(); + } } else { - LogPrintf("Invalid or missing banlist.dat; recreating\n"); + LogPrintf("Clearing banlist.dat for reindex\n"); CNode::SetBannedSetDirty(true); // force write DumpBanlist(); } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 754805dc3..2daa9f1f3 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -51,21 +51,24 @@ UniValue getinfo(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"version\": xxxxx, (numeric) the server version\n" + " \"build\": xxxxx, (string) the build number\n" + " \"subversion\": xxxxx, (string) the server sub-version identifier\n" " \"protocolversion\": xxxxx, (numeric) the protocol version\n" - " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total Zcash balance of the wallet\n" + " \"walletversion\": xxxxx, (numeric, optional) the wallet version, if wallet functionality is enabled\n" + " \"balance\": xxxxxxx, (numeric, optional) the total Zcash balance of the wallet, if wallet functionality is enabled\n" " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" " \"timeoffset\": xxxxx, (numeric) the time offset (deprecated; always 0)\n" " \"connections\": xxxxx, (numeric) the number of connections\n" " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" - " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" - " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" - " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"keypoololdest\": xxxxxx, (numeric, optional) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool, if wallet functionality is enabled\n" + " \"keypoolsize\": xxxx, (numeric, optional) how many new keys are pre-generated\n" + " \"unlocked_until\": ttt, (numeric, optional) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked, if wallet functionality is available and the wallet is encrypted\n" " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n" " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" - " \"errors\": \"...\" (string) any error messages\n" + " \"errors\": \"...\" (string) message describing the latest or highest-priority error\n" + " \"errorstimestamp\": \"...\" (string) timestamp associated with the latest or highest-priority error\n" "}\n" "\nExamples:\n" + HelpExampleCli("getinfo", "") diff --git a/src/rust/bin/wallet_tool.rs b/src/rust/bin/wallet_tool.rs index 832ec9073..fcb1ca5b4 100644 --- a/src/rust/bin/wallet_tool.rs +++ b/src/rust/bin/wallet_tool.rs @@ -124,7 +124,7 @@ enum WalletToolError { #[error("zcashd -exportdir option not set")] ExportDirNotSet, - #[error("Could not parse a recovery phrase from the export file")] + #[error("Could not parse a unique recovery phrase from the export file")] RecoveryPhraseNotFound, #[error("Unexpected EOF in input")] @@ -475,9 +475,20 @@ fn run(opts: &CliOptions) -> anyhow::Result<()> { println!(concat!( "\nThe backup of the emergency recovery phrase for the zcashd\n", "wallet has been successfully confirmed 🙂. You can now use the\n", - "zcashd RPC methods that create keys and addresses in that wallet.\n\n", - "If you use other wallets, their recovery information will need\n", - "to be backed up separately.\n" + "zcashd RPC methods that create keys and addresses in that wallet.\n", + "\n", + "The zcashd wallet might also contain keys that are *not* derived\n", + "from the emergency recovery phrase. If you lose the 'wallet.dat'\n", + "file then any funds associated with these keys would be lost. To\n", + "minimize this risk, it is recommended to send all funds from this\n", + "wallet into new addresses and discontinue the use of legacy addresses.\n", + "Note that even after doing so, there is the possibility that\n", + "additional funds could be sent to legacy addresses (if any exist)\n", + "and so it is necessary to keep backing up the 'wallet.dat' file\n", + "in any case.\n", + "\n", + "If you use other wallets, their recovery information will need to\n", + "be backed up separately.\n" )); Ok(()) }) diff --git a/src/rust/include/rust/orchard/wallet.h b/src/rust/include/rust/orchard/wallet.h index a6425df4f..72bd1ff1f 100644 --- a/src/rust/include/rust/orchard/wallet.h +++ b/src/rust/include/rust/orchard/wallet.h @@ -333,6 +333,18 @@ void orchard_wallet_get_potential_spends( push_txid_callback_t push_cb ); +/** + * Returns a vector of transaction IDs for transactions that have been observed as + * spending the given nullifier by using the `push_cb` callback to push transaction + * IDs onto the provided result vector. + */ +void orchard_wallet_get_potential_spends_from_nullifier( + const OrchardWalletPtr* wallet, + const unsigned char *nullifier, + void* resultVector, + push_txid_callback_t push_cb + ); + /** * Fetches the information needed to spend the wallet note at the given outpoint, * relative to the current root known to the wallet of the Orchard commitment diff --git a/src/rust/src/wallet.rs b/src/rust/src/wallet.rs index d4bfd0408..e2be5ed12 100644 --- a/src/rust/src/wallet.rs +++ b/src/rust/src/wallet.rs @@ -1125,6 +1125,24 @@ pub extern "C" fn orchard_wallet_get_potential_spends( } } +#[no_mangle] +pub extern "C" fn orchard_wallet_get_potential_spends_from_nullifier( + wallet: *const Wallet, + nullifier: *const [c_uchar; 32], + result: Option, + push_cb: Option, +) { + let wallet = unsafe { wallet.as_ref() }.expect("Wallet pointer may not be null."); + let nullifier = + Nullifier::from_bytes(unsafe { nullifier.as_ref() }.expect("nullifier may not be null.")); + + if let Some(inpoints) = wallet.potential_spends.get(&nullifier.unwrap()) { + for inpoint in inpoints { + unsafe { (push_cb.unwrap())(result, inpoint.txid.as_ref()) }; + } + } +} + #[no_mangle] pub extern "C" fn orchard_wallet_get_spend_info( wallet: *const Wallet, diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index a1d5a8ffd..d12e77595 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -806,6 +806,126 @@ TEST(WalletTests, GetConflictedSaplingNotes) { } } +TEST(WalletTests, GetConflictedOrchardNotes) { + LoadProofParameters(); + + auto consensusParams = RegtestActivateNU5(); + TestWallet wallet(Params()); + wallet.GenerateNewSeed(); + + LOCK2(cs_main, wallet.cs_wallet); + + // Create an account. + auto ufvk = wallet.GenerateNewUnifiedSpendingKey().first; + auto fvk = ufvk.GetOrchardKey().value(); + auto ivk = fvk.ToIncomingViewingKey(); + libzcash::diversifier_index_t j(0); + auto recipient = ivk.Address(j); + + uint256 orchardAnchor; + + // Generate transparent funds + CBasicKeyStore keystore; + CKey tsk = AddTestCKeyToKeyStore(keystore); + auto tkeyid = tsk.GetPubKey().GetID(); + auto scriptPubKey = GetScriptForDestination(tkeyid); + + // Generate a bundle containing output note A. + auto builder = TransactionBuilder(consensusParams, 1, orchardAnchor, &keystore); + builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000); + builder.AddOrchardOutput(std::nullopt, recipient, 40000, {}); + auto tx = builder.Build().GetTxOrThrow(); + CWalletTx wtx {&wallet, tx}; + + // Fake-mine the transaction + SproutMerkleTree sproutTree; + SaplingMerkleTree saplingTree; + OrchardMerkleFrontier orchardTree; + orchardTree.AppendBundle(wtx.GetOrchardBundle()); + + EXPECT_EQ(-1, chainActive.Height()); + CBlock block; + block.vtx.push_back(wtx); + block.hashMerkleRoot = block.BuildMerkleTree(); + auto blockHash = block.GetHash(); + CBlockIndex fakeIndex {block}; + fakeIndex.hashFinalOrchardRoot = orchardTree.root(); + mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex)); + chainActive.SetTip(&fakeIndex); + EXPECT_TRUE(chainActive.Contains(&fakeIndex)); + EXPECT_EQ(0, chainActive.Height()); + + // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe + auto orchardTxMeta = wallet.GetOrchardWallet().AddNotesIfInvolvingMe(wtx); + ASSERT_TRUE(orchardTxMeta.has_value()); + EXPECT_FALSE(orchardTxMeta.value().empty()); + wtx.SetOrchardTxMeta(orchardTxMeta.value()); + wtx.SetMerkleBranch(block); + wallet.LoadWalletTx(wtx); + + // Simulate receiving new block and ChainTip signal + wallet.IncrementNoteWitnesses(Params().GetConsensus(),&fakeIndex, &block, sproutTree, saplingTree, true); + + // Fetch the Orchard note so we can spend it. + std::vector sproutEntries; + std::vector saplingEntries; + std::vector orchardEntries; + wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, -1); + EXPECT_EQ(0, sproutEntries.size()); + EXPECT_EQ(0, saplingEntries.size()); + EXPECT_EQ(1, orchardEntries.size()); + + // Generate another recipient + libzcash::diversifier_index_t j2(1); + auto recipient2 = ivk.Address(j2); + + // Generate tx to spend note A + auto noteToSpend = std::move(wallet.GetOrchardSpendInfo(orchardEntries)[0]); + auto builder2 = TransactionBuilder(consensusParams, 2, orchardTree.root()); + builder2.AddOrchardSpend(std::move(noteToSpend.first), std::move(noteToSpend.second)); + auto tx2 = builder2.Build().GetTxOrThrow(); + CWalletTx wtx2 {&wallet, tx2}; + + // Generate conflicting tx to spend note A + auto noteToSpend2 = std::move(wallet.GetOrchardSpendInfo(orchardEntries)[0]); + auto builder3 = TransactionBuilder(consensusParams, 2, orchardTree.root()); + builder3.AddOrchardSpend(std::move(noteToSpend2.first), std::move(noteToSpend2.second)); + auto tx3 = builder3.Build().GetTxOrThrow(); + CWalletTx wtx3 {&wallet, tx3}; + + auto hash2 = wtx2.GetHash(); + auto hash3 = wtx3.GetHash(); + + // No conflicts for no spends (wtx is currently the only transaction in the wallet) + EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); + EXPECT_EQ(0, wallet.GetConflicts(hash3).size()); + + // No conflicts for one spend + auto orchardTxMeta2 = wallet.GetOrchardWallet().AddNotesIfInvolvingMe(wtx2); + ASSERT_TRUE(orchardTxMeta2.has_value()); + EXPECT_FALSE(orchardTxMeta2.value().empty()); + wtx2.SetOrchardTxMeta(orchardTxMeta2.value()); + wallet.LoadWalletTx(wtx2); + EXPECT_EQ(0, wallet.GetConflicts(hash2).size()); + EXPECT_EQ(0, wallet.GetConflicts(hash3).size()); + + // Conflicts for two spends + auto orchardTxMeta3 = wallet.GetOrchardWallet().AddNotesIfInvolvingMe(wtx3); + ASSERT_TRUE(orchardTxMeta3.has_value()); + EXPECT_FALSE(orchardTxMeta3.value().empty()); + wtx3.SetOrchardTxMeta(orchardTxMeta3.value()); + wallet.LoadWalletTx(wtx3); + auto c3 = wallet.GetConflicts(hash2); + EXPECT_EQ(2, c3.size()); + EXPECT_EQ(std::set({hash2, hash3}), c3); + + // Tear down + chainActive.SetTip(NULL); + mapBlockIndex.erase(blockHash); + + RegtestDeactivateNU5(); +} + TEST(WalletTests, SproutNullifierIsSpent) { SelectParams(CBaseChainParams::REGTEST); CWallet wallet(Params()); diff --git a/src/wallet/orchard.h b/src/wallet/orchard.h index 894c21362..45e5a3ab4 100644 --- a/src/wallet/orchard.h +++ b/src/wallet/orchard.h @@ -410,6 +410,17 @@ public: reinterpret_cast*>(txidsRet)->push_back(txid_out); } + std::vector GetPotentialSpendsFromNullifier(const uint256& nullifier) const { + std::vector result; + orchard_wallet_get_potential_spends_from_nullifier( + inner.get(), + nullifier.begin(), + &result, + PushTxId + ); + return result; + } + std::vector GetPotentialSpends(const OrchardOutPoint& outPoint) const { std::vector result; orchard_wallet_get_potential_spends( diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 727696eb9..3bc4e37ec 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -726,7 +726,8 @@ UniValue z_importkey(const UniValue& params, bool fHelp) "\nNote: This call can take a long time to complete if rescan is true.\n" "\nResult:\n" "{\n" - " \"type\" : \"xxxx\", (string) \"sprout\" or \"sapling\"\n" + " \"address_type\" : \"xxxx\", (string) \"sprout\" or \"sapling\"\n" + " \"type\" : \"xxxx\", (string) \"sprout\" or \"sapling\" (DEPRECATED, legacy attribute)\n" " \"address\" : \"address|DefaultAddress\", (string) The address corresponding to the spending key (for Sapling, this is the default address).\n" "}\n" "\nExamples:\n" @@ -792,7 +793,8 @@ UniValue z_importkey(const UniValue& params, bool fHelp) auto addrInfo = std::visit(libzcash::AddressInfoFromSpendingKey{}, spendingkey.value()); UniValue result(UniValue::VOBJ); - result.pushKV("type", addrInfo.first); + result.pushKV("address_type", addrInfo.first); + result.pushKV("type", addrInfo.first); //deprecated result.pushKV("address", keyIO.EncodePaymentAddress(addrInfo.second)); // Sapling support @@ -832,7 +834,8 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp) "\nNote: This call can take a long time to complete if rescan is true. Import of Unified viewing keys is not yet supported.\n" "\nResult:\n" "{\n" - " \"type\" : \"xxxx\", (string) \"sprout\" or \"sapling\"\n" + " \"address_type\" : \"xxxx\", (string) \"sprout\" or \"sapling\"\n" + " \"type\" : \"xxxx\", (string) \"sprout\" or \"sapling\" (DEPRECATED, legacy attribute)\n" " \"address\" : \"address|DefaultAddress\", (string) The address corresponding to the viewing key (for Sapling, this is the default address).\n" "}\n" "\nExamples:\n" @@ -889,7 +892,8 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp) auto addrInfo = std::visit(libzcash::AddressInfoFromViewingKey(chainparams), viewingkey.value()); UniValue result(UniValue::VOBJ); const string strAddress = keyIO.EncodePaymentAddress(addrInfo.second); - result.pushKV("type", addrInfo.first); + result.pushKV("address_type", addrInfo.first); + result.pushKV("type", addrInfo.first); //deprecated result.pushKV("address", strAddress); auto addResult = std::visit(AddViewingKeyToWallet(pwalletMain, true), viewingkey.value()); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ff6b0deaa..f41c70890 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -386,7 +386,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) " },\n" " ...\n" "]" - "\nIn the case that a source does not have addresses for a pool, the key\n" + "\nIn the case that a source does not have addresses for a value pool, the key\n" "associated with that pool will be absent.\n" "\nExamples:\n" + HelpExampleCli("listaddresses", "") @@ -2432,9 +2432,10 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) "\nReturns array of unspent shielded notes with between minconf and maxconf (inclusive) confirmations.\n" "Optionally filter to only include notes sent to specified addresses.\n" "When minconf is 0, unspent notes with zero confirmations are returned, even though they are not immediately spendable.\n" + "Orchard notes are not yet returned (the documentation below shows what the results will be when this is fixed).\n" "Results are an array of Objects, each of which has:\n" - "{txid, type, jsindex, jsoutindex, confirmations, address, amount, memo} (Sprout)\n" - "{txid, type, outindex, confirmations, address, amount, memo} (Sapling)\n" + "{txid, pool, jsindex, jsoutindex, confirmations, address, amount, memo} (Sprout)\n" + "{txid, pool, outindex, confirmations, address, amount, memo} (Sapling)\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" @@ -2444,14 +2445,15 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) " \"address\" (string) Sprout, Sapling, or Unified address\n" " ,...\n" " ]\n" - "\nResult (output indices for only one pool will be present):\n" + "\nResult (output indices for only one value pool will be present):\n" "[ (array of json object)\n" " {\n" " \"txid\" : \"txid\", (string) the transaction id \n" - " \"type\" : \"sprout|sapling|orchard\", (string) The shielded pool\n" + " \"pool\" : \"sprout|sapling|orchard\", (string) The shielded value pool\n" + " \"type\" : \"sprout|sapling|orchard\", (string) The shielded value pool (DEPRECATED legacy attribute)\n" " \"jsindex\" (sprout) : n, (numeric) the joinsplit index\n" " \"jsoutindex\" (sprout) : n, (numeric) the output index of the joinsplit\n" - " \"outindex\" (transparent, sapling, orchard) : n, (numeric) the output index\n" + " \"outindex\" (transparent, sapling, orchard) : n, (numeric) the Output or Action index\n" " \"confirmations\" : n, (numeric) the number of confirmations\n" " \"spendable\" : true|false, (boolean) true if note can be spent by wallet, false if address is watchonly\n" " \"address\" : \"address\", (string) the shielded address\n" @@ -2551,7 +2553,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) for (auto & entry : sproutEntries) { UniValue obj(UniValue::VOBJ); obj.pushKV("txid", entry.jsop.hash.ToString()); - obj.pushKV("type", ADDR_TYPE_SPROUT); + obj.pushKV("pool", ADDR_TYPE_SPROUT); + obj.pushKV("type", ADDR_TYPE_SPROUT); //deprecated obj.pushKV("jsindex", (int)entry.jsop.js ); obj.pushKV("jsoutindex", (int)entry.jsop.n); obj.pushKV("confirmations", entry.confirmations); @@ -2570,7 +2573,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) for (auto & entry : saplingEntries) { UniValue obj(UniValue::VOBJ); obj.pushKV("txid", entry.op.hash.ToString()); - obj.pushKV("type", ADDR_TYPE_SAPLING); + obj.pushKV("pool", ADDR_TYPE_SAPLING); + obj.pushKV("type", ADDR_TYPE_SAPLING); //deprecated obj.pushKV("outindex", (int)entry.op.n); obj.pushKV("confirmations", entry.confirmations); bool hasSaplingSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(entry.address); @@ -2699,7 +2703,7 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp) throw runtime_error( "zcbenchmark benchmarktype samplecount\n" "\n" - "Runs a benchmark of the selected type samplecount times,\n" + "Runs a benchmark of the selected benchmark type samplecount times,\n" "returning the running times of each sample.\n" "\n" "Output: [\n" @@ -3253,25 +3257,27 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp) return NullUniValue; if (fHelp || params.size() < 1 || params.size() > 3) throw runtime_error( - "z_getaddressforaccount account ( [\"pool\", ...] diversifier_index )\n" + "z_getaddressforaccount account ( [\"receiver_type\", ...] diversifier_index )\n" "\nFor the given account number, derives a Unified Address in accordance" "\nwith the remaining arguments:\n" - "\n- If no list of pools is given (or the empty list \"[]\"), the best and" - "\n second-best shielded pools, along with the transparent pool, will be used." + "\n- If no list of receiver types is given (or the empty list \"[]\"), the best" + "\n and second-best shielded receiver types, along with the \"p2pkh\" (i.e. transparent) receiver" + "\n type, will be used." "\n- If no diversifier index is given, the next unused index (that is valid" - "\n for the list of pools) will be selected.\n" + "\n for the list of receiver types) will be selected.\n" "\nThe account number must have been previously generated by a call to the" "\nz_getnewaccount RPC method.\n" "\nOnce a Unified Address has been derived at a specific diversifier index," "\nre-deriving it (via a subsequent call to z_getaddressforaccount with the" "\nsame account and index) will produce the same address with the same list" - "\nof pools. An error will be returned if a different list of pools is given.\n" + "\nof receiver types. An error will be returned if a different list of receiver" + "\ntypes is requested.\n" "\nResult:\n" "{\n" - " \"account\": n, (numeric) the specified account number\n" - " \"diversifier_index\": n, (numeric) the index specified or chosen\n" - " \"pools\": [\"pool\",...]\", (json array of string) the pools (e.g. \"transparent\", \"orchard\") for which the UA contains receivers\n" - " \"unifiedaddress\" (string) The corresponding address\n" + " \"account\": n, (numeric) the specified account number\n" + " \"diversifier_index\": n, (numeric) the index specified or chosen\n" + " \"receiver_types\": [\"orchard\",...]\", (json array of string) the receiver types that the UA contains (valid values are \"p2pkh\", \"sapling\", \"orchard\")\n" + " \"address\" (string) The corresponding address\n" "}\n" "\nExamples:\n" + HelpExampleCli("z_getaddressforaccount", "4") @@ -3294,25 +3300,25 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp) } libzcash::AccountId account = accountInt; - std::set receivers; + std::set receiverTypes; if (params.size() >= 2) { - const auto& pools = params[1].get_array(); - for (unsigned int i = 0; i < pools.size(); i++) { - const std::string& p = pools[i].get_str(); - if (p == "transparent") { - receivers.insert(ReceiverType::P2PKH); + const auto& parsed = params[1].get_array(); + for (size_t i = 0; i < parsed.size(); i++) { + const std::string& p = parsed[i].get_str(); + if (p == "p2pkh") { + receiverTypes.insert(ReceiverType::P2PKH); } else if (p == "sapling") { - receivers.insert(ReceiverType::Sapling); + receiverTypes.insert(ReceiverType::Sapling); } else if (p == "orchard") { - receivers.insert(ReceiverType::Orchard); + receiverTypes.insert(ReceiverType::Orchard); } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, "pool arguments must be \"transparent\", \"sapling\", or \"orchard\""); + throw JSONRPCError(RPC_INVALID_PARAMETER, "receiver type arguments must be \"p2pkh\", \"sapling\", or \"orchard\""); } } } - if (receivers.empty()) { - // Default is the best and second-best shielded pools, and the transparent pool. - receivers = CWallet::DefaultReceiverTypes(chainActive.Height()); + if (receiverTypes.empty()) { + // Default is the best and second-best shielded receiver types, and the transparent (P2PKH) receiver type. + receiverTypes = CWallet::DefaultReceiverTypes(chainActive.Height()); } std::optional j = std::nullopt; @@ -3335,16 +3341,14 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); - // Generate the first UA for this account, using the best and next-best shielded pools - // and the transparent pool. - auto res = pwalletMain->GenerateUnifiedAddress(account, receivers, j); + auto res = pwalletMain->GenerateUnifiedAddress(account, receiverTypes, j); UniValue result(UniValue::VOBJ); result.pushKV("account", (uint64_t)account); std::visit(match { [&](std::pair addr) { - result.pushKV("unifiedaddress", KeyIO(Params()).EncodePaymentAddress(addr.first)); + result.pushKV("address", KeyIO(Params()).EncodePaymentAddress(addr.first)); UniValue j; j.setNumStr(ArbitraryIntStr(std::vector(addr.second.begin(), addr.second.end()))); result.pushKV("diversifier_index", j); @@ -3397,24 +3401,24 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp) }, }, res); - UniValue pools(UniValue::VARR); - for (const auto& receiver : receivers) { - switch (receiver) { + UniValue receiver_types(UniValue::VARR); + for (const auto& receiverType : receiverTypes) { + switch (receiverType) { case ReceiverType::P2PKH: - pools.push_back("transparent"); + receiver_types.push_back("p2pkh"); break; case ReceiverType::Sapling: - pools.push_back("sapling"); + receiver_types.push_back("sapling"); break; case ReceiverType::Orchard: - pools.push_back("orchard"); + receiver_types.push_back("orchard"); break; default: // Unreachable assert(false); } } - result.pushKV("pools", pools); + result.pushKV("receiver_types", receiver_types); return result; } @@ -3485,8 +3489,9 @@ UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 1) throw runtime_error( "z_listunifiedreceivers unified_address\n" - "\nReturns the (per-pool) receivers contained within the provided UA;" - "\nthe UA may not have receivers for some pools.\n" + "\nReturns a record of the individual receivers contained within the provided UA," + "\nkeyed by receiver type. The UA may not have receivers for some receiver types," + "\nin which case those keys will be absent.\n" "\nTransactions that send funds to any of the receivers returned by this RPC" "\nmethod will be detected by the wallet as having been sent to the unified" "\naddress.\n" @@ -3494,9 +3499,9 @@ UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp) "1. unified_address (string) The unified address\n" "\nResult:\n" "{\n" - " \"transparent\": \"address\", (string) The legacy transparent address (P2PKH or P2SH)\n" - " \"sapling\": \"address\", (string) The legacy Sapling address\n" - " \"orchard\": \"address\" (string) The single-receiver Unified Address for the Orchard receiver\n" + " \"TRANSPARENT_TYPE\": \"address\", (string) The legacy transparent address (\"p2pkh\" or \"p2sh\", never both)\n" + " \"sapling\": \"address\", (string) The legacy Sapling address\n" + " \"orchard\": \"address\" (string) A single-receiver Unified Address containing the Orchard receiver\n" "}\n" "\nExamples:\n" + HelpExampleCli("z_listunifiedreceivers", "") @@ -3530,10 +3535,10 @@ UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp) result.pushKV("sapling", keyIO.EncodePaymentAddress(addr)); }, [&](const CScriptID& addr) { - result.pushKV("transparent", keyIO.EncodePaymentAddress(addr)); + result.pushKV("p2sh", keyIO.EncodePaymentAddress(addr)); }, [&](const CKeyID& addr) { - result.pushKV("transparent", keyIO.EncodePaymentAddress(addr)); + result.pushKV("p2pkh", keyIO.EncodePaymentAddress(addr)); }, [](auto rest) {}, }, receiver); @@ -3629,7 +3634,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) "\nArguments:\n" "1. \"address\" (string) The shielded address.\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" - "\nResult (output indices for only one pool will be present):\n" + "\nResult (output indices for only one value pool will be present):\n" "{\n" " \"pool\": \"pool\" (string) one of (\"transparent\", \"sprout\", \"sapling\", \"orchard\")\n" " \"txid\": \"txid\", (string) the transaction id\n" @@ -3924,8 +3929,9 @@ UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "z_getbalanceforviewingkey \"fvk\" ( minconf )\n" - "\nReturns the per-pool balances viewable by a full viewing key known to the node's wallet." - "\nSprout viewing keys may be used only if the wallet controls the corresponding spending key." + "\nReturns the balance viewable by a full viewing key known to the node's wallet" + "\nfor each value pool. Sprout viewing keys may be used only if the wallet controls" + "\nthe corresponding spending key." "\nArguments:\n" "1. \"fvk\" (string) The selected full viewing key.\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" @@ -3933,16 +3939,16 @@ UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp) "{\n" " \"pools\": {\n" " \"transparent\": {\n" - " \"valueZat\": amount (numeric) The amount held in the transparent pool viewable by this fvk\n" + " \"valueZat\": amount (numeric) The amount viewable by this FVK held in the transparent value pool\n" " \"},\n" " \"sprout\": {\n" - " \"valueZat\": amount (numeric) The amount held in the sprout pool viewable by this fvk\n" + " \"valueZat\": amount (numeric) The amount viewable by this FVK held in the Sprout value pool\n" " \"},\n" " \"sapling\": {\n" - " \"valueZat\": amount (numeric) The amount held in the sapling pool viewable by this fvk\n" + " \"valueZat\": amount (numeric) The amount viewable by this FVK held in the Sapling value pool\n" " \"},\n" " \"orchard\": {\n" - " \"valueZat\": amount (numeric) The amount held in the orchard pool viewable by this fvk\n" + " \"valueZat\": amount (numeric) The amount viewable by this FVK held in the Orchard value pool\n" " \"}\n" " \"},\n" " \"minimum_confirmations\": n (numeric) The given minconf argument\n" @@ -4039,7 +4045,7 @@ UniValue z_getbalanceforaccount(const UniValue& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( "z_getbalanceforaccount account ( minconf )\n" - "\nReturns the spendable pool balances of the given account." + "\nReturns the account's spendable balance for each value pool (\"transparent\", \"sapling\", and \"orchard\")." "\nArguments:\n" "1. account (numeric) The account number.\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" @@ -4047,13 +4053,13 @@ UniValue z_getbalanceforaccount(const UniValue& params, bool fHelp) "{\n" " \"pools\": {\n" " \"transparent\": {\n" - " \"valueZat\": amount (numeric) The amount held in the transparent pool by this account\n" + " \"valueZat\": amount (numeric) The amount held by this account in the transparent value pool\n" " \"},\n" " \"sapling\": {\n" - " \"valueZat\": amount (numeric) The amount held in the sapling pool by this account\n" + " \"valueZat\": amount (numeric) The amount held by this account in the Sapling value pool\n" " \"},\n" " \"orchard\": {\n" - " \"valueZat\": amount (numeric) The amount held in the orchard pool by this account\n" + " \"valueZat\": amount (numeric) The amount held by this account in the Orchard value pool\n" " \"}\n" " \"},\n" " \"minimum_confirmations\": n (numeric) The given minconf argument\n" @@ -4209,7 +4215,8 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " \"txid\" : \"transactionid\", (string) The transaction id\n" " \"spends\" : [\n" " {\n" - " \"type\" : \"sprout|sapling|orchard\", (string) The shielded pool\n" + " \"pool\" : \"sprout|sapling|orchard\", (string) The shielded value pool\n" + " \"type\" : \"sprout|sapling|orchard\", (string) The shielded value pool (DEPRECATED legacy attribute)" " \"js\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\n" " \"jsSpend\" : n, (numeric, sprout) the index of the spend within the JSDescription\n" " \"spend\" : n, (numeric, sapling) the index of the spend within vShieldedSpend\n" @@ -4227,7 +4234,8 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " ],\n" " \"outputs\" : [\n" " {\n" - " \"type\" : \"sprout|sapling|orchard\", (string) The shielded pool\n" + " \"pool\" : \"sprout|sapling|orchard\", (string) The shielded value pool\n" + " \"type\" : \"sprout|sapling|orchard\", (string) The shielded value pool (DEPRECATED legacy attribute)\n" " \"js\" : n, (numeric, sprout) the index of the JSDescription within vJoinSplit\n" " \"jsOutput\" : n, (numeric, sprout) the index of the output within the JSDescription\n" " \"output\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n" @@ -4301,7 +4309,8 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) auto pa = decrypted.second; UniValue entry(UniValue::VOBJ); - entry.pushKV("type", ADDR_TYPE_SPROUT); + entry.pushKV("pool", ADDR_TYPE_SPROUT); + entry.pushKV("type", ADDR_TYPE_SPROUT); //deprecated entry.pushKV("js", (int)i); entry.pushKV("jsSpend", (int)j); entry.pushKV("txidPrev", jsop.hash.GetHex()); @@ -4324,7 +4333,8 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) auto memo = notePt.memo(); UniValue entry(UniValue::VOBJ); - entry.pushKV("type", ADDR_TYPE_SPROUT); + entry.pushKV("pool", ADDR_TYPE_SPROUT); + entry.pushKV("type", ADDR_TYPE_SPROUT); //deprecated entry.pushKV("js", (int)jsop.js); entry.pushKV("jsOutput", (int)jsop.n); entry.pushKV("address", keyIO.EncodePaymentAddress(pa)); @@ -4407,7 +4417,8 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) } UniValue entry(UniValue::VOBJ); - entry.pushKV("type", ADDR_TYPE_SAPLING); + entry.pushKV("pool", ADDR_TYPE_SAPLING); + entry.pushKV("type", ADDR_TYPE_SAPLING); //deprecated entry.pushKV("spend", (int)i); entry.pushKV("txidPrev", op.hash.GetHex()); entry.pushKV("outputPrev", (int)op.n); @@ -4460,7 +4471,8 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) } UniValue entry(UniValue::VOBJ); - entry.pushKV("type", ADDR_TYPE_SAPLING); + entry.pushKV("pool", ADDR_TYPE_SAPLING); + entry.pushKV("type", ADDR_TYPE_SAPLING); //deprecated entry.pushKV("output", (int)op.n); entry.pushKV("outgoing", isOutgoing); entry.pushKV("walletInternal", addr.second == RecipientType::WalletInternalAddress); @@ -4492,7 +4504,8 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) } UniValue entry(UniValue::VOBJ); - entry.pushKV("type", ADDR_TYPE_ORCHARD); + entry.pushKV("pool", ADDR_TYPE_ORCHARD); + entry.pushKV("type", ADDR_TYPE_ORCHARD); //deprecated entry.pushKV("action", (int) actionIdx); entry.pushKV("txidPrev", outpoint.hash.GetHex()); entry.pushKV("actionPrev", (int) outpoint.n); @@ -4519,7 +4532,8 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) } UniValue entry(UniValue::VOBJ); - entry.pushKV("type", ADDR_TYPE_ORCHARD); + entry.pushKV("pool", ADDR_TYPE_ORCHARD); + entry.pushKV("type", ADDR_TYPE_ORCHARD); //deprecated entry.pushKV("action", (int) actionIdx); entry.pushKV("outgoing", orchardActionOutput.IsOutgoing()); entry.pushKV("walletInternal", addr.second == RecipientType::WalletInternalAddress); @@ -4743,7 +4757,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) " The following special strings are also accepted:\n" " - \"ANY_TADDR\": Select non-coinbase UTXOs from any transparent addresses belonging to the wallet.\n" " Use z_shieldcoinbase to shield coinbase UTXOs from multiple transparent addresses.\n" - " If the address is a UA, transfer from the most recent pool with sufficient funds\n" + " If the address is a UA, transfer from the most recent value pool with sufficient funds\n" "2. \"amounts\" (array, required) An array of json objects representing the amounts to send.\n" " [{\n" " \"address\":address (string, required) The address is a taddr, zaddr, or Unified Address\n" @@ -4754,10 +4768,10 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) "4. fee (numeric, optional, default=" + strprintf("%s", FormatMoney(DEFAULT_FEE)) + ") The fee amount to attach to this transaction.\n" "5. privacyPolicy (string, optional, default=\"LegacyCompat\") Policy for what information leakage is acceptable.\n" " One of the following strings:\n" - " - \"FullPrivacy\": Only allow fully-shielded transactions (involving a single shielded pool).\n" + " - \"FullPrivacy\": Only allow fully-shielded transactions (involving a single shielded value pool).\n" " - \"LegacyCompat\": If the transaction involves any Unified Addresses, this is equivalent to\n" " \"FullPrivacy\". Otherwise, this is equivalent to \"AllowFullyTransparent\".\n" - " - \"AllowRevealedAmounts\": Allow funds to cross between shielded pools, revealing the amount\n" + " - \"AllowRevealedAmounts\": Allow funds to cross between shielded value pools, revealing the amount\n" " that crosses pools.\n" " - \"AllowRevealedRecipients\": Allow transparent recipients. This also implies revealing\n" " information described under \"AllowRevealedAmounts\".\n" @@ -4897,7 +4911,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) if (toSprout) { throw JSONRPCError( RPC_INVALID_PARAMETER, - "Sending funds into the Sprout pool is not supported by z_sendmany"); + "Sending funds into the Sprout value pool is not supported by z_sendmany"); } else { throw JSONRPCError( RPC_INVALID_PARAMETER, @@ -5430,7 +5444,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) " - \"ANY_SAPLING\": Merge notes from any Sapling zaddrs belonging to the wallet.\n" " While it is possible to use a variety of different combinations of addresses and the above values,\n" " it is not possible to send funds from both sprout and sapling addresses simultaneously. If a special\n" - " string is given, any given addresses of that type will be counted as duplicates and cause an error.\n" + " string is given, any given addresses of that address type will be counted as duplicates and cause an error.\n" " [\n" " \"address\" (string) Can be a taddr or a zaddr\n" " ,...\n" @@ -5722,7 +5736,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress"); } - // If sending between shielded addresses, they must be the same type + // If sending between shielded addresses, they must be within the same value pool if ((saplingEntries.size() > 0 && isToSproutZaddr) || (sproutEntries.size() > 0 && isToSaplingZaddr)) { throw JSONRPCError( RPC_INVALID_PARAMETER, @@ -5931,7 +5945,7 @@ UniValue z_getnotescount(const UniValue& params, bool fHelp) "z_getnotescount\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) Only include notes in transactions confirmed at least this many times.\n" - "\nReturns the number of shielded notes of each pool available in the wallet.\n" + "\nReturns the number of notes available in the wallet for each shielded value pool.\n" "\nResult:\n" "{\n" " \"sprout\" (numeric) the number of Sprout notes in the wallet\n" diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4809189a1..7f6f1310d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1136,6 +1136,32 @@ bool CWallet::LoadCaches() } } + // Sapling legacy addresses were not directly added to the keystore; instead, + // the default address for each key was automatically added to the in-memory + // keystore, but not persisted. Following the addition of unified addresses, + // all addresses must be written to the wallet database explicitly. + auto legacySeed = GetLegacyHDSeed(); + if (legacySeed.has_value()) { + for (const auto& [saplingIvk, keyMeta] : mapSaplingZKeyMetadata) { + // This condition only applies for keys derived from the legacy seed + if (keyMeta.seedFp == legacySeed.value().Fingerprint()) { + SaplingExtendedFullViewingKey extfvk; + if (GetSaplingFullViewingKey(saplingIvk, extfvk)) { + auto defaultAddress = extfvk.DefaultAddress(); + if (!HaveSaplingIncomingViewingKey(defaultAddress)) { + // restore the address to the keystore and persist it so that + // the database state is consistent. + if (!AddSaplingPaymentAddress(saplingIvk, defaultAddress)) { + LogPrintf("%s: Error: Failed to write legacy Sapling payment address to the wallet database.\n", + __func__); + return false; + } + } + } + } + } + } + // Restore decrypted Orchard notes. for (const auto& [_, walletTx] : mapWallet) { if (!walletTx.orchardTxMeta.empty()) { @@ -1668,9 +1694,8 @@ set CWallet::GetConflicts(const uint256& txid) const } } - for (uint32_t i = 0; i < wtx.GetOrchardBundle().GetNumActions(); i++) { - OrchardOutPoint op(wtx.GetHash(), i); - auto potential_spends = orchardWallet.GetPotentialSpends(op); + for (const uint256& nullifier : wtx.GetOrchardBundle().GetNullifiers()) { + auto potential_spends = orchardWallet.GetPotentialSpendsFromNullifier(nullifier); if (potential_spends.size() <= 1) { continue; // No conflict if zero or one spends @@ -4289,6 +4314,7 @@ int CWallet::ScanForWalletTransactions( bool fUpdate, bool isInitScan) { + assert(pindexStart != nullptr); int ret = 0; int64_t nNow = GetTime(); const CChainParams& chainParams = Params(); @@ -4303,7 +4329,7 @@ int CWallet::ScanForWalletTransactions( // no need to read and scan block, if block was created before // our wallet birthday (as adjusted for block time variability) - while (pindex && nTimeFirstKey && pindex->GetBlockTime() < nTimeFirstKey - TIMESTAMP_WINDOW) { + while (chainActive.Next(pindex) != NULL && nTimeFirstKey && pindex->GetBlockTime() < nTimeFirstKey - TIMESTAMP_WINDOW) { pindex = chainActive.Next(pindex); } diff --git a/src/zcash/Zcash.h b/src/zcash/Zcash.h index a6eb53b22..aecef6f66 100644 --- a/src/zcash/Zcash.h +++ b/src/zcash/Zcash.h @@ -33,8 +33,8 @@ #define ZC_ZIP225_ORCHARD_FLAGS_SIZE 1 #define ZC_ZIP225_ORCHARD_VALUE_BALANCE_SIZE 8 #define ZC_ZIP225_ORCHARD_ANCHOR_SIZE 32 -// - CompactSize is at least 2 bytes because sizeProofsOrchard >= 253 -#define ZC_ZIP225_ORCHARD_SIZE_PROOFS_BASE_SIZE 2 +// - CompactSize is at least 3 bytes because sizeProofsOrchard >= 253 +#define ZC_ZIP225_ORCHARD_SIZE_PROOFS_BASE_SIZE 3 #define ZC_ZIP225_ORCHARD_PROOF_BASE_SIZE 2720 #define ZC_ZIP225_ORCHARD_BINDING_SIG_SIZE 64 #define ZC_ZIP225_ORCHARD_BASE_SIZE (ZC_ZIP225_ORCHARD_NUM_ACTIONS_SIZE + ZC_ZIP225_ORCHARD_FLAGS_SIZE + ZC_ZIP225_ORCHARD_VALUE_BALANCE_SIZE + ZC_ZIP225_ORCHARD_ANCHOR_SIZE + ZC_ZIP225_ORCHARD_SIZE_PROOFS_BASE_SIZE + ZC_ZIP225_ORCHARD_PROOF_BASE_SIZE + ZC_ZIP225_ORCHARD_BINDING_SIG_SIZE)