From 5cfaef7bbdfb5de406b78f6ce3939b179bcb59fc Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 5 May 2023 22:55:41 +1000 Subject: [PATCH] Upgrade zcash_script to zcashd v5.5.0 (#84) * Update dependencies to match zcashd v5.5.0 * Update dependencies to match Zebra main branch * Update release instructions * Add module docs for the build script to avoid warnings * Update bridge file list to match the latest zcashd * Ignore some emacs temporary files * Standardise directory include paths in Cargo.toml * Add extra info to cxx_gen errors * Add additional Rust dependencies needed to compile * Ignore some expected clippy lints * Silence a C compiler macro redefinition warning * Standardise directory paths in build.rs * fix cxxbridge code generation * Update Cargo.lock * Use include!() for bridge.rs * Add a changelog entry for the next release * Fix a warning by adding docs for the crate * Remove previous depend/zcash * Squashed 'depend/zcash/' content from commit eb80047476 git-subtree-dir: depend/zcash git-subtree-split: eb80047476e9c0db3524f647d412faf8d4a584ee * Update depend/zcash to v5.5.0 ```sh git subtree add -P depend/zcash https://github.com/zcash/zcash.git v5.5.0 --squash git rm depend/zcash/Cargo.toml ``` --------- Co-authored-by: Conrado Gouvea --- .gitignore | 3 +- CHANGELOG.md | 11 + Cargo.toml | 32 +- README.md | 7 +- build.rs | 54 +- depend/zcash/.editorconfig | 3 + depend/zcash/.github/workflows/audits.yml | 22 +- depend/zcash/.github/workflows/book.yml | 5 +- depend/zcash/.github/workflows/build.yml | 112 ++ depend/zcash/.github/workflows/checks.yml | 45 + depend/zcash/.github/workflows/lints.yml | 19 +- depend/zcash/Cargo.lock | 794 ++++++------ depend/zcash/INSTALL | 2 +- depend/zcash/Makefile.am | 2 - depend/zcash/README.md | 2 +- depend/zcash/SECURITY.md | 2 +- depend/zcash/configure.ac | 2 +- depend/zcash/contrib/debian/changelog | 42 + depend/zcash/contrib/debian/copyright | 7 +- .../zcash/contrib/debian/examples/zcash.conf | 52 +- .../contrib/devtools/update-clang-hashes.sh | 1 - .../gitian-linux-parallel.yml | 2 +- .../gitian-descriptors/gitian-linux.yml | 2 +- .../zcash/contrib/metrics/docker-compose.yml | 52 + .../grafana/dashboards/zcashd-metrics.json | 911 ++++++++++++++ .../zcash/contrib/metrics/grafana/grafana.ini | 2 + .../provisioning/dashboards/zcash.yaml | 18 + .../provisioning/datasources/prometheus.yaml | 12 + depend/zcash/contrib/metrics/prometheus.yaml | 2 +- depend/zcash/depends/Makefile | 2 +- depend/zcash/depends/funcs.mk | 2 +- depend/zcash/depends/hosts/mingw32.mk | 1 + depend/zcash/depends/packages/bdb.mk | 11 + depend/zcash/depends/packages/boost.mk | 4 +- depend/zcash/depends/packages/googletest.mk | 2 +- depend/zcash/depends/packages/libcxx.mk | 34 +- depend/zcash/depends/packages/libevent.mk | 2 +- .../zcash/depends/packages/native_cctools.mk | 5 +- depend/zcash/depends/packages/native_clang.mk | 33 +- depend/zcash/depends/packages/native_cmake.mk | 4 +- .../depends/packages/native_cxxbridge.mk | 20 +- depend/zcash/depends/packages/native_rust.mk | 29 +- depend/zcash/depends/packages/native_zstd.mk | 4 +- depend/zcash/depends/packages/rustcxx.mk | 2 +- depend/zcash/depends/packages/tl_expected.mk | 6 +- depend/zcash/depends/packages/utfcpp.mk | 2 +- .../patches/native_cxxbridge/Cargo.lock | 416 +++++++ depend/zcash/doc/authors.md | 49 +- depend/zcash/doc/book/src/SUMMARY.md | 1 + depend/zcash/doc/book/src/dev/rust.md | 34 + depend/zcash/doc/book/src/user/deprecation.md | 11 +- depend/zcash/doc/book/src/user/metrics.md | 66 +- .../doc/book/src/user/release-support.md | 53 + depend/zcash/doc/man/zcash-cli.1 | 6 +- depend/zcash/doc/man/zcash-tx.1 | 6 +- depend/zcash/doc/man/zcashd-wallet-tool.1 | 4 +- depend/zcash/doc/man/zcashd.1 | 49 +- .../doc/release-notes/release-notes-5.3.3.md | 36 + .../doc/release-notes/release-notes-5.4.1.md | 46 + .../doc/release-notes/release-notes-5.4.2.md | 36 + .../release-notes/release-notes-5.5.0-rc1.md | 407 ++++++ .../release-notes/release-notes-5.5.0-rc2.md | 458 +++++++ .../release-notes/release-notes-5.5.0-rc3.md | 467 +++++++ .../doc/release-notes/release-notes-5.5.0.md | 479 +++++++ depend/zcash/qa/pull-tester/rpc-tests.py | 62 +- depend/zcash/qa/rpc-tests/addressindex.py | 1 + depend/zcash/qa/rpc-tests/finalorchardroot.py | 1 + depend/zcash/qa/rpc-tests/finalsaplingroot.py | 1 + .../zcash/qa/rpc-tests/fundrawtransaction.py | 2 +- depend/zcash/qa/rpc-tests/invalidtxrequest.py | 3 + depend/zcash/qa/rpc-tests/mempool_limit.py | 29 +- .../qa/rpc-tests/mempool_nu_activation.py | 10 +- depend/zcash/qa/rpc-tests/mempool_packages.py | 64 +- depend/zcash/qa/rpc-tests/mempool_reorg.py | 7 +- .../qa/rpc-tests/mempool_resurrect_test.py | 7 +- .../qa/rpc-tests/mempool_spendcoinbase.py | 7 +- .../zcash/qa/rpc-tests/mempool_tx_expiry.py | 19 +- .../qa/rpc-tests/mergetoaddress_helper.py | 305 +++-- .../qa/rpc-tests/mergetoaddress_mixednotes.py | 2 + .../qa/rpc-tests/mergetoaddress_sapling.py | 10 +- .../qa/rpc-tests/mergetoaddress_ua_nu5.py | 35 + .../qa/rpc-tests/mergetoaddress_ua_sapling.py | 31 + depend/zcash/qa/rpc-tests/merkle_blocks.py | 5 +- .../qa/rpc-tests/mining_shielded_coinbase.py | 11 +- depend/zcash/qa/rpc-tests/orchard_reorg.py | 1 + .../zcash/qa/rpc-tests/p2p_txexpiringsoon.py | 1 + .../qa/rpc-tests/prioritisetransaction.py | 247 ++-- depend/zcash/qa/rpc-tests/pruning.py | 2 +- depend/zcash/qa/rpc-tests/rawtransactions.py | 2 +- .../rpc-tests/regtest_signrawtransaction.py | 4 +- .../qa/rpc-tests/remove_sprout_shielding.py | 99 +- depend/zcash/qa/rpc-tests/rpcbind_test.py | 2 +- .../zcash/qa/rpc-tests/shorter_block_times.py | 1 + depend/zcash/qa/rpc-tests/show_help.py | 162 ++- depend/zcash/qa/rpc-tests/smartfees.py | 23 +- .../test_framework/test_framework.py | 3 +- .../zcash/qa/rpc-tests/test_framework/util.py | 47 +- .../qa/rpc-tests/test_framework/zip317.py | 39 + depend/zcash/qa/rpc-tests/turnstile.py | 1 + depend/zcash/qa/rpc-tests/txn_doublespend.py | 3 +- depend/zcash/qa/rpc-tests/wallet.py | 6 +- depend/zcash/qa/rpc-tests/wallet_accounts.py | 3 +- .../qa/rpc-tests/wallet_changeaddresses.py | 5 +- .../qa/rpc-tests/wallet_changeindicator.py | 1 + .../zcash/qa/rpc-tests/wallet_deprecation.py | 87 +- .../zcash/qa/rpc-tests/wallet_doublespend.py | 3 +- depend/zcash/qa/rpc-tests/wallet_isfromme.py | 1 + depend/zcash/qa/rpc-tests/wallet_listnotes.py | 3 +- .../zcash/qa/rpc-tests/wallet_listreceived.py | 33 +- .../zcash/qa/rpc-tests/wallet_listunspent.py | 7 +- .../zcash/qa/rpc-tests/wallet_nullifiers.py | 5 +- depend/zcash/qa/rpc-tests/wallet_orchard.py | 3 +- .../qa/rpc-tests/wallet_orchard_change.py | 3 +- .../zcash/qa/rpc-tests/wallet_orchard_init.py | 3 +- .../rpc-tests/wallet_orchard_persistence.py | 3 +- .../qa/rpc-tests/wallet_orchard_reindex.py | 1 + .../qa/rpc-tests/wallet_parsing_amounts.py | 13 - .../zcash/qa/rpc-tests/wallet_persistence.py | 1 + depend/zcash/qa/rpc-tests/wallet_sapling.py | 33 +- .../qa/rpc-tests/wallet_sendmany_any_taddr.py | 22 +- .../qa/rpc-tests/wallet_shieldcoinbase.py | 23 +- .../qa/rpc-tests/wallet_shieldingcoinbase.py | 63 +- depend/zcash/qa/rpc-tests/wallet_treestate.py | 4 +- .../qa/rpc-tests/wallet_unified_change.py | 3 +- .../zcash/qa/rpc-tests/wallet_z_sendmany.py | 117 +- .../zcash/qa/rpc-tests/zkey_import_export.py | 1 + depend/zcash/qa/supply-chain/audits.toml | 865 +++++++++++++ depend/zcash/qa/supply-chain/config.toml | 189 +-- depend/zcash/qa/supply-chain/imports.lock | 757 +++++------ depend/zcash/qa/zcash/postponed-updates.txt | 62 +- depend/zcash/qa/zcash/smoke_tests.py | 101 +- .../qa/zcash/test-depends-sources-mirror.py | 9 +- depend/zcash/qa/zcash/updatecheck.py | 27 +- depend/zcash/rust-toolchain.toml | 3 +- depend/zcash/src/Makefile.am | 52 +- depend/zcash/src/Makefile.bench.include | 1 + depend/zcash/src/Makefile.gtest.include | 2 + depend/zcash/src/Makefile.test.include | 2 +- depend/zcash/src/amount.cpp | 10 +- depend/zcash/src/amount.h | 4 +- depend/zcash/src/bench/verification.cpp | 20 +- depend/zcash/src/bitcoin-cli.cpp | 48 +- depend/zcash/src/bitcoin-tx.cpp | 2 +- depend/zcash/src/bitcoind.cpp | 16 +- depend/zcash/src/bloom.cpp | 29 +- depend/zcash/src/bloom.h | 6 + depend/zcash/src/chainparams.cpp | 19 +- depend/zcash/src/chainparams.h | 14 +- depend/zcash/src/clientversion.h | 2 +- depend/zcash/src/coins.cpp | 59 - depend/zcash/src/coins.h | 71 +- depend/zcash/src/compat.h | 5 + depend/zcash/src/consensus/params.cpp | 4 +- depend/zcash/src/crypto/equihash.cpp | 2 +- depend/zcash/src/crypto/equihash.h | 2 +- depend/zcash/src/deprecation.cpp | 18 +- depend/zcash/src/deprecation.h | 21 +- depend/zcash/src/gtest/main.cpp | 2 +- .../zcash/src/gtest/test_checktransaction.cpp | 29 +- depend/zcash/src/gtest/test_coins.cpp | 62 +- depend/zcash/src/gtest/test_consensus.cpp | 20 +- depend/zcash/src/gtest/test_deprecation.cpp | 22 +- depend/zcash/src/gtest/test_history.cpp | 67 +- depend/zcash/src/gtest/test_joinsplit.cpp | 24 +- depend/zcash/src/gtest/test_keystore.cpp | 6 +- depend/zcash/src/gtest/test_mempool.cpp | 28 +- depend/zcash/src/gtest/test_mempoollimit.cpp | 82 +- .../zcash/src/gtest/test_noteencryption.cpp | 283 +---- depend/zcash/src/gtest/test_transaction.cpp | 4 +- .../src/gtest/test_transaction_builder.cpp | 9 +- .../src/gtest/test_transaction_builder.h | 20 +- depend/zcash/src/gtest/test_validation.cpp | 23 +- depend/zcash/src/gtest/test_weightedmap.cpp | 160 +++ depend/zcash/src/hash.h | 5 + depend/zcash/src/init.cpp | 51 +- depend/zcash/src/int128.h | 25 + depend/zcash/src/key_io.cpp | 4 +- depend/zcash/src/keystore.cpp | 38 +- depend/zcash/src/keystore.h | 22 +- depend/zcash/src/main.cpp | 239 ++-- depend/zcash/src/main.h | 25 +- depend/zcash/src/mempool_limit.cpp | 114 +- depend/zcash/src/mempool_limit.h | 116 +- depend/zcash/src/metrics.cpp | 2 +- depend/zcash/src/miner.cpp | 706 ++++++----- depend/zcash/src/miner.h | 69 +- depend/zcash/src/net.cpp | 12 +- depend/zcash/src/net.h | 79 +- depend/zcash/src/policy/fees.cpp | 157 +-- depend/zcash/src/policy/fees.h | 108 +- depend/zcash/src/policy/policy.cpp | 2 +- depend/zcash/src/policy/policy.h | 55 +- depend/zcash/src/pow/tromp/equi.h | 71 +- depend/zcash/src/pow/tromp/equi_miner.h | 98 +- depend/zcash/src/primitives/orchard.h | 70 +- depend/zcash/src/primitives/transaction.cpp | 54 +- depend/zcash/src/primitives/transaction.h | 45 +- depend/zcash/src/proof_verifier.cpp | 6 +- depend/zcash/src/proof_verifier.h | 4 +- depend/zcash/src/random.cpp | 30 +- depend/zcash/src/random.h | 39 +- depend/zcash/src/rest.cpp | 2 +- depend/zcash/src/rpc/blockchain.cpp | 8 +- depend/zcash/src/rpc/client.cpp | 397 +++--- depend/zcash/src/rpc/client.h | 48 +- depend/zcash/src/rpc/mining.cpp | 55 +- depend/zcash/src/rpc/misc.cpp | 18 +- depend/zcash/src/rpc/net.cpp | 52 +- depend/zcash/src/rpc/rawtransaction.cpp | 14 +- depend/zcash/src/rust/bin/inspect/block.rs | 2 +- .../zcash/src/rust/bin/inspect/transaction.rs | 89 +- depend/zcash/src/rust/include/librustzcash.h | 17 +- depend/zcash/src/rust/include/rust/ed25519.h | 59 - .../src/rust/include/rust/ed25519/types.h | 56 - depend/zcash/src/rust/include/rust/orchard.h | 164 --- .../rust/orchard/incremental_merkle_tree.h | 96 -- .../src/rust/include/rust/orchard/wallet.h | 5 +- depend/zcash/src/rust/src/bridge.rs | 327 +++++ depend/zcash/src/rust/src/builder_ffi.rs | 3 +- depend/zcash/src/rust/src/bundlecache.rs | 20 +- depend/zcash/src/rust/src/ed25519.rs | 90 +- depend/zcash/src/rust/src/history_ffi.rs | 2 +- .../src/rust/src/incremental_merkle_tree.rs | 8 +- .../rust/src/incremental_merkle_tree_ffi.rs | 186 --- depend/zcash/src/rust/src/merkle_frontier.rs | 121 ++ depend/zcash/src/rust/src/metrics_ffi.rs | 22 +- depend/zcash/src/rust/src/note_encryption.rs | 145 +++ depend/zcash/src/rust/src/orchard_bundle.rs | 219 +++- depend/zcash/src/rust/src/orchard_ffi.rs | 349 ++---- depend/zcash/src/rust/src/params.rs | 146 +++ depend/zcash/src/rust/src/rustzcash.rs | 134 +- depend/zcash/src/rust/src/sapling.rs | 243 ++-- depend/zcash/src/rust/src/streams.rs | 122 ++ .../zcash/src/rust/src/tests/key_agreement.rs | 28 +- .../src/rust/src/tests/key_components.rs | 6 +- depend/zcash/src/rust/src/tests/notes.rs | 1 - depend/zcash/src/rust/src/transaction_ffi.rs | 44 +- depend/zcash/src/rust/src/wallet.rs | 2 +- depend/zcash/src/rust/src/wallet_scanner.rs | 228 +--- depend/zcash/src/script/zcash_script.cpp | 59 +- depend/zcash/src/serialize.h | 55 +- depend/zcash/src/streams.h | 32 + depend/zcash/src/streams_rust.cpp | 25 + depend/zcash/src/streams_rust.h | 21 + depend/zcash/src/test/addrman_tests.cpp | 1 + depend/zcash/src/test/bloom_tests.cpp | 26 + depend/zcash/src/test/coins_tests.cpp | 8 +- depend/zcash/src/test/mempool_tests.cpp | 31 +- depend/zcash/src/test/miner_tests.cpp | 461 ++++--- .../zcash/src/test/policyestimator_tests.cpp | 55 +- depend/zcash/src/test/rpc_tests.cpp | 34 +- depend/zcash/src/test/script_P2SH_tests.cpp | 2 +- depend/zcash/src/test/sighash_tests.cpp | 32 +- depend/zcash/src/test/test_bitcoin.cpp | 6 +- depend/zcash/src/test/test_bitcoin.h | 4 +- depend/zcash/src/test/test_util.cpp | 9 +- depend/zcash/src/test/transaction_tests.cpp | 50 +- depend/zcash/src/transaction_builder.cpp | 30 +- depend/zcash/src/transaction_builder.h | 7 +- depend/zcash/src/txdb.h | 1 + depend/zcash/src/txmempool.cpp | 139 ++- depend/zcash/src/txmempool.h | 113 +- depend/zcash/src/util/match.h | 13 + depend/zcash/src/util/moneystr.cpp | 4 + depend/zcash/src/util/moneystr.h | 3 + depend/zcash/src/util/test.cpp | 49 +- depend/zcash/src/util/test.h | 35 + depend/zcash/src/validationinterface.cpp | 6 - depend/zcash/src/validationinterface.h | 6 - .../src/wallet/asyncrpcoperation_common.cpp | 181 +++ .../src/wallet/asyncrpcoperation_common.h | 6 + .../asyncrpcoperation_mergetoaddress.cpp | 984 ++------------- .../wallet/asyncrpcoperation_mergetoaddress.h | 142 +-- .../src/wallet/asyncrpcoperation_sendmany.cpp | 700 +---------- .../src/wallet/asyncrpcoperation_sendmany.h | 44 +- .../asyncrpcoperation_shieldcoinbase.cpp | 478 ++----- .../wallet/asyncrpcoperation_shieldcoinbase.h | 130 +- .../src/wallet/gtest/test_orchard_wallet.cpp | 6 +- .../wallet/gtest/test_paymentdisclosure.cpp | 29 +- .../src/wallet/gtest/test_rpc_wallet.cpp | 258 ++-- depend/zcash/src/wallet/gtest/test_wallet.cpp | 42 +- depend/zcash/src/wallet/memo.h | 4 +- depend/zcash/src/wallet/orchard.h | 11 +- depend/zcash/src/wallet/paymentdisclosure.cpp | 25 +- depend/zcash/src/wallet/paymentdisclosure.h | 22 +- depend/zcash/src/wallet/rpcdisclosure.cpp | 30 +- depend/zcash/src/wallet/rpcdump.cpp | 4 +- depend/zcash/src/wallet/rpcwallet.cpp | 1089 ++++++++-------- .../src/wallet/test/rpc_wallet_tests.cpp | 166 +-- depend/zcash/src/wallet/test/wallet_tests.cpp | 4 +- depend/zcash/src/wallet/wallet.cpp | 623 ++++----- depend/zcash/src/wallet/wallet.h | 130 +- depend/zcash/src/wallet/wallet_tx_builder.cpp | 1108 +++++++++++++++++ depend/zcash/src/wallet/wallet_tx_builder.h | 431 ++++++- depend/zcash/src/wallet/walletdb.h | 4 +- depend/zcash/src/weighted_map.h | 199 +++ depend/zcash/src/zcash/Address.cpp | 8 +- depend/zcash/src/zcash/Address.hpp | 8 +- .../zcash/src/zcash/IncrementalMerkleTree.hpp | 52 +- depend/zcash/src/zcash/JoinSplit.cpp | 6 +- depend/zcash/src/zcash/JoinSplit.hpp | 6 +- depend/zcash/src/zcash/Note.cpp | 142 +-- depend/zcash/src/zcash/Note.hpp | 27 +- depend/zcash/src/zcash/NoteEncryption.cpp | 57 +- depend/zcash/src/zcash/NoteEncryption.hpp | 1 - depend/zcash/src/zcash/address/unified.cpp | 12 +- depend/zcash/src/zcbenchmarks.cpp | 15 +- depend/zcash/src/zip317.cpp | 39 + depend/zcash/src/zip317.h | 43 + depend/zcash/zcutil/fetch-params.sh | 50 +- depend/zcash/zcutil/make-release.py | 63 + depend/zcash/zcutil/release-notes.py | 1 + src/bridge.rs | 1 + src/builder_ffi.rs | 1 + src/incremental_merkle_tree.rs | 1 + src/lib.rs | 50 + src/merkle_frontier.rs | 1 + src/note_encryption.rs | 1 + src/orchard_bundle.rs | 1 + src/params.rs | 1 + src/sapling.rs | 1 + src/streams.rs | 1 + src/wallet.rs | 1 + src/wallet_scanner.rs | 1 + src/zcashd_orchard.rs | 1 + 325 files changed, 14679 insertions(+), 9820 deletions(-) create mode 100644 depend/zcash/.github/workflows/build.yml create mode 100644 depend/zcash/.github/workflows/checks.yml create mode 100644 depend/zcash/contrib/metrics/docker-compose.yml create mode 100644 depend/zcash/contrib/metrics/grafana/dashboards/zcashd-metrics.json create mode 100644 depend/zcash/contrib/metrics/grafana/grafana.ini create mode 100644 depend/zcash/contrib/metrics/grafana/provisioning/dashboards/zcash.yaml create mode 100644 depend/zcash/contrib/metrics/grafana/provisioning/datasources/prometheus.yaml create mode 100644 depend/zcash/depends/patches/native_cxxbridge/Cargo.lock create mode 100644 depend/zcash/doc/book/src/user/release-support.md create mode 100644 depend/zcash/doc/release-notes/release-notes-5.3.3.md create mode 100644 depend/zcash/doc/release-notes/release-notes-5.4.1.md create mode 100644 depend/zcash/doc/release-notes/release-notes-5.4.2.md create mode 100644 depend/zcash/doc/release-notes/release-notes-5.5.0-rc1.md create mode 100644 depend/zcash/doc/release-notes/release-notes-5.5.0-rc2.md create mode 100644 depend/zcash/doc/release-notes/release-notes-5.5.0-rc3.md create mode 100644 depend/zcash/doc/release-notes/release-notes-5.5.0.md create mode 100755 depend/zcash/qa/rpc-tests/mergetoaddress_ua_nu5.py create mode 100755 depend/zcash/qa/rpc-tests/mergetoaddress_ua_sapling.py create mode 100644 depend/zcash/qa/rpc-tests/test_framework/zip317.py create mode 100644 depend/zcash/src/gtest/test_weightedmap.cpp create mode 100644 depend/zcash/src/int128.h delete mode 100644 depend/zcash/src/rust/include/rust/ed25519.h delete mode 100644 depend/zcash/src/rust/include/rust/ed25519/types.h delete mode 100644 depend/zcash/src/rust/include/rust/orchard/incremental_merkle_tree.h create mode 100644 depend/zcash/src/rust/src/bridge.rs delete mode 100644 depend/zcash/src/rust/src/incremental_merkle_tree_ffi.rs create mode 100644 depend/zcash/src/rust/src/merkle_frontier.rs create mode 100644 depend/zcash/src/rust/src/note_encryption.rs create mode 100644 depend/zcash/src/rust/src/params.rs create mode 100644 depend/zcash/src/rust/src/streams.rs create mode 100644 depend/zcash/src/streams_rust.cpp create mode 100644 depend/zcash/src/streams_rust.h create mode 100644 depend/zcash/src/wallet/wallet_tx_builder.cpp create mode 100644 depend/zcash/src/weighted_map.h create mode 100644 depend/zcash/src/zip317.cpp create mode 100644 depend/zcash/src/zip317.h create mode 100644 src/bridge.rs create mode 100644 src/builder_ffi.rs create mode 100644 src/incremental_merkle_tree.rs create mode 100644 src/merkle_frontier.rs create mode 100644 src/note_encryption.rs create mode 100644 src/orchard_bundle.rs create mode 100644 src/params.rs create mode 100644 src/sapling.rs create mode 100644 src/streams.rs create mode 100644 src/wallet.rs create mode 100644 src/wallet_scanner.rs create mode 100644 src/zcashd_orchard.rs diff --git a/.gitignore b/.gitignore index 1c7eb663f..78bf16cb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea target *.iml -Cargo.lock \ No newline at end of file +Cargo.lock +.\#* diff --git a/CHANGELOG.md b/CHANGELOG.md index c1a7c3cd0..cebfddf54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +## [0.1.12] - 2023-05-03 + +### Changed +- Update `depend/zcash` to version 5.5.0 which includes updated dependencies + - This includes additional `zcashd` C++ and Rust code, and its dependencies + - Fix code generation C++ header paths to avoid conflicts +- Update other dependencies to match Zebra + +### Fixed +- Improve error reporting in `build.rs` + ## [0.1.11] - 2023-02-24 ### Changed diff --git a/Cargo.toml b/Cargo.toml index a3db88bf7..c594c36ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,10 +31,10 @@ include = [ "/depend/zcash/src/script/interpreter.cpp", "/depend/zcash/src/script/script.cpp", "/depend/zcash/src/script/script_error.cpp", - "/depend/zcash/src", + "/depend/zcash/src/", "/depend/zcash/src/rust/include/", - "/depend/zcash/src/secp256k1/include", - "/depend/zcash/src/secp256k1", + "/depend/zcash/src/secp256k1/include/", + "/depend/zcash/src/secp256k1/", "/depend/zcash/src/support/cleanse.cpp", "/depend/zcash/src/support/cleanse.h", "/depend/zcash/src/rust/gen/", @@ -50,16 +50,28 @@ external-secp = [] [dependencies] # All these dependencies must match the versions in: # https://github.com/zcash/zcash/blob//Cargo.toml +bellman = "0.14" blake2b_simd = "1" -cxx = { version = "=1.0.83", features = ["c++17"] } +bls12_381 = "0.8" +byteorder = "1" +crossbeam-channel = "0.5" +cxx = { version = "=1.0.94", features = ["c++17"] } +group = "0.13" +incrementalmerkletree = "0.3" +jubjub = "0.10" libc = "0.2" memuse = "0.2" -orchard = "0.3" +metrics = "0.20" +orchard = "0.4" rand_core = "0.6" +rayon = "1.5" +subtle = "2.2" tracing = "0.1" +zcash_address = "0.2" zcash_encoding = "0.2" -zcash_note_encryption = "0.2" -zcash_primitives = { version = "0.9.1", features = ["transparent-inputs"] } +zcash_note_encryption = "0.3" +zcash_primitives = { version = "0.11", features = ["temporary-zcashd", "transparent-inputs"] } +zcash_proofs = "0.11" [build-dependencies] # The `bindgen` dependency should automatically upgrade to match the version used by zebra-state's `rocksdb` dependency in: @@ -71,10 +83,10 @@ bindgen = ">= 0.64.0" # These dependencies are shared with a lot of other Zebra dependencies, # so they are configured to automatically upgrade to match Zebra. # But we try to use the latest versions here, to catch any bugs in `zcash_script`'s CI. -cc = { version = "1.0.73", features = ["parallel"] } +cc = { version = "1.0.79", features = ["parallel"] } # Treat minor versions with a zero major version as compatible (cargo doesn't by default). -cxx-gen = ">= 0.7.74" -syn = { version = "1.0.104", features = ["full", "printing"] } +cxx-gen = ">= 0.7.93" +syn = { version = "1.0.109", features = ["full", "printing"] } [dev-dependencies] # These dependencies are shared with a lot of other Zebra dependencies. diff --git a/README.md b/README.md index 6f63e4b4c..bba87233b 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,17 @@ to include a lot of other stuff e.g. the orchard library. ### Updating this crate -1. Update `depend/zcash` with the latest tagged version of `zcashd` +1. Update `depend/zcash` with the latest tagged version of `zcashd`, using the instructions below 2. Update `Cargo.toml` versions to match the versions used by the latest tagged version of `zcashd`, and its dependencies 3. For dependencies that are shared with Zebra (but not `zcashd`), match the latest version in Zebra's [Cargo.lock](https://github.com/ZcashFoundation/zebra/blob/main/Cargo.lock): - use `cargo tree --invert ` to see if the crate is from `zcash_script` or another dependency - see the list in [Cargo.toml](https://github.com/ZcashFoundation/zcash_script/blob/master/Cargo.toml#L69) 4. For new dependencies with a leading zero in their version (`0.x.y`), use a `>=` dependency [to make them automatically upgrade to match Zebra's dependencies](https://doc.rust-lang.org/cargo/reference/resolver.html#semver-compatibility) 5. Check all open PRs to see if they can be merged before the release -6. Run `cargo-release` with `--no-publish` to commit any automatic changes +6. Run `cargo release patch` to commit the release version bump (but not actually publish) 7. Open a `zcash_script` PR with the changes, get it reviewed, and wait for CI to pass -8. Publish a new release +8. Publish a new release using `cargo release --execute patch` +9. Check the release tag was pushed to https://github.com/ZcashFoundation/zcash_script/tags ### Updating `depend/zcash` diff --git a/build.rs b/build.rs index 1c8a0810d..ee85c537c 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,5 @@ +//! Build script for zcash_script. + use std::{env, fmt, fs, io::Read, path::PathBuf}; use syn::__private::ToTokens; @@ -62,23 +64,19 @@ fn bindgen_headers() -> Result<()> { /// (This is in contrast to zcash which generates in `depend/zcash/src/rust/gen/`) fn gen_cxxbridge() -> Result<()> { let out_path = env::var("OUT_DIR").map_err(Error::Env)?; - let out_path = PathBuf::from(out_path).join("include"); + let out_path = PathBuf::from(out_path).join("gen"); + let src_out_path = PathBuf::from(&out_path).join("src"); + let header_out_path = PathBuf::from(&out_path).join("include").join("rust"); // These must match `CXXBRIDGE_RS` in depend/zcash/src/Makefile.am - let filenames = [ - "blake2b", - "bundlecache", - "equihash", - "orchard_bundle", - "sapling", - "wallet_scanner", - ]; + let filenames = ["blake2b", "ed25519", "equihash", "streams", "bridge"]; // The output folder must exist - fs::create_dir_all(out_path.join("rust")).unwrap(); + fs::create_dir_all(&src_out_path).unwrap(); + fs::create_dir_all(&header_out_path).unwrap(); // Generate the generic header file - fs::write(out_path.join("rust/cxx.h"), cxx_gen::HEADER).unwrap(); + fs::write(header_out_path.join("cxx.h"), cxx_gen::HEADER).unwrap(); // Generate the source and header for each bridge file for filename in filenames { @@ -99,11 +97,19 @@ fn gen_cxxbridge() -> Result<()> { path: "rust/cxx.h".to_string(), kind: cxx_gen::IncludeKind::Quoted, }); - let output = cxx_gen::generate_header_and_cc(token_stream, &opt).unwrap(); + let output = cxx_gen::generate_header_and_cc(token_stream, &opt).unwrap_or_else(|err| { + panic!( + "invalid bridge file {filename}: {err}. Try updating `filenames` to match zcashd" + ) + }); - fs::write(out_path.join(format!("rust/{}.h", filename)), output.header).unwrap(); fs::write( - out_path.join(format!("rust/{}.c", filename)), + header_out_path.join(format!("{}.h", filename)), + output.header, + ) + .unwrap(); + fs::write( + src_out_path.join(format!("{}.c", filename)), output.implementation, ) .unwrap(); @@ -115,8 +121,8 @@ fn main() -> Result<()> { bindgen_headers()?; gen_cxxbridge()?; - let include_path = env::var("OUT_DIR").map_err(Error::Env)?; - let include_path = PathBuf::from(include_path).join("include"); + let gen_path = env::var("OUT_DIR").map_err(Error::Env)?; + let gen_path = PathBuf::from(gen_path).join("gen"); let target = env::var("TARGET").expect("TARGET was not set"); let mut base_config = cc::Build::new(); @@ -124,10 +130,10 @@ fn main() -> Result<()> { language_std(&mut base_config, "c++17"); base_config - .include("depend/zcash/src") + .include("depend/zcash/src/") .include("depend/zcash/src/rust/include/") - .include("depend/zcash/src/secp256k1/include") - .include(&include_path) + .include("depend/zcash/src/secp256k1/include/") + .include(&gen_path.join("include")) .flag_if_supported("-Wno-implicit-fallthrough") .flag_if_supported("-Wno-catch-value") .flag_if_supported("-Wno-reorder") @@ -156,6 +162,7 @@ fn main() -> Result<()> { .file("depend/zcash/src/uint256.cpp") .file("depend/zcash/src/pubkey.cpp") .file("depend/zcash/src/hash.cpp") + .file("depend/zcash/src/streams_rust.cpp") .file("depend/zcash/src/primitives/transaction.cpp") .file("depend/zcash/src/crypto/ripemd160.cpp") .file("depend/zcash/src/crypto/sha1.cpp") @@ -166,7 +173,11 @@ fn main() -> Result<()> { .file("depend/zcash/src/script/script.cpp") .file("depend/zcash/src/script/script_error.cpp") .file("depend/zcash/src/support/cleanse.cpp") - .file(include_path.join("rust/blake2b.c")) + // A subset of the files generated by gen_cxxbridge + // which are required by zcash_script. + .file(gen_path.join("src/blake2b.c")) + .file(gen_path.join("src/bridge.c")) + .file(gen_path.join("src/streams.c")) .compile("libzcash_script.a"); Ok(()) @@ -181,7 +192,8 @@ fn build_secp256k1() { // Define configuration constants build - .define("SECP256K1_BUILD", "1") + // This matches the #define in depend/zcash/src/secp256k1/src/secp256k1.c + .define("SECP256K1_BUILD", "") .define("USE_NUM_NONE", "1") .define("USE_FIELD_INV_BUILTIN", "1") .define("USE_SCALAR_INV_BUILTIN", "1") diff --git a/depend/zcash/.editorconfig b/depend/zcash/.editorconfig index 71ae7b106..2ffda198d 100644 --- a/depend/zcash/.editorconfig +++ b/depend/zcash/.editorconfig @@ -12,3 +12,6 @@ indent_size = 4 indent_style = space insert_final_newline = true trim_trailing_whitespace = true + +[{Makefile*, *.mk}] +indent_style = tab diff --git a/depend/zcash/.github/workflows/audits.yml b/depend/zcash/.github/workflows/audits.yml index db1d6fc8f..ab19a3dc4 100644 --- a/depend/zcash/.github/workflows/audits.yml +++ b/depend/zcash/.github/workflows/audits.yml @@ -11,20 +11,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - - name: Install cargo-vet - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-vet - - - name: Run cargo vet --locked - uses: actions-rs/cargo@v1 - with: - command: vet - args: --locked + - uses: dtolnay/rust-toolchain@stable + id: toolchain + - run: rustup override set ${{steps.toolchain.outputs.name}} + - run: cargo install cargo-vet --version ~0.6 + - run: cargo vet --locked diff --git a/depend/zcash/.github/workflows/book.yml b/depend/zcash/.github/workflows/book.yml index 86d3d076c..fac308c59 100644 --- a/depend/zcash/.github/workflows/book.yml +++ b/depend/zcash/.github/workflows/book.yml @@ -21,10 +21,7 @@ jobs: mdbook-version: 'latest' - name: Install mdbook-katex - uses: actions-rs/cargo@v1 - with: - command: install - args: mdbook-katex + run: cargo install mdbook-katex - name: Build zcashd book run: mdbook build doc/book/ diff --git a/depend/zcash/.github/workflows/build.yml b/depend/zcash/.github/workflows/build.yml new file mode 100644 index 000000000..bd2a3d2b3 --- /dev/null +++ b/depend/zcash/.github/workflows/build.yml @@ -0,0 +1,112 @@ +name: Build + +on: [push, pull_request] + +jobs: + build: + name: Tier ${{ matrix.tier }} platform ${{ matrix.platform }} + runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.tier == 3 }} + strategy: + matrix: + include: + - name: ubuntu-20.04 + tier: 1 + platform: Ubuntu 20.04 + os: ubuntu-20.04 + + - name: macos-11 + tier: 3 + platform: macOS Big Sur 11 + os: macos-11 + brew_deps: > + autoconf + automake + coreutils + libtool + pkgconfig + + - name: mingw32 + tier: 3 + platform: Windows (64-bit MinGW) + os: ubuntu-latest + cross_deps: > + mingw-w64 + host: HOST=x86_64-w64-mingw32 + + - name: aarch64-linux + tier: 3 + platform: ARM64 Linux + os: ubuntu-latest + cross_deps: > + g++-aarch64-linux-gnu + host: HOST=aarch64-linux-gnu + + steps: + - uses: actions/checkout@v3 + + - name: Install Homebrew build dependencies + if: matrix.brew_deps != '' + run: brew install ${{ matrix.brew_deps }} + + - name: Install cross-compilation build dependencies + if: matrix.cross_deps != '' + run: sudo apt install ${{ matrix.cross_deps }} + + - name: Configure MinGW to use POSIX variant + if: matrix.name == 'mingw32' + run: | + sudo update-alternatives --set x86_64-w64-mingw32-gcc $(update-alternatives --query x86_64-w64-mingw32-gcc | grep Alternative | grep posix | cut -d' ' -f2) + sudo update-alternatives --set x86_64-w64-mingw32-g++ $(update-alternatives --query x86_64-w64-mingw32-g++ | grep Alternative | grep posix | cut -d' ' -f2) + + - name: Cache built dependencies + uses: actions/cache@v3 + with: + path: depends/built + key: ${{ matrix.name }}-built-${{ hashFiles('depends/Makefile', 'depends/funcs.mk') }}-${{ hashFiles('depends/packages/*.mk', 'depends/patches/**/*') }} + restore-keys: | + ${{ matrix.name }}-built-${{ hashFiles('depends/Makefile', 'depends/funcs.mk') }}- + + - name: Prepare ccache timestamp + id: ccache_cache_timestamp + shell: bash + run: echo "timestamp=$(date +'%Y-%m-%d-%H;%M;%S')" >> "$GITHUB_OUTPUT" + + - name: Cache ccache files + uses: actions/cache@v3 + with: + path: ~/.cache/ccache + key: ${{ matrix.name }}-ccache-${{ steps.ccache_cache_timestamp.outputs.timestamp }} + restore-keys: | + ${{ matrix.name }}-ccache- + + - name: Build zcashd + id: build + run: > + ${{ matrix.host }} + ./zcutil/build.sh + -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" + + - name: Build zcashd with libraries enabled + if: ${{ always() && steps.build.outcome == 'success' }} + run: > + CONFIGURE_FLAGS="--with-libs" + ${{ matrix.host }} + ./zcutil/build.sh + -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" + + - name: Build zcashd with wallet disabled + if: ${{ always() && steps.build.outcome == 'success' }} + run: > + CONFIGURE_FLAGS="--disable-wallet" + ${{ matrix.host }} + ./zcutil/build.sh + -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" + + - name: Build zcashd with mining disabled + if: ${{ always() && steps.build.outcome == 'success' }} + run: > + CONFIGURE_FLAGS="--disable-mining" + ${{ matrix.host }} + ./zcutil/build.sh + -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)" diff --git a/depend/zcash/.github/workflows/checks.yml b/depend/zcash/.github/workflows/checks.yml new file mode 100644 index 000000000..a39d54a59 --- /dev/null +++ b/depend/zcash/.github/workflows/checks.yml @@ -0,0 +1,45 @@ +name: Checks + +on: pull_request_target + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + recent-base: + name: Branch base is sufficiently recent + runs-on: ubuntu-latest + steps: + - name: Check out the base branch + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Check out the PR branch + uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + + - name: Ensure branch contains necessary commits for Tekton CI + id: tekton + # https://github.com/zcash/zcash/pull/6358 + run: git merge-base --is-ancestor 267ddf8efe36cc799c4c31772a8883ea332ef55b ${{ github.head_ref }} + + - name: Tell PR author if they need to rebase + if: failure() && steps.tekton.outcome == 'failure' + run: echo "::error::Branch needs to be rebased so that Tekton CI can run" + + - name: Avoid running Tekton CI if it would provably fail + if: failure() && steps.tekton.outcome == 'failure' + uses: actions/github-script@v6 + with: + script: | + github.rest.issues.removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: "safe-to-build", + }) diff --git a/depend/zcash/.github/workflows/lints.yml b/depend/zcash/.github/workflows/lints.yml index bcb957bc6..71ca24dc6 100644 --- a/depend/zcash/.github/workflows/lints.yml +++ b/depend/zcash/.github/workflows/lints.yml @@ -75,19 +75,14 @@ jobs: if: always() rust-clippy: - name: Clippy (1.64.0) + name: Clippy (MSRV) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.64.0 - components: clippy - override: true - name: Run clippy uses: actions-rs/clippy-check@v1 with: - name: Clippy (1.64.0) + name: Clippy (MSRV) token: ${{ secrets.GITHUB_TOKEN }} args: --all-features --all-targets -- -D warnings @@ -96,12 +91,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.64.0 - override: true - - run: rustup component add rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check + - run: cargo fmt -- --check diff --git a/depend/zcash/Cargo.lock b/depend/zcash/Cargo.lock index 0f7382dbf..bddc5c7bb 100644 --- a/depend/zcash/Cargo.lock +++ b/depend/zcash/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", "generic-array", @@ -29,14 +29,13 @@ dependencies = [ [[package]] name = "aes" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" dependencies = [ - "cfg-if 1.0.0", - "cipher 0.3.0", + "cfg-if", + "cipher", "cpufeatures", - "opaque-debug", ] [[package]] @@ -45,7 +44,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.8", + "getrandom", "once_cell", "version_check", ] @@ -61,15 +60,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -91,7 +90,7 @@ checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object", @@ -106,15 +105,15 @@ checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" [[package]] name = "bech32" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] name = "bellman" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4dd656ef4fdf7debb6d87d4dd92642fcbcdb78cbf6600c13e25c87e4d1a3807" +checksum = "9afceed28bac7f9f5a508bca8aeeff51cdfa4770c0b967ac55c621e2ddfd6171" dependencies = [ "bitvec", "blake2s_simd", @@ -133,14 +132,14 @@ dependencies = [ [[package]] name = "bip0039" -version = "0.9.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0830ae4cc96b0617cc912970c2b17e89456fecbf55e8eed53a956f37ab50c41" +checksum = "bef0f0152ec5cf17f49a5866afaa3439816207fd4f0a224c0211ffaf5e278426" dependencies = [ "hmac", "pbkdf2", - "rand 0.8.5", - "sha2", + "rand", + "sha2 0.10.6", "unicode-normalization", "zeroize", ] @@ -165,9 +164,9 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec", @@ -176,9 +175,9 @@ dependencies = [ [[package]] name = "blake2s_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", "arrayvec", @@ -196,34 +195,18 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] -[[package]] -name = "block-modes" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" -dependencies = [ - "block-padding", - "cipher 0.3.0", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "bls12_381" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" +checksum = "d7bc6d6292be3a19e6379786dac800f551e5865a5bb51ebbe3064ab80433f403" dependencies = [ "ff", "group", @@ -238,14 +221,14 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" dependencies = [ - "sha2", + "sha2 0.9.9", ] [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-slice-cast" @@ -261,21 +244,24 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] -name = "cc" -version = "1.0.78" +name = "cbc" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] [[package]] -name = "cfg-if" -version = "0.1.10" +name = "cc" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -285,12 +271,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chacha20" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ - "cfg-if 1.0.0", - "cipher 0.4.3", + "cfg-if", + "cipher", "cpufeatures", ] @@ -302,25 +288,16 @@ checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", "chacha20", - "cipher 0.4.3", + "cipher", "poly1305", "zeroize", ] [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - -[[package]] -name = "cipher" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", @@ -329,9 +306,9 @@ dependencies = [ [[package]] name = "clearscreen" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41aa24cc5e1d6b3fc49ad4cd540b522fedcbe88bc6f259ff16e20e7010b6f8c7" +checksum = "72f3f22f1a586604e62efd23f78218f3ccdecf7a33c4500db2d37d85a24fe994" dependencies = [ "nix", "terminfo", @@ -342,48 +319,48 @@ dependencies = [ [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" dependencies = [ "libc", ] [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", "memoffset", "scopeguard", @@ -391,11 +368,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -414,16 +391,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -439,9 +406,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -451,19 +418,19 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -481,27 +448,27 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "crypto-common", + "subtle", ] [[package]] name = "directories" -version = "4.0.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +checksum = "74be3be809c18e089de43bdc504652bb2bc473fca8756131f8689db8cf079ba9" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.0", ] [[package]] name = "dirs" -version = "2.0.2" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ - "cfg-if 0.1.10", - "dirs-sys", + "dirs-sys 0.3.7", ] [[package]] @@ -515,6 +482,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b" +dependencies = [ + "libc", + "redox_users", + "windows-sys", +] + [[package]] name = "ed25519-zebra" version = "3.1.0" @@ -526,15 +504,15 @@ dependencies = [ "hex", "rand_core 0.6.4", "serde", - "sha2", + "sha2 0.9.9", "zeroize", ] [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "equihash" @@ -557,9 +535,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "bitvec", "rand_core 0.6.4", @@ -573,7 +551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" dependencies = [ "byteorder", - "rand 0.8.5", + "rand", "rustc-hex", "static_assertions", ] @@ -586,12 +564,12 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fpe" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd910db5f9ca4dc3116f8c46367825807aa2b942f72565f16b4be0b208a00a9e" +checksum = "26c4b37de5ae15812a764c958297cfc50f5c010438f60c6ce75d11b802abd404" dependencies = [ - "block-modes", - "cipher 0.3.0", + "cbc", + "cipher", "libm", "num-bigint", "num-integer", @@ -606,30 +584,30 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -639,9 +617,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -649,37 +627,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "gimli" -version = "0.27.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "group" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "memuse", @@ -704,14 +671,14 @@ checksum = "729f9bd3449d77e7831a18abfb7ba2f99ee813dfd15b8c2167c9a54ba20aa99d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "halo2_gadgets" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e10bf9924da1754e443641c9e7f9f00483749f8fb837fde696ef6ed6e2f079" +checksum = "126a150072b0c38c7b573fe3eaf0af944a7fed09e154071bf2436d3f016f7230" dependencies = [ "arrayvec", "bitvec", @@ -720,23 +687,30 @@ dependencies = [ "halo2_proofs", "lazy_static", "pasta_curves", - "rand 0.8.5", + "rand", "subtle", "uint", ] +[[package]] +name = "halo2_legacy_pdqsort" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47716fe1ae67969c5e0b2ef826f32db8c3be72be325e1aa3c1951d06b5575ec5" + [[package]] name = "halo2_proofs" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff771b9a2445cd2545c9ef26d863c290fbb44ae440c825a20eb7156f67a949a" +checksum = "7b867a8d9bbb85fca76fff60652b5cd19b853a1c4d0665cb89bee68b18d2caf0" dependencies = [ "blake2b_simd", "ff", "group", + "halo2_legacy_pdqsort", + "maybe-rayon", "pasta_curves", "rand_core 0.6.4", - "rayon", "tracing", ] @@ -778,19 +752,18 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hmac" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "crypto-mac", - "digest 0.9.0", + "digest 0.10.6", ] [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -822,9 +795,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -860,23 +833,23 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "incrementalmerkletree" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068c5bdd31006d55536655cf1eb0d22d84d28de7c725b419480fd5d005c83216" +checksum = "d5ad43a3f5795945459d577f6589cf62a476e92c79b75e70cd954364e14ce17b" dependencies = [ "serde", ] [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -893,30 +866,30 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] [[package]] name = "jubjub" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a575df5f985fe1cd5b2b05664ff6accfc46559032b954529fd225a2168d27b0f" +checksum = "8499f7a74008aafbecb2a2e608a3e13e4dd3e84df198b604451efe93f2de6e61" dependencies = [ "bitvec", "bls12_381", @@ -931,12 +904,15 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "libc" -version = "0.2.139" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libm" @@ -975,14 +951,14 @@ dependencies = [ "metrics-util", "nonempty", "orchard", - "rand 0.8.5", + "rand", "rand_core 0.6.4", "rayon", "secp256k1", "secrecy", "serde", "serde_json", - "sha2", + "sha2 0.10.6", "subtle", "thiserror", "time", @@ -1026,7 +1002,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1047,6 +1023,16 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "memchr" version = "2.5.0" @@ -1055,9 +1041,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] @@ -1109,7 +1095,7 @@ checksum = "731f8ecebd9f3a4aa847dfe75455e4757a45da40a7793d2f0b1f9b6ed18b23f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1130,6 +1116,12 @@ dependencies = [ "sketches-ddsketch", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.6.2" @@ -1141,9 +1133,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", @@ -1153,24 +1145,24 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "libc", "static_assertions", ] [[package]] name = "nom" -version = "5.1.2" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", - "version_check", + "minimal-lexical", ] [[package]] @@ -1231,18 +1223,18 @@ dependencies = [ [[package]] name = "object" -version = "0.30.2" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "opaque-debug" @@ -1252,9 +1244,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f06b263206a75a7d96ca75d46a3e9ca8eaf7ab7feea209749bb8b818d22f427" +checksum = "2c6f418f2c25573923f81a091f38b4b19bc20f6c92b5070fb8f0711e64a2b998" dependencies = [ "aes", "bitvec", @@ -1270,7 +1262,7 @@ dependencies = [ "memuse", "nonempty", "pasta_curves", - "rand 0.8.5", + "rand", "reddsa", "serde", "subtle", @@ -1286,18 +1278,18 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pairing" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" dependencies = [ "group", ] [[package]] name = "parity-scale-codec" -version = "3.2.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" dependencies = [ "arrayvec", "bitvec", @@ -1309,14 +1301,14 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1331,11 +1323,11 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", "smallvec", @@ -1355,43 +1347,43 @@ dependencies = [ [[package]] name = "pasta_curves" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc65faf8e7313b4b1fbaa9f7ca917a0eed499a9663be71477f87993604341d8" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" dependencies = [ "blake2b_simd", "ff", "group", "lazy_static", - "rand 0.8.5", + "rand", "static_assertions", "subtle", ] [[package]] name = "pbkdf2" -version = "0.9.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" +checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" dependencies = [ - "crypto-mac", + "digest 0.10.6", "password-hash", ] [[package]] name = "phf" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" dependencies = [ "phf_shared", ] [[package]] name = "phf_codegen" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770" dependencies = [ "phf_generator", "phf_shared", @@ -1399,19 +1391,19 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" dependencies = [ "phf_shared", - "rand 0.7.3", + "rand", ] [[package]] name = "phf_shared" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" dependencies = [ "siphasher", ] @@ -1464,20 +1456,19 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -1500,9 +1491,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -1513,20 +1504,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - [[package]] name = "rand" version = "0.8.5" @@ -1534,20 +1511,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", + "rand_chacha", "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - [[package]] name = "rand_chacha" version = "0.3.1" @@ -1563,9 +1530,6 @@ name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] [[package]] name = "rand_core" @@ -1573,41 +1537,23 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] name = "raw-cpuid" -version = "10.6.0" +version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ "bitflags", ] [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -1615,9 +1561,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1627,13 +1573,14 @@ dependencies = [ [[package]] name = "reddsa" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc8038c8b7e481bdf688d0585d4897ed0e9e0cee10aa365dde51238c20e4182" +checksum = "54b34d2c0df43159d2ff79d3cf929c9f11415529127344edb8160ad2be499fcd" dependencies = [ "blake2b_simd", "byteorder", "group", + "hex", "jubjub", "pasta_curves", "rand_core 0.6.4", @@ -1644,15 +1591,12 @@ dependencies = [ [[package]] name = "redjubjub" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6039ff156887caf92df308cbaccdc058c9d3155a913da046add6e48c4cdbd91d" +checksum = "7a60db2c3bc9c6fd1e8631fee75abc008841d27144be744951d6b9b75f9b569c" dependencies = [ - "blake2b_simd", - "byteorder", - "digest 0.9.0", - "jubjub", "rand_core 0.6.4", + "reddsa", "serde", "thiserror", "zeroize", @@ -1673,16 +1617,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.8", + "getrandom", "redox_syscall", "thiserror", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "regex-syntax", ] @@ -1698,9 +1642,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "ring" @@ -1728,9 +1672,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hex" @@ -1740,9 +1684,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "scopeguard" @@ -1779,29 +1723,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -1815,12 +1759,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -1838,9 +1793,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "sketches-ddsketch" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceb945e54128e09c43d8e4f1277851bd5044c6fc540bbaa2ad888f60b3da9ae7" +checksum = "68a406c1882ed7f29cd5e248c9848a80e7cb6ae0fea82346d2746f2f941c07e1" [[package]] name = "smallvec" @@ -1850,9 +1805,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1878,9 +1833,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -1888,15 +1843,14 @@ dependencies = [ ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "syn" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", - "syn", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1907,9 +1861,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "terminfo" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76971977e6121664ec1b960d1313aacfa75642adc93b9d4d53b247bd4cb1747e" +checksum = "666cd3a6681775d22b200409aad3b089c5b99fb11ecdd8a204d9d62f8148498f" dependencies = [ "dirs", "fnv", @@ -1920,38 +1874,39 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] [[package]] name = "time" -version = "0.3.17" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ "itoa", "serde", @@ -1967,9 +1922,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" dependencies = [ "time-core", ] @@ -1985,15 +1940,15 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.24.1" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "libc", @@ -2004,12 +1959,20 @@ dependencies = [ ] [[package]] -name = "toml" -version = "0.5.10" +name = "toml_datetime" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ - "serde", + "indexmap", + "toml_datetime", + "winnow", ] [[package]] @@ -2024,7 +1987,7 @@ version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2049,7 +2012,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2105,9 +2068,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -2118,12 +2081,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "universal-hash" version = "0.5.0" @@ -2162,12 +2119,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" @@ -2182,34 +2133,34 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2217,28 +2168,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -2246,9 +2197,9 @@ dependencies = [ [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -2279,9 +2230,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -2294,45 +2254,54 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "winnow" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +dependencies = [ + "memchr", +] [[package]] name = "wyz" @@ -2345,9 +2314,9 @@ dependencies = [ [[package]] name = "zcash_address" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804268e702b664fc09d3e2ce82786d0addf4ae57ba6976469be63e09066bf9f7" +checksum = "52be35a205369d480378646bff9c9fedafd8efe8af1e0e54bb858f405883f2b2" dependencies = [ "bech32", "bs58", @@ -2378,22 +2347,22 @@ dependencies = [ [[package]] name = "zcash_note_encryption" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be9c12532389fd03786b7068fb7936c17fade23b48f584707bdc5f79f3ec867" +checksum = "2eb2149e6cd5fbee36c5b87c601715a8c35554602f7fe84af38b636afa2db318" dependencies = [ "chacha20", "chacha20poly1305", - "cipher 0.4.3", + "cipher", "rand_core 0.6.4", "subtle", ] [[package]] name = "zcash_primitives" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9a45953c4ddd81d68f45920955707f45c8926800671f354dd13b97507edf28" +checksum = "914d2195a478d5b63191584dff126f552751115181857b290211ec88e68acc3e" dependencies = [ "aes", "bip0039", @@ -2414,11 +2383,11 @@ dependencies = [ "memuse", "nonempty", "orchard", - "rand 0.8.5", + "rand", "rand_core 0.6.4", "ripemd", "secp256k1", - "sha2", + "sha2 0.10.6", "subtle", "zcash_address", "zcash_encoding", @@ -2427,9 +2396,9 @@ dependencies = [ [[package]] name = "zcash_proofs" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77381adc72286874e563ee36ba99953946abcbd195ada45440a2754ca823d407" +checksum = "e5c8147884952748b00aa443d36511ae2d7b49acfec74cfd39c0959fbb61ef14" dependencies = [ "bellman", "blake2b_simd", @@ -2446,21 +2415,20 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", - "synstructure", + "syn 2.0.15", ] diff --git a/depend/zcash/INSTALL b/depend/zcash/INSTALL index eecfe36e1..9c62d3995 100644 --- a/depend/zcash/INSTALL +++ b/depend/zcash/INSTALL @@ -1,5 +1,5 @@ Building Zcash -See the Zcash documentation wiki (https://zcash.readthedocs.io/en/latest/rtd_pages/user_guide.html) for instructions on building zcashd, +See the Zcash documentation wiki (https://zcash.readthedocs.io/en/latest/rtd_pages/zcashd.html) for instructions on building zcashd, the intended-for-services, no-graphical-interface, reference implementation of Zcash. diff --git a/depend/zcash/Makefile.am b/depend/zcash/Makefile.am index f9b60c8df..a5fa7513f 100644 --- a/depend/zcash/Makefile.am +++ b/depend/zcash/Makefile.am @@ -15,8 +15,6 @@ SUBDIRS += doc/man endif .PHONY: deploy FORCE rpc-tests -GZIP_ENV="-9n" - if BUILD_BITCOIN_LIBS pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libzcash_script.pc diff --git a/depend/zcash/README.md b/depend/zcash/README.md index cdba61ea0..7e5743b77 100644 --- a/depend/zcash/README.md +++ b/depend/zcash/README.md @@ -1,4 +1,4 @@ -Zcash 5.4.0 +Zcash 5.5.0 =========== diff --git a/depend/zcash/SECURITY.md b/depend/zcash/SECURITY.md index 22f991c92..0baf1be36 100644 --- a/depend/zcash/SECURITY.md +++ b/depend/zcash/SECURITY.md @@ -52,7 +52,7 @@ We have set up agreements with the following neighboring projects to share vulne Specifically, we have agreed to engage in responsible disclosures for security issues affecting this repository with the following contacts: - Zcash Foundation https://github.com/ZcashFoundation/zebra/security/policy -- Horizen security@horizen.com via PGP +- Horizen security@horizen.io https://github.com/HorizenOfficial/zen/blob/master/SECURITY.md - Komodo ca333@komodoplatform.com via PGP - BitcoinABC https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/DISCLOSURE_POLICY.md diff --git a/depend/zcash/configure.ac b/depend/zcash/configure.ac index c3591944b..e068dd1c6 100644 --- a/depend/zcash/configure.ac +++ b/depend/zcash/configure.ac @@ -1,7 +1,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 5) -define(_CLIENT_VERSION_MINOR, 4) +define(_CLIENT_VERSION_MINOR, 5) define(_CLIENT_VERSION_REVISION, 0) define(_CLIENT_VERSION_BUILD, 50) define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50))) diff --git a/depend/zcash/contrib/debian/changelog b/depend/zcash/contrib/debian/changelog index ef30326cf..5fdfb1dff 100644 --- a/depend/zcash/contrib/debian/changelog +++ b/depend/zcash/contrib/debian/changelog @@ -1,3 +1,39 @@ +zcash (5.5.0) stable; urgency=medium + + * 5.5.0 release. + + -- Electric Coin Company Thu, 27 Apr 2023 16:17:00 -0600 + +zcash (5.5.0~rc3) stable; urgency=medium + + * 5.5.0-rc3 release. + + -- Electric Coin Company Wed, 26 Apr 2023 22:41:41 -0600 + +zcash (5.5.0~rc2) stable; urgency=medium + + * 5.5.0-rc2 release. + + -- Electric Coin Company Tue, 25 Apr 2023 18:30:53 +0000 + +zcash (5.5.0~rc1) stable; urgency=medium + + * 5.5.0-rc1 release. + + -- Electric Coin Company Thu, 20 Apr 2023 22:19:50 +0000 + +zcash (5.4.2) stable; urgency=high + + * 5.4.2 release. + + -- Electric Coin Company Mon, 20 Feb 2023 20:04:31 -0700 + +zcash (5.4.1) stable; urgency=medium + + * 5.4.1 release. + + -- Electric Coin Company Mon, 13 Feb 2023 18:10:07 +0000 + zcash (5.4.0) stable; urgency=medium * 5.4.0 release. @@ -28,6 +64,12 @@ zcash (5.4.0~rc1) stable; urgency=medium -- Electric Coin Company Thu, 19 Jan 2023 22:57:59 +0000 +zcash (5.3.3) stable; urgency=high + + * 5.3.3 release. + + -- Electric Coin Company Mon, 20 Feb 2023 19:47:26 -0700 + zcash (5.3.2) stable; urgency=medium * 5.3.2 release. diff --git a/depend/zcash/contrib/debian/copyright b/depend/zcash/contrib/debian/copyright index 5d7ccc974..8723592bd 100644 --- a/depend/zcash/contrib/debian/copyright +++ b/depend/zcash/contrib/debian/copyright @@ -108,6 +108,7 @@ Copyright: 2014-2015, Brian Davis License: BSD-3-clause-Tobias-Klein Files: depends/sources/libsodium-*.tar.gz + depends/patches/libsodium/* Copyright: 2013-2022 Frank Denis License: ISC @@ -122,6 +123,7 @@ Comment: This entry is specifically for the libc++ library. The libc++ Authors are listed in https://github.com/llvm/llvm-project/blob/main/libcxx/CREDITS.TXT . Files: depends/sources/db-*.tar.gz + depends/patches/bdb/* Copyright: 1990, 2016 Oracle and/or its affiliates; 1990, 1993, 1994, 1995 The Regents of the University of California; 1995, 1996 The President and Fellows of Harvard University; @@ -129,6 +131,7 @@ Copyright: 1990, 2016 Oracle and/or its affiliates; License: BDB Files: depends/sources/libevent-*.tar.gz + depends/patches/libevent/* Copyright: 2000-2007 Niels Provos 2007-2012 Niels Provos and Nick Mathewson 2000 Dug Song @@ -141,6 +144,7 @@ Copyright: 2000-2007 Niels Provos License: BSD-3-clause Files: depends/sources/zeromq-*.tar.gz + depends/patches/zeromq/* Copyright: 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. 2007-2014 iMatix Corporation 2009-2011 250bpm s.r.o. @@ -161,7 +165,8 @@ Copyright: 2006 Nemanja Trifunovic License: Boost-Software-License-1.0 Files: depends/sources/tl-expected-*.tar.gz -Copyright: 2017-2021 Sy Brand (@TartanLlama), 2022 The Zcash developers + depends/patches/tl_expected/* +Copyright: 2017-2022 Sy Brand (@TartanLlama), 2022 The Zcash developers License: CC0-1.0 Comment: Other contributors are Simon Truscott (@bobbleclank), Kévin Alexandre Boissonneault (@KABoissonneault), and Björn Fahller (@rollbear). diff --git a/depend/zcash/contrib/debian/examples/zcash.conf b/depend/zcash/contrib/debian/examples/zcash.conf index 0293500d0..c01332d85 100644 --- a/depend/zcash/contrib/debian/examples/zcash.conf +++ b/depend/zcash/contrib/debian/examples/zcash.conf @@ -2,7 +2,9 @@ ## zcash.conf configuration file. Lines beginning with # are comments. ## -# Network-related settings: +## +## Network-related settings +## # Run on the test network instead of the real zcash network. #testnet=0 @@ -56,9 +58,9 @@ # Maximum number of inbound+outbound connections. #maxconnections= -# -# JSON-RPC options (for controlling a running Zcash/zcashd process) -# +## +## JSON-RPC options (for controlling a running Zcash/zcashd process) +## # server=1 tells zcashd to accept JSON-RPC commands (set as default if not specified) #server=1 @@ -72,7 +74,7 @@ #rpcpassword=YourSuperGreatPasswordNumber_DO_NOT_USE_THIS_OR_YOU_WILL_GET_ROBBED_385593 # How many seconds zcash will wait for a complete RPC HTTP request. -# after the HTTP connection is established. +# after the HTTP connection is established. #rpcclienttimeout=30 # By default, only RPC connections from localhost are allowed. @@ -80,7 +82,7 @@ # either as a single IPv4/IPv6 or with a subnet specification. # NOTE: opening up the RPC port to hosts outside your local trusted network is NOT RECOMMENDED, -# because the rpcpassword is transmitted over the network unencrypted and also because anyone +# because the rpcpassword is transmitted over the network unencrypted and also because anyone # that can authenticate on the RPC port can steal your keys + take over the account running zcashd # For more information see https://github.com/zcash/zcash/issues/1497 @@ -91,22 +93,32 @@ # Listen for RPC connections on this TCP port: #rpcport=8232 -# You can use Zcash or zcashd to send commands to Zcash/zcashd -# running on another host using this option: +# You can use zcash-cli to send commands to zcashd running on another host using +# this option: #rpcconnect=127.0.0.1 -# Transaction Fee +## +## Transaction creation options for 'z_*' APIs. +## + +# Set the maximum number of Orchard actions permitted in a transaction. +#orchardactionlimit=50 -# Send transactions as zero-fee transactions if possible (default: 0) -#sendfreetransactions=0 +## +## Transaction creation options for legacy APIs (sendtoaddress, sendmany, +## and fundrawtransaction). +## -# Create transactions that have enough fees (or priority) so they are likely to # begin confirmation within n blocks (default: 1). -# This setting is overridden by the -paytxfee option. -#txconfirmtarget=n +# The preferred fee rate (in ZEC per 1000 bytes) used for transactions +# created by legacy APIs. If the transaction is less than 1000 bytes then +# the fee rate is applied as though it were 1000 bytes. +#paytxfee= -# Miscellaneous options +## +## Miscellaneous options +## -# Enable attempt to mine zcash. +# Enable attempt to CPU-mine zcash. This is only useful on testnet. #gen=0 # Set the number of threads to be used for mining zcash (-1 = all cores). @@ -119,11 +131,3 @@ # Pre-generate this many public/private key pairs, so wallet backups will be valid for # both prior transactions and several dozen future transactions. #keypool=100 - -# Pay an optional transaction fee every time you send zcash. Transactions with fees -# are more likely than free transactions to be included in generated blocks, so may -# be validated sooner. This setting does not affect private transactions created with -# 'z_sendmany'. -#paytxfee=0.00 - - diff --git a/depend/zcash/contrib/devtools/update-clang-hashes.sh b/depend/zcash/contrib/devtools/update-clang-hashes.sh index 0e64360bb..cf682392f 100755 --- a/depend/zcash/contrib/devtools/update-clang-hashes.sh +++ b/depend/zcash/contrib/devtools/update-clang-hashes.sh @@ -45,4 +45,3 @@ update_clang_hash amd64-unknown-freebsd12 freebsd # For Windows cross-compilation # update_libcxx_msys2_hash LIBCXX_LIBRARY MAKEFILE_HASH_SUFFIX update_libcxx_msys2_hash libc++ sha256_hash -update_libcxx_msys2_hash libc++abi libcxxabi_sha256_hash diff --git a/depend/zcash/contrib/gitian-descriptors/gitian-linux-parallel.yml b/depend/zcash/contrib/gitian-descriptors/gitian-linux-parallel.yml index 23f383025..9aa85723f 100644 --- a/depend/zcash/contrib/gitian-descriptors/gitian-linux-parallel.yml +++ b/depend/zcash/contrib/gitian-descriptors/gitian-linux-parallel.yml @@ -1,5 +1,5 @@ --- -name: "zcash-5.4.0" +name: "zcash-5.5.0" enable_cache: true distro: "debian" suites: diff --git a/depend/zcash/contrib/gitian-descriptors/gitian-linux.yml b/depend/zcash/contrib/gitian-descriptors/gitian-linux.yml index d614acf01..368fcdf61 100644 --- a/depend/zcash/contrib/gitian-descriptors/gitian-linux.yml +++ b/depend/zcash/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-5.4.0" +name: "zcash-5.5.0" enable_cache: true distro: "debian" suites: diff --git a/depend/zcash/contrib/metrics/docker-compose.yml b/depend/zcash/contrib/metrics/docker-compose.yml new file mode 100644 index 000000000..b8984f9f7 --- /dev/null +++ b/depend/zcash/contrib/metrics/docker-compose.yml @@ -0,0 +1,52 @@ +version: "3.7" + +services: + grafana: + image: grafana/grafana + container_name: zcashd-grafana + depends_on: + - prometheus + environment: + GF_SERVER_HTTP_PORT: "3030" + ports: + - "3030:3030" + volumes: + - type: volume + source: grafana-storage + target: /var/lib/grafana + - type: bind + source: ./grafana/grafana.ini + target: /etc/grafana/grafana.ini + - type: bind + source: ./grafana/provisioning + target: /etc/grafana/provisioning + - type: bind + source: ./grafana/dashboards + target: /etc/grafana/dashboards + networks: + - zcashd-metrics + + prometheus: + image: prom/prometheus + container_name: zcashd-prometheus + ports: + - "9090:9090" + volumes: + - type: volume + source: prometheus-storage + target: /prometheus + - type: bind + source: ./prometheus.yaml + target: /etc/prometheus/prometheus.yml + read_only: true + networks: + - zcashd-metrics + extra_hosts: + - "host.docker.internal:host-gateway" + +volumes: + grafana-storage: + prometheus-storage: + +networks: + zcashd-metrics: diff --git a/depend/zcash/contrib/metrics/grafana/dashboards/zcashd-metrics.json b/depend/zcash/contrib/metrics/grafana/dashboards/zcashd-metrics.json new file mode 100644 index 000000000..bf86d172c --- /dev/null +++ b/depend/zcash/contrib/metrics/grafana/dashboards/zcashd-metrics.json @@ -0,0 +1,911 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.1.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "editorMode": "code", + "expr": "zcash_chain_verified_block_height", + "interval": "", + "legendFormat": "Chain tip", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "editorMode": "builder", + "expr": "zcashd_wallet_synced_block_height", + "hide": false, + "legendFormat": "Wallet", + "range": true, + "refId": "B" + } + ], + "title": "Block height", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 12, + "interval": "", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "zcash_chain_verified_block_seconds", + "format": "time_series", + "interval": "", + "legendFormat": "{{quantile}}", + "refId": "A" + } + ], + "title": "Block verification", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byFrameRefID", + "options": "Heap Usage" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "unit", + "value": "bytes" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "editorMode": "builder", + "expr": "zcashd_wallet_batchscanner_size_transactions", + "hide": false, + "legendFormat": "Transactions", + "range": true, + "refId": "Transactions" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "editorMode": "builder", + "expr": "avg_over_time(zcashd_wallet_batchscanner_usage_byes[$__interval])", + "hide": false, + "legendFormat": "Heap usage", + "range": true, + "refId": "Heap Usage" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "editorMode": "builder", + "expr": "rate(zcashd_wallet_batchscanner_outputs_scanned[$__rate_interval])", + "legendFormat": "{{kind}}", + "range": true, + "refId": "Output Rate" + } + ], + "title": "Wallet batch scanner", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "txs" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Size" + }, + "properties": [ + { + "id": "unit", + "value": "bytes" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Usage" + }, + "properties": [ + { + "id": "unit", + "value": "bytes" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "zcash_mempool_size_transactions", + "interval": "", + "legendFormat": "Count", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "zcash_mempool_size_bytes", + "interval": "", + "legendFormat": "Size", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "zcash_mempool_usage_bytes", + "interval": "", + "legendFormat": "Usage", + "refId": "C" + } + ], + "title": "Mempool", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "rate(zcash_net_in_bytes[5m])", + "interval": "", + "legendFormat": "{{command}}", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "rate(zcash_net_in_bytes_total[5m])", + "interval": "", + "legendFormat": "Total", + "refId": "B" + } + ], + "title": "Inbound messages", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ZEC" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "zcash_pool_value_zatoshis/100000000", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Shielded pools", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "rate(zcash_net_out_bytes[5m])", + "interval": "", + "legendFormat": "{{command}}", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "rate(zcash_net_out_bytes_total[5m])", + "interval": "", + "legendFormat": "Total", + "refId": "B" + } + ], + "title": "Outbound messages", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.1.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "T9XAoUn4k" + }, + "expr": "zcash_pool_notes_created", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Commitment tree sizes", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "zcashd metrics", + "uid": "U4U58t-Gk", + "version": 1, + "weekStart": "" +} diff --git a/depend/zcash/contrib/metrics/grafana/grafana.ini b/depend/zcash/contrib/metrics/grafana/grafana.ini new file mode 100644 index 000000000..0066b7f78 --- /dev/null +++ b/depend/zcash/contrib/metrics/grafana/grafana.ini @@ -0,0 +1,2 @@ +[paths] +provisioning = /etc/grafana/provisioning diff --git a/depend/zcash/contrib/metrics/grafana/provisioning/dashboards/zcash.yaml b/depend/zcash/contrib/metrics/grafana/provisioning/dashboards/zcash.yaml new file mode 100644 index 000000000..37546c9a8 --- /dev/null +++ b/depend/zcash/contrib/metrics/grafana/provisioning/dashboards/zcash.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: 1 + +providers: + - name: 'Zcash metrics' + # provider type. Default to 'file' + type: file + # disable dashboard deletion + disableDeletion: false + # how often Grafana will scan for changed dashboards + updateIntervalSeconds: 10 + # allow updating provisioned dashboards from the UI + allowUiUpdates: false + options: + # path to dashboard files on disk. Required when using the 'file' type + path: /etc/grafana/dashboards + # use folder names from filesystem to create folders in Grafana + foldersFromFilesStructure: true diff --git a/depend/zcash/contrib/metrics/grafana/provisioning/datasources/prometheus.yaml b/depend/zcash/contrib/metrics/grafana/provisioning/datasources/prometheus.yaml new file mode 100644 index 000000000..d9f0cc3a9 --- /dev/null +++ b/depend/zcash/contrib/metrics/grafana/provisioning/datasources/prometheus.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: 1 +datasources: + - + access: proxy + editable: false + name: Prometheus + orgId: 1 + type: prometheus + uid: T9XAoUn4k + url: "http://prometheus:9090" + version: 1 diff --git a/depend/zcash/contrib/metrics/prometheus.yaml b/depend/zcash/contrib/metrics/prometheus.yaml index 77d08e5b4..d3a286620 100644 --- a/depend/zcash/contrib/metrics/prometheus.yaml +++ b/depend/zcash/contrib/metrics/prometheus.yaml @@ -3,4 +3,4 @@ scrape_configs: scrape_interval: 500ms metrics_path: '/' static_configs: - - targets: ['127.0.0.1:9969'] + - targets: ['host.docker.internal:9969'] diff --git a/depend/zcash/depends/Makefile b/depend/zcash/depends/Makefile index 85401b502..280581671 100644 --- a/depend/zcash/depends/Makefile +++ b/depend/zcash/depends/Makefile @@ -107,7 +107,7 @@ wallet_packages_$(NO_WALLET) = $(wallet_packages) packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(wallet_packages_) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) -all_packages = $(packages) $(native_packages) +all_packages = $(native_packages) $(packages) meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk diff --git a/depend/zcash/depends/funcs.mk b/depend/zcash/depends/funcs.mk index 1ac42c62d..afc55efe7 100644 --- a/depend/zcash/depends/funcs.mk +++ b/depend/zcash/depends/funcs.mk @@ -37,7 +37,7 @@ define vendor_crate_deps ( mkdir -p $$($(1)_download_dir)/$(1) && echo Vendoring dependencies for $(1)... && \ tar -xf $(native_rust_cached) -C $$($(1)_download_dir) && \ tar --strip-components=1 -xf $$($(1)_source_dir)/$(2) -C $$($(1)_download_dir)/$(1) && \ - cp $$($(1)_download_dir)/$(1)/$(3) $$($(1)_download_dir)/$(1)/Cargo.lock && \ + cp $(3) $$($(1)_download_dir)/$(1)/Cargo.lock && \ $$($(1)_download_dir)/native/bin/cargo vendor --locked --manifest-path $$($(1)_download_dir)/$(1)/$(4) $$($(1)_download_dir)/$(CRATE_REGISTRY) && \ cd $$($(1)_download_dir) && \ find $(CRATE_REGISTRY) | sort | tar --no-recursion -czf $$($(1)_download_dir)/$(5).temp -T - && \ diff --git a/depend/zcash/depends/hosts/mingw32.mk b/depend/zcash/depends/hosts/mingw32.mk index f80061a24..cf93b7aa1 100644 --- a/depend/zcash/depends/hosts/mingw32.mk +++ b/depend/zcash/depends/hosts/mingw32.mk @@ -2,6 +2,7 @@ mingw32_CFLAGS=-pipe mingw32_CXXFLAGS=$(mingw32_CFLAGS) -isystem $(host_prefix)/include/c++/v1 mingw32_LDFLAGS?=-fuse-ld=lld +mingw32_LDFLAGS+=-L/usr/lib/gcc/x86_64-w64-mingw32/$(shell x86_64-w64-mingw32-g++-posix -dumpversion) mingw32_release_CFLAGS=-O3 mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) diff --git a/depend/zcash/depends/packages/bdb.mk b/depend/zcash/depends/packages/bdb.mk index 350816eeb..a0c0bef6c 100644 --- a/depend/zcash/depends/packages/bdb.mk +++ b/depend/zcash/depends/packages/bdb.mk @@ -42,6 +42,9 @@ define $(package)_build_cmds $(MAKE) libdb_cxx-6.2.a libdb-6.2.a endef +ifneq ($(build_os),darwin) +# Install the BDB utilities as well, so that we have the specific compatible +# versions for recovery purposes (https://github.com/zcash/zcash/issues/4537). define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install endef @@ -51,3 +54,11 @@ define $(package)_postprocess_cmds mkdir -p bin && \ mv -f $($(package)_staging_dir)$(host_prefix)/bin/db_* bin endef +else +# The BDB utilities silently fail to link on native macOS, causing the rest of +# the install to fail due to missing binaries. Until we can figure out how to +# make them work, avoid building them. +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install_lib install_include +endef +endif diff --git a/depend/zcash/depends/packages/boost.mk b/depend/zcash/depends/packages/boost.mk index 47ef946e7..5b74d7156 100644 --- a/depend/zcash/depends/packages/boost.mk +++ b/depend/zcash/depends/packages/boost.mk @@ -1,8 +1,8 @@ package=boost -$(package)_version=1_81_0 +$(package)_version=1_82_0 $(package)_download_path=https://boostorg.jfrog.io/artifactory/main/release/$(subst _,.,$($(package)_version))/source/ $(package)_file_name=boost_$($(package)_version).tar.bz2 -$(package)_sha256_hash=71feeed900fbccca04a3b4f2f84a7c217186f28a940ed8b7ed4725986baf99fa +$(package)_sha256_hash=a6e1ab9b0860e6a2881dd7b21fe9f737a095e5f33a3a874afc6a345228597ee6 $(package)_dependencies=native_b2 ifneq ($(host_os),darwin) diff --git a/depend/zcash/depends/packages/googletest.mk b/depend/zcash/depends/packages/googletest.mk index 97f2bd9b1..7d5357e89 100644 --- a/depend/zcash/depends/packages/googletest.mk +++ b/depend/zcash/depends/packages/googletest.mk @@ -1,6 +1,6 @@ package=googletest $(package)_version=1.12.1 -$(package)_download_path=https://github.com/google/$(package)/archive/ +$(package)_download_path=https://github.com/google/$(package)/archive/refs/tags $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_download_file=release-$($(package)_version).tar.gz $(package)_sha256_hash=81964fe578e9bd7c94dfdb09c8e4d6e6759e19967e397dbea48d1c10e45d0df2 diff --git a/depend/zcash/depends/packages/libcxx.mk b/depend/zcash/depends/packages/libcxx.mk index 0ee3c2aea..7c5d6b4b2 100644 --- a/depend/zcash/depends/packages/libcxx.mk +++ b/depend/zcash/depends/packages/libcxx.mk @@ -1,6 +1,6 @@ package=libcxx -$(package)_version=$(native_clang_version) -$(package)_msys2_version=14.0.6-1 +$(package)_version=$(if $(native_clang_version_$(host_arch)_$(host_os)),$(native_clang_version_$(host_arch)_$(host_os)),$(if $(native_clang_version_$(host_os)),$(native_clang_version_$(host_os)),$(native_clang_default_version))) +$(package)_msys2_version=15.0.7-3 ifneq ($(canonical_host),$(build)) ifneq ($(host_os),mingw32) @@ -9,10 +9,10 @@ ifneq ($(host_os),mingw32) $(package)_download_path=$(native_clang_download_path) $(package)_download_file_aarch64_linux=clang+llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz $(package)_file_name_aarch64_linux=clang-llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz -$(package)_sha256_hash_aarch64_linux=1a81fda984f5e607584916fdf69cf41e5385b219b983544d2c1a14950d5a65cf +$(package)_sha256_hash_aarch64_linux=8ca4d68cf103da8331ca3f35fe23d940c1b78fb7f0d4763c1c059e352f5d1bec $(package)_download_file_linux=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz $(package)_file_name_linux=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz -$(package)_sha256_hash_linux=61582215dafafb7b576ea30cc136be92c877ba1f1c31ddbbd372d6d65622fef5 +$(package)_sha256_hash_linux=38bc7f5563642e73e69ac5626724e206d6d539fbef653541b34cae0ba9c3f036 # Starting from LLVM 14.0.0, some Clang binary tarballs store libc++ in a # target-specific subdirectory. @@ -26,37 +26,17 @@ endef else # For Windows cross-compilation, use the MSYS2 binaries. +# Starting from LLVM 15.0.0, libc++abi is provided by libc++. $(package)_download_path=https://repo.msys2.org/mingw/x86_64 $(package)_download_file=mingw-w64-x86_64-libc++-$($(package)_msys2_version)-any.pkg.tar.zst $(package)_file_name=mingw-w64-x86_64-libcxx-$($(package)_msys2_version)-any.pkg.tar.zst -$(package)_sha256_hash=05f888ec1d82cb989a22ced6b85834c5d9aac46613b15334e6a2806c3d0960a4 - -$(package)_libcxxabi_download_file=mingw-w64-x86_64-libc++abi-$($(package)_msys2_version)-any.pkg.tar.zst -$(package)_libcxxabi_file_name=mingw-w64-x86_64-libcxxabi-$($(package)_msys2_version)-any.pkg.tar.zst -$(package)_libcxxabi_sha256_hash=0ca22d18cb155f6d230d7f6691c05b876316e0c381195428f063f66352689ca4 - -$(package)_extra_sources += $($(package)_libcxxabi_file_name) - -define $(package)_fetch_cmds -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_libcxxabi_download_file),$($(package)_libcxxabi_file_name),$($(package)_libcxxabi_sha256_hash)) -endef - -define $(package)_extract_cmds - mkdir -p $($(package)_extract_dir) && \ - echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - echo "$($(package)_libcxxabi_sha256_hash) $($(package)_source_dir)/$($(package)_libcxxabi_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - mkdir -p libcxxabi && \ - tar --no-same-owner --strip-components=1 -C libcxxabi -xf $($(package)_source_dir)/$($(package)_libcxxabi_file_name) && \ - tar --no-same-owner --strip-components=1 -xf $($(package)_source) -endef +$(package)_sha256_hash=8c14da21fa9622cc7450b22467452c6c933a03cee526cf8744faea3d4674035b define $(package)_stage_cmds mkdir -p $($(package)_staging_prefix_dir)/lib && \ mv include/ $($(package)_staging_prefix_dir) && \ cp lib/libc++.a $($(package)_staging_prefix_dir)/lib && \ - cp libcxxabi/lib/libc++abi.a $($(package)_staging_prefix_dir)/lib + cp lib/libc++abi.a $($(package)_staging_prefix_dir)/lib endef endif diff --git a/depend/zcash/depends/packages/libevent.mk b/depend/zcash/depends/packages/libevent.mk index 1f1c305aa..e2d1d9d34 100644 --- a/depend/zcash/depends/packages/libevent.mk +++ b/depend/zcash/depends/packages/libevent.mk @@ -1,6 +1,6 @@ package=libevent $(package)_version=2.1.12 -$(package)_download_path=https://github.com/libevent/libevent/archive/ +$(package)_download_path=https://github.com/libevent/libevent/archive/refs/tags $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_download_file=release-$($(package)_version)-stable.tar.gz $(package)_sha256_hash=7180a979aaa7000e1264da484f712d403fcf7679b1e9212c4e3d09f5c93efc24 diff --git a/depend/zcash/depends/packages/native_cctools.mk b/depend/zcash/depends/packages/native_cctools.mk index 5febaa3b9..95052ad78 100644 --- a/depend/zcash/depends/packages/native_cctools.mk +++ b/depend/zcash/depends/packages/native_cctools.mk @@ -1,7 +1,8 @@ package=native_cctools $(package)_version=55562e4073dea0fbfd0b20e0bf69ffe6390c7f97 $(package)_download_path=https://github.com/tpoechtrager/cctools-port/archive -$(package)_file_name=$($(package)_version).tar.gz +$(package)_download_file=$($(package)_version).tar.gz +$(package)_file_name=$(package)_$($(package)_version).tar.gz $(package)_sha256_hash=e51995a843533a3dac155dd0c71362dd471597a2d23f13dff194c6285362f875 $(package)_build_subdir=cctools $(package)_dependencies=native_clang @@ -10,7 +11,7 @@ $(package)_patches=ignore-otool.diff $(package)_libtapi_version=3efb201881e7a76a21e0554906cf306432539cef $(package)_libtapi_download_path=https://github.com/tpoechtrager/apple-libtapi/archive $(package)_libtapi_download_file=$($(package)_libtapi_version).tar.gz -$(package)_libtapi_file_name=$($(package)_libtapi_version).tar.gz +$(package)_libtapi_file_name=$(package)_libtapi_$($(package)_libtapi_version).tar.gz $(package)_libtapi_sha256_hash=380c1ca37cfa04a8699d0887a8d3ee1ad27f3d08baba78887c73b09485c0fbd3 $(package)_extra_sources += $($(package)_libtapi_file_name) diff --git a/depend/zcash/depends/packages/native_clang.mk b/depend/zcash/depends/packages/native_clang.mk index c431bf520..4cd197ebe 100644 --- a/depend/zcash/depends/packages/native_clang.mk +++ b/depend/zcash/depends/packages/native_clang.mk @@ -5,17 +5,29 @@ package=native_clang # - Manually fix the versions for packages that don't exist (the LLVM project # doesn't uniformly cut binaries across releases). # The Clang compiler should use the same LLVM version as the Rust compiler. -$(package)_major_version=14 -$(package)_version=14.0.6 -$(package)_download_path=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version) -$(package)_download_path_linux=https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0 -$(package)_download_file_linux=clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz +$(package)_default_major_version=15 +$(package)_default_version=15.0.6 +$(package)_version_darwin=15.0.4 +# 2023-02-16: No FreeBSD packages are available for Clang 15. +# 2023-04-07: Still the case. +$(package)_major_version_freebsd=14 +$(package)_version_freebsd=14.0.6 + +# Tolerate split LLVM versions. If an LLVM build is not available for a Tier 3 +# platform, we permit an older LLVM version to be used. This means the version +# of LLVM used in Clang and Rust will differ on these platforms, preventing LTO +# from working. +$(package)_version=$(if $($(package)_version_$(host_arch)_$(host_os)),$($(package)_version_$(host_arch)_$(host_os)),$(if $($(package)_version_$(host_os)),$($(package)_version_$(host_os)),$($(package)_default_version))) +$(package)_major_version=$(if $($(package)_major_version_$(host_arch)_$(host_os)),$($(package)_major_version_$(host_arch)_$(host_os)),$(if $($(package)_major_version_$(host_os)),$($(package)_major_version_$(host_os)),$($(package)_default_major_version))) + +$(package)_download_path_linux=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version) +$(package)_download_file_linux=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz $(package)_file_name_linux=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz -$(package)_sha256_hash_linux=61582215dafafb7b576ea30cc136be92c877ba1f1c31ddbbd372d6d65622fef5 +$(package)_sha256_hash_linux=38bc7f5563642e73e69ac5626724e206d6d539fbef653541b34cae0ba9c3f036 $(package)_download_path_darwin=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version) $(package)_download_file_darwin=clang+llvm-$($(package)_version)-x86_64-apple-darwin.tar.xz $(package)_file_name_darwin=clang-llvm-$($(package)_version)-x86_64-apple-darwin.tar.xz -$(package)_sha256_hash_darwin=e6cc6b8279661fd4452c2847cb8e55ce1e54e1faf4ab497b37c85ffdb6685e7c +$(package)_sha256_hash_darwin=4c98d891c07c8f6661b233bf6652981f28432cfdbd6f07181114195c3536544b $(package)_download_path_freebsd=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version) $(package)_download_file_freebsd=clang+llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz $(package)_file_name_freebsd=clang-llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz @@ -23,7 +35,7 @@ $(package)_sha256_hash_freebsd=b0a7b86dacb12afb8dd2ca99ea1b894d9cce84aab7711cb19 $(package)_download_path_aarch64_linux=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version) $(package)_download_file_aarch64_linux=clang+llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz $(package)_file_name_aarch64_linux=clang-llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz -$(package)_sha256_hash_aarch64_linux=1a81fda984f5e607584916fdf69cf41e5385b219b983544d2c1a14950d5a65cf +$(package)_sha256_hash_aarch64_linux=8ca4d68cf103da8331ca3f35fe23d940c1b78fb7f0d4763c1c059e352f5d1bec ifneq (,$(wildcard /etc/arch-release)) $(package)_dependencies=native_libtinfo @@ -39,6 +51,11 @@ endif define $(package)_stage_cmds mkdir -p $($(package)_staging_prefix_dir)/bin && \ + rm -r include/flang && \ + rm -r include/lldb && \ + rm lib/libflang* && \ + rm lib/libFortran* && \ + rm lib/liblldb* && \ cp bin/clang-$($(package)_major_version) $($(package)_staging_prefix_dir)/bin && \ cp bin/lld $($(package)_staging_prefix_dir)/bin && \ cp bin/llvm-ar $($(package)_staging_prefix_dir)/bin && \ diff --git a/depend/zcash/depends/packages/native_cmake.mk b/depend/zcash/depends/packages/native_cmake.mk index abcd1c141..e8bae950f 100644 --- a/depend/zcash/depends/packages/native_cmake.mk +++ b/depend/zcash/depends/packages/native_cmake.mk @@ -1,8 +1,8 @@ package=native_cmake -$(package)_version=3.25.2 +$(package)_version=3.26.3 $(package)_download_path=https://github.com/Kitware/CMake/releases/download/v$($(package)_version) $(package)_file_name=cmake-$($(package)_version).tar.gz -$(package)_sha256_hash=c026f22cb931dd532f648f087d587f07a1843c6e66a3dfca4fb0ea21944ed33c +$(package)_sha256_hash=bbd8d39217509d163cb544a40d6428ac666ddc83e22905d3e52c925781f0f659 define $(package)_set_vars $(package)_config_opts += -DCMAKE_BUILD_TYPE:STRING=Release diff --git a/depend/zcash/depends/packages/native_cxxbridge.mk b/depend/zcash/depends/packages/native_cxxbridge.mk index 836dcaad4..536d8e162 100644 --- a/depend/zcash/depends/packages/native_cxxbridge.mk +++ b/depend/zcash/depends/packages/native_cxxbridge.mk @@ -1,17 +1,27 @@ package=native_cxxbridge # The version needs to match cxx in Cargo.toml -$(package)_version=1.0.83 +$(package)_version=1.0.94 $(package)_download_path=https://github.com/dtolnay/cxx/archive/refs/tags $(package)_file_name=native_cxxbridge-$($(package)_version).tar.gz $(package)_download_file=$($(package)_version).tar.gz -$(package)_sha256_hash=e30cbd34fc8ec2ae78f4f9e546d29c6c92e6d714f30c3c150f7b8c6ea08ea971 +$(package)_sha256_hash=0c8d5c2fad6f2e09b04214007361e94b5e4d85200546eb67fd8885f72aa236f1 $(package)_build_subdir=gen/cmd $(package)_dependencies=native_rust +# This file is somewhat annoying to update, but can be done like so from the repo base: +# $ export VERSION=1.0.94 +# $ rm .cargo/config .cargo/.configured-for-offline +# $ mkdir tmp +# $ cd tmp +# $ tar xf ../depends/sources/native_cxxbridge-$VERSION.tar.gz +# $ cd cxx-$VERSION +# $ cargo check --release --package=cxxbridge-cmd --bin=cxxbridge +# $ cp Cargo.lock ../../depends/patches/native_cxxbridge/ +$(package)_patches=Cargo.lock $(package)_extra_sources=$(package)-$($(package)_version)-vendored.tar.gz define $(package)_fetch_cmds -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ -$(call vendor_crate_deps,$(package),$($(package)_file_name),third-party/Cargo.lock,Cargo.toml,$(package)-$($(package)_version)-vendored.tar.gz) +$(call fetch_file,$(1),$($(1)_download_path),$($(1)_download_file),$($(1)_file_name),$($(1)_sha256_hash)) && \ +$(call vendor_crate_deps,$(1),$($(1)_file_name),$(PATCHES_PATH)/$(1)/Cargo.lock,Cargo.toml,$(1)-$($(1)_version)-vendored.tar.gz) endef define $(package)_extract_cmds @@ -23,7 +33,7 @@ define $(package)_extract_cmds endef define $(package)_preprocess_cmds - cp third-party/Cargo.lock . && \ + cp $($(package)_patch_dir)/Cargo.lock . && \ mkdir -p .cargo && \ echo "[source.crates-io]" >.cargo/config && \ echo "replace-with = \"vendored-sources\"" >>.cargo/config && \ diff --git a/depend/zcash/depends/packages/native_rust.mk b/depend/zcash/depends/packages/native_rust.mk index 93b4e9f86..99bc5da0f 100644 --- a/depend/zcash/depends/packages/native_rust.mk +++ b/depend/zcash/depends/packages/native_rust.mk @@ -3,16 +3,16 @@ package=native_rust # ./contrib/devtools/update-rust-hashes.sh # The Rust compiler should use the same LLVM version as the Clang compiler; you # can check this with `rustc --version -v`. -$(package)_version=1.64.0 +$(package)_version=1.69.0 $(package)_download_path=https://static.rust-lang.org/dist $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_linux=a893977f238291370ab96726a74b6b9ae854dc75fbf5730954d901a93843bf9b +$(package)_sha256_hash_linux=2ca4a306047c0b8b4029c382910fcbc895badc29680e0332c9df990fd1c70d4f $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz -$(package)_sha256_hash_darwin=b6003d49fb857ff8dc105a3ccba98b851cd3e7d874005acb92284fd1113adc0d +$(package)_sha256_hash_darwin=9818dab2c3726d63dfbfde12c9273e62e484ef6d6f6e05a6431a3e089c335454 $(package)_file_name_freebsd=rust-$($(package)_version)-x86_64-unknown-freebsd.tar.gz -$(package)_sha256_hash_freebsd=f188a9a7f947d559add5aa7b5aa218d9c5177237eb9ea62109347f0f1464e3a2 +$(package)_sha256_hash_freebsd=2985d98910b4a1dd336bfc7a1ac3b18082ed917cff097b4db6f0d6602016c289 $(package)_file_name_aarch64_linux=rust-$($(package)_version)-aarch64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_aarch64_linux=7d8860572431bd4ee1b9cd0cd77cf7ff29fdd5b91ed7c92a820f872de6ced558 +$(package)_sha256_hash_aarch64_linux=88af5aa7a40c8f1b40416a1f27de8ffbe09c155d933f69d3e109c0ccee92353b # Mapping from GCC canonical hosts to Rust targets # If a mapping is not present, we assume they are identical, unless $host_os is @@ -21,15 +21,20 @@ $(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu $(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu # Mapping from Rust targets to SHA-256 hashes -$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=2b425658f84793d5bbf00ce545f410ec6454add202cce27a718d81e0233e7007 -$(package)_rust_std_sha256_hash_x86_64-apple-darwin=eb2f7c51f63973765f01efe509ccd2f26345d4bf0d77695adb4198a0899ae648 -$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=dcf87f97432adf7228e907b551d9b73f1ab16f79dc5da0724a227b7ffdaf57b4 -$(package)_rust_std_sha256_hash_x86_64-unknown-freebsd=c91edba781ba56f35f2dba56a268d41866ea9bb5f6ffb9d342635f66b836898b +$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=8f42b40c0a0658ee75ce758652c9821fac7db3fbd8d20f7fb2483ec2c57ee0ac +$(package)_rust_std_sha256_hash_x86_64-apple-darwin=e44d71250dc5a238da0dc4784dad59d562862653adecd31ea52e0920b85c6a7c +$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=09ded4a4c27c16aff9c9911640b1bdf6e1172237ce540ed4dc3e166e9438f0d7 +$(package)_rust_std_sha256_hash_x86_64-unknown-freebsd=eed4b3f3358a8887b0f6a62e021469878a8990af9b94c2fe87d3c1b0220913bb define rust_target $(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(if $(findstring freebsd,$(3)),x86_64-unknown-freebsd,$(2)))) endef +define $(package)_set_vars +$(package)_stage_opts=--disable-ldconfig +$(package)_stage_build_opts=--without=rust-docs-json-preview,rust-docs +endef + ifneq ($(canonical_host),$(build)) $(package)_rust_target=$(call rust_target,$(package),$(canonical_host),$(host_os)) $(package)_exact_file_name=rust-std-$($(package)_version)-$($(package)_rust_target).tar.gz @@ -54,12 +59,12 @@ define $(package)_extract_cmds endef define $(package)_stage_cmds - bash ./install.sh --without=rust-docs --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig && \ - ../$(canonical_host)/install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig + bash ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) $($(package)_stage_opts) $($(package)_stage_build_opts) && \ + ../$(canonical_host)/install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) $($(package)_stage_opts) endef else define $(package)_stage_cmds - bash ./install.sh --without=rust-docs --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig + bash ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) $($(package)_stage_opts) $($(package)_stage_build_opts) endef endif diff --git a/depend/zcash/depends/packages/native_zstd.mk b/depend/zcash/depends/packages/native_zstd.mk index ed7b54faa..b8844c1f8 100644 --- a/depend/zcash/depends/packages/native_zstd.mk +++ b/depend/zcash/depends/packages/native_zstd.mk @@ -1,8 +1,8 @@ package=native_zstd -$(package)_version=1.5.2 +$(package)_version=1.5.5 $(package)_download_path=https://github.com/facebook/zstd/releases/download/v$($(package)_version) $(package)_file_name=zstd-$($(package)_version).tar.gz -$(package)_sha256_hash=7c42d56fac126929a6a85dbc73ff1db2411d04f104fae9bdea51305663a83fd0 +$(package)_sha256_hash=9c4396cc829cfae319a6e2615202e82aad41372073482fce286fac78646d3ee4 $(package)_build_subdir=build/cmake $(package)_dependencies=native_cmake diff --git a/depend/zcash/depends/packages/rustcxx.mk b/depend/zcash/depends/packages/rustcxx.mk index 1373228a6..6badddfea 100644 --- a/depend/zcash/depends/packages/rustcxx.mk +++ b/depend/zcash/depends/packages/rustcxx.mk @@ -3,8 +3,8 @@ $(package)_version=$(native_cxxbridge_version) $(package)_file_name=$(native_cxxbridge_file_name) $(package)_sha256_hash=$(native_cxxbridge_sha256_hash) -# Nothing to do, this was fetched by native_cxxbridge. define $(package)_fetch_cmds + $(call native_cxxbridge_fetch_cmds,native_cxxbridge) endef define $(package)_stage_cmds diff --git a/depend/zcash/depends/packages/tl_expected.mk b/depend/zcash/depends/packages/tl_expected.mk index c9ad2e3b6..e09f38c4c 100644 --- a/depend/zcash/depends/packages/tl_expected.mk +++ b/depend/zcash/depends/packages/tl_expected.mk @@ -1,8 +1,8 @@ package=tl_expected -$(package)_version=1.0.1 +$(package)_version=96d547c03d2feab8db64c53c3744a9b4a7c8f2c5 $(package)_download_path=https://github.com/TartanLlama/expected/archive -$(package)_download_file=96d547c03d2feab8db64c53c3744a9b4a7c8f2c5.tar.gz -$(package)_file_name=tl-expected-1.0.1.tar.gz +$(package)_download_file=$($(package)_version).tar.gz +$(package)_file_name=$(package)_$($(package)_version).tar.gz $(package)_sha256_hash=64901df1de9a5a3737b331d3e1de146fa6ffb997017368b322c08f45c51b90a7 $(package)_patches=remove-undefined-behaviour.diff diff --git a/depend/zcash/depends/packages/utfcpp.mk b/depend/zcash/depends/packages/utfcpp.mk index 21201e54a..1bce168f0 100644 --- a/depend/zcash/depends/packages/utfcpp.mk +++ b/depend/zcash/depends/packages/utfcpp.mk @@ -1,6 +1,6 @@ package=utfcpp $(package)_version=3.2.3 -$(package)_download_path=https://github.com/nemtrif/$(package)/archive/ +$(package)_download_path=https://github.com/nemtrif/$(package)/archive/refs/tags $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_download_file=v$($(package)_version).tar.gz $(package)_sha256_hash=3ba9b0dbbff08767bdffe8f03b10e596ca351228862722e4c9d4d126d2865250 diff --git a/depend/zcash/depends/patches/native_cxxbridge/Cargo.lock b/depend/zcash/depends/patches/native_cxxbridge/Cargo.lock new file mode 100644 index 000000000..30294c074 --- /dev/null +++ b/depend/zcash/depends/patches/native_cxxbridge/Cargo.lock @@ -0,0 +1,416 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "basic-toml" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-ast" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f01a8823bc658ffd6c6cc1931b0283e7daacf2299fe79faab307cc08c65c98fe" +dependencies = [ + "serde", +] + +[[package]] +name = "clap" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" +dependencies = [ + "bitflags", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cxx" +version = "1.0.94" +dependencies = [ + "cc", + "cxx-build", + "cxx-gen", + "cxx-test-suite", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", + "rustversion", + "trybuild", +] + +[[package]] +name = "cxx-build" +version = "1.0.94" +dependencies = [ + "cc", + "codespan-reporting", + "cxx", + "cxx-gen", + "once_cell", + "pkg-config", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxx-gen" +version = "0.7.94" +dependencies = [ + "codespan-reporting", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxx-test-suite" +version = "0.0.0" +dependencies = [ + "cxx", + "cxx-build", + "cxxbridge-flags", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.94" +dependencies = [ + "clap", + "codespan-reporting", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.94" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.94" +dependencies = [ + "clang-ast", + "cxx", + "flate2", + "memmap", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "demo" +version = "0.0.0" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "dissimilar" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210ec60ae7d710bed8683e333e9d2855a8a56a3e9892b38bad3bb0d4d29b0d5e" + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "trybuild" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501dbdbb99861e4ab6b60eb6a7493956a9defb644fd034bc4a5ef27c693c8a3a" +dependencies = [ + "basic-toml", + "dissimilar", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/depend/zcash/doc/authors.md b/depend/zcash/doc/authors.md index 8d81748cb..78d5e6ecf 100644 --- a/depend/zcash/doc/authors.md +++ b/depend/zcash/doc/authors.md @@ -1,52 +1,54 @@ Zcash Contributors ================== -Jack Grigg (1296) -Kris Nuttycombe (618) +Jack Grigg (1301) +Kris Nuttycombe (659) Simon Liu (460) -Sean Bowe (389) -Daira Hopwood (376) +Daira Hopwood (392) +Sean Bowe (390) Eirik Ogilvie-Wigley (216) -Wladimir J. van der Laan (159) -Pieter Wuille (143) +Greg Pfeil (174) +Wladimir J. van der Laan (160) +Pieter Wuille (149) Alfredo Garcia (120) Taylor Hornby (118) Marshall Gaucher (118) +Marco Falke (91) Ying Tong Lai (90) -Marco Falke (90) Jonas Schnelli (90) Jay Graber (89) Larry Ruane (88) -Greg Pfeil (83) +sasha (80) Cory Fields (78) -sasha (62) -Matt Corallo (60) +Matt Corallo (62) Nathan Wilcox (57) +Dimitris Apostolou (43) practicalswift (42) -Dimitris Apostolou (40) Kevin Gallagher (38) +Daira Emma Hopwood (38) fanquake (36) Carl Dong (30) -Luke Dashjr (26) +Alex Morcos (28) +Luke Dashjr (27) Gregory Maxwell (24) John Newbery (23) Jorge Timón (22) +Suhas Daftuar (20) furszy (18) Jonathan "Duke" Leto (18) +Marius Kjærstad (17) syd (16) -Suhas Daftuar (16) Patick Strateman (16) -Marius Kjærstad (15) -Charlie O'Keefe (15) +Charlie O'Keefe (16) avnish (14) Per Grön (14) -Alex Morcos (14) Benjamin Winston (13) Steven Smith (12) Pavel Janík (12) Patrick Strateman (12) Jeremy Rubin (12) Ariel Gabizon (12) +teor (11) Russell Yanofsky (11) Paige Peterson (11) Kaz Wesley (11) @@ -54,7 +56,6 @@ João Barbosa (11) Philip Kaufmann (10) Peter Todd (10) ying tong (9) -teor (9) nomnombtc (9) Zancas Wilcox (9) kozyilmaz (8) @@ -71,18 +72,20 @@ Daniel Cousens (6) Casey Rodarmor (6) jnewbery (5) ca333 (5) -Sasha (5) MeshCollider (5) Johnathan Corgan (5) George Tankersley (5) Gavin Andresen (5) Gareth Davies (5) sandakersmann (4) +instagibbs (4) gladcow (4) WO (4) Sjors Provoost (4) Nate Wilcox (4) +Miodrag Popović (4) Jim Posen (4) +Evan Klitzke (4) Ben Woosley (4) mruddy (3) lpescher (3) @@ -93,21 +96,21 @@ NikVolf (3) Martin Ankerl (3) Julian Fleischer (3) Jason Davies (3) -Evan Klitzke (3) Ethan Heilman (3) Eric Lombrozo (3) +DeckerSU (3) Danny Willems (3) Conrado Gouvea (3) Anthony Towns (3) Alfie John (3) Aditya Kulkarni (3) +ANISH M (3) whythat (2) rofl0r (2) ptschip (2) noname45688@gmail.com (2) kpcyrd (2) kobake (2) -instagibbs (2) hexabot (2) face (2) aniemerg (2) @@ -123,17 +126,16 @@ Pejvan (2) Pavol Rusnak (2) Pavel Vasin (2) Mustafa (2) -Miodrag Popović (2) Matthew King (2) Mary Moore-Simmons (2) Mark Friedenbach (2) Marek (2) +Jon Atack (2) Joe Turgeon (2) Jesse Cohen (2) Jeffrey Czyz (2) Jack Gavigan (2) ITH4Coinomia (2) -DeckerSU (2) Dan Raviv (2) Dagur Valberg Johannsson (2) Bryant Eisenbach (2) @@ -168,16 +170,19 @@ emilrus (1) dexX7 (1) dependabot[bot] (1) daniel (1) +cronicc (1) codetriage-readme-bot (1) calebogden (1) ayleph (1) avnish98 (1) adityapk00 (1) Za Wilcox (1) +Yasser Isa (1) William M Peaster (1) Vidar Holen (1) Vasil Dimov (1) Ulrich Kempken (1) +TrellixVulnTeam (1) Tom Ritter (1) Tom Harding (1) Technetium (1) diff --git a/depend/zcash/doc/book/src/SUMMARY.md b/depend/zcash/doc/book/src/SUMMARY.md index a80d31a8b..60ff3c665 100644 --- a/depend/zcash/doc/book/src/SUMMARY.md +++ b/depend/zcash/doc/book/src/SUMMARY.md @@ -2,6 +2,7 @@ [zcashd](README.md) - [User Documentation](user.md) + - [Release Support](user/release-support.md) - [Platform Support](user/platform-support.md) - [Wallet Backup](user/wallet-backup.md) - [Shielding Coinbase Outputs](user/shield-coinbase.md) diff --git a/depend/zcash/doc/book/src/dev/rust.md b/depend/zcash/doc/book/src/dev/rust.md index f4bad9be5..fe5616744 100644 --- a/depend/zcash/doc/book/src/dev/rust.md +++ b/depend/zcash/doc/book/src/dev/rust.md @@ -64,6 +64,40 @@ To add dependencies that are compatible with the reproducible build system, you ./zcutil/build.sh ``` +## Using an unpublished Rust dependency + +Occasionally we may need to depend on an unpublished git revision of a crate. +We sometimes want to prove out API changes to the `zcash_*` Rust crates by +migrating `zcashd` to them first, before making a public crate release. Or we +might need to cut a `zcashd` release before some upstream dependency has +published a fix we need. In these cases, we use patch dependencies. + +For example, to use an unpublished version of the `orchard` crate that includes +a new API, add the following patch to `Cargo.toml`: + +``` +[dependencies] +# This dependency is listed with a version, meaning it comes from crates.io; the +# patch goes into a [patch.crates-io] section. +orchard = "0.4" +... + +[patch.crates-io] +orchard = { git = "https://github.com/zcash/orchard.git", rev = "..." } +``` + +Note that if the git repository contains a workspace of interconnected crates +(for example, https://github.com/zcash/librustzcash), you will need to provide +patches for each of the dependencies that reference the same git revision. + +You also need to update `.cargo/config.offline` to add a replacement definition +for each `(git, rev)` pair. Run `./test/lint/lint-cargo-patches.sh` to get the +lines that need to be present. + +Finally, `./qa/supply-chain/config.toml` needs to be updated to ignore patched +dependencies. Run `cargo vet regenerate audit-as-crates-io`, and then ensure the +newly-added lines are of the form `audit-as-crates-io = false`. + ## Using a local Rust dependency During development, you can use a locally checked out version of a dependency diff --git a/depend/zcash/doc/book/src/user/deprecation.md b/depend/zcash/doc/book/src/user/deprecation.md index ea177228c..07f0b17fa 100644 --- a/depend/zcash/doc/book/src/user/deprecation.md +++ b/depend/zcash/doc/book/src/user/deprecation.md @@ -35,11 +35,12 @@ The following features are deprecated, but remain enabled by default. These feat will be disabled if `-allowdeprecated=none` is added to the CLI arguments when starting the node, or if an `allowdeprecated=none` line is added to `zcash.conf`. -| `feature` | Deprecated | Feature details -|-----------------------|------------|---------------- -| `z_getbalance` | 5.0.0 | The `z_getbalance` RPC method. -| `z_gettotalbalance` | 5.0.0 | The `z_gettotalbalance` RPC method. -| `gbt_oldhashes` | 5.4.0 | The `finalsaplingroothash`, `lightclientroothash`, and `blockcommitmentshash` fields in the output of `getblocktemplate`, which are replaced by the `defaultroots` field. +| `feature` | Deprecated | Feature details +|-------------------------------------|------------|---------------- +| `z_getbalance` | 5.0.0 | The `z_getbalance` RPC method. +| `z_gettotalbalance` | 5.0.0 | The `z_gettotalbalance` RPC method. +| `gbt_oldhashes` | 5.4.0 | The `finalsaplingroothash`, `lightclientroothash`, and `blockcommitmentshash` fields in the output of `getblocktemplate`, which are replaced by the `defaultroots` field. +| `deprecationinfo_deprecationheight` | 5.5.0 | The `deprecationheight` field returned by the `getdeprecationinfo` RPC method has been replaced by the `end_of_service` object. Stage 2 ------- diff --git a/depend/zcash/doc/book/src/user/metrics.md b/depend/zcash/doc/book/src/user/metrics.md index 3775b0f4f..d0dc9d7e9 100644 --- a/depend/zcash/doc/book/src/user/metrics.md +++ b/depend/zcash/doc/book/src/user/metrics.md @@ -54,14 +54,66 @@ zcash_chain_verified_block_total 162 ``` By default, access is restricted to localhost. This can be expanded with -`-metricsallowip=`, which can specify IPs or subnets. Note that HTTPS is not -supported, and therefore connections to the endpoint are not encrypted or +`-metricsallowip=`, which can specify IPs or subnets. Note that HTTPS is +not supported, and therefore connections to the endpoint are not encrypted or authenticated. Access to the endpoint should be assumed to compromise the privacy of node operations, by the provided metrics and/or by timing side channels. Non-localhost access is **strongly discouraged** if the node has a wallet holding live funds. -### Example metrics collection with Docker +### Metrics collection with Docker + +A docker-compose.yml has been provided in `./contrib/metrics` that sets up +local instances of `prometheus` and `grafana` and provides a dashboard that +charts several of the various metrics exposed by `zcashd`'s `prometheus` +endpoint. Note that both `docker` and `docker-compose` must ordinarily be run +with superuser permissions (use `sudo`) when running on Linux. + +`docker-compose up`[^1] will start local instances of `prometheus` and `grafana`, +accessible over HTTP on ports `9090` and `3030`, respectively. + +``` +cd /contrib/metrics +docker-compose up -d +``` +(substitute the root directory where you have checked out the `zcash` git +repository for ``) + +`~/.zcash/zcash.conf` must be updated to enable `prometheus` and to allow the +`prometheus` server launched via `docker-compose` to connect to the `zcashd` +prometheus endpoint. The following commands can be used to detect the local IP +address for the `prometheus` server and add it to the `~/.zcash/zcash.conf` +file. + +First, figure out where `prometheus` is running. + +``` +export PROMETHEUS_DOCKER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' zcashd-prometheus) +``` + +Then, update your `~/.zcash/zcash.conf` file to open port `9969` and allow +connections from the `zcashd-prometheus` docker container. + +``` +cat << PROM_CONF >> ~/.zcash/zcash.conf +prometheusport=9969 +metricsallowip=$PROMETHEUS_DOCKER_IP/32 +PROM_CONF +``` + +You may then (re)start `zcashd` and navigate to +[http://localhost:9090/targets?search=](http://localhost:9090/targets?search=) +to verify that the `prometheus` server is able to connect to the `zcashd` +prometheus endpoint; you should see the host +`http://host.docker.internal:9969/` having `UP` status. Once this is working, +navigate to +[http://localhost:3030/d/U4U58t-Gk/zcashd-metrics](http://localhost:3030/d/U4U58t-Gk/zcashd-metrics) +to view the dashboard. The username and password for this local grafana +instance are set by default to `admin`/`admin`; the UI will ask you to change +this on first use. Data gathered by the running prometheus instance and changes +that you make to the grafana interface will be persisted across restarts. + +#### Manual Docker Setup Without docker-compose The example instructions below were tested on Windows 10 using Docker Desktop with the WSL 2 backend, connected to a `zcashd` running inside WSL2 (but not @@ -75,11 +127,15 @@ docker volume create grafana-storage docker volume create prometheus-storage # Run Prometheus -# You will need to modify ~/contrib/metrics/prometheus.yaml to match the +# You will need to modify $(zcash_root)/contrib/metrics/prometheus.yaml to match the # port configured with -prometheusport and -metricsbind / -metricsallowip # (and possibly also for your Docker network setup). -docker run --detach -p 9090:9090 --volume prometheus-storage:/prometheus --volume ~/contrib/metrics/prometheus.yaml:/etc/prometheus/prometheus.yml prom/prometheus +docker run --detach -p 9090:9090 --volume prometheus-storage:/prometheus --volume $(zcash_root)/contrib/metrics/prometheus.yaml:/etc/prometheus/prometheus.yml prom/prometheus # Run Grafana docker run --detach -p 3030:3030 --env GF_SERVER_HTTP_PORT=3030 --volume grafana-storage:/var/lib/grafana grafana/grafana ``` + +[^1]: This requires a running Docker daemon. See [the relevant section of the + Docker Engine manual](https://docs.docker.com/config/daemon/start/). + diff --git a/depend/zcash/doc/book/src/user/release-support.md b/depend/zcash/doc/book/src/user/release-support.md new file mode 100644 index 000000000..02b961bcc --- /dev/null +++ b/depend/zcash/doc/book/src/user/release-support.md @@ -0,0 +1,53 @@ +# `zcashd` Release Support + +## Release cadence and support window + +`zcashd` releases happen approximately every six weeks, although this may change if a +particular release is delayed, or if a hotfix release occurs. + +Each `zcashd` release is generally supported for 16 weeks. Occasionally this changes for +individual releases (for example, near to a Network Upgrade activation). + +These two policies mean that there are generally at least two separate `zcashd` versions +currently supported at any given time. + +## End-of-Support halt + +Every `zcashd` version released by ECC has an End-of-Support height. When the Zcash chain +reaches this height, `zcashd` will automatically shut down, and the binary will refuse to +restart. This is for several reasons: + +- The `zcashd` maintainers do not have the resources to maintain old versions of `zcashd` + indefinitely. +- Each time a user upgrades their `zcashd` node, they are re-confirming that they are + happy to run the Zcash consensus rules encoded in the version of `zcashd` they are + running. This is an important part of the overall strategy for changes to the node and + consensus rules; users who want to follow different rules (or even just have a different + End-of-Support halt policy) will obtain a `zcashd` binary from some other source, with + its own support policies. +- Knowing that old versions will shut down is useful for planning backwards-incompatible + changes in Network Upgrades. A Network Upgrade activation can be targeted for a height + where we know that all released `zcashd` versions which _did not_ support the Network + Upgrade will have shut down by the time the Network Upgrade activates. + +## End-of-Support heights + +The End-of-Support height for a running `zcashd` can be queried over JSON-RPC using the +`getdeprecationinfo` method. + +The following table shows End-of-Support information for recent `zcashd` releases. It is +automatically updated during each release. "End of Support" dates are estimated at that +time, and may shift due to changes in network solution power. + + +| `zcashd` version | Release date | Halt height | End of Support | +| ---------------- | ------------ | ----------- | -------------- | +| 5.4.0 | 2023-02-09 | 2106524 | 2023-06-01 | +| 5.4.1 | 2023-02-13 | 2112024 | 2023-06-05 | +| 5.3.3 | 2023-03-13 | 2121024 | 2023-06-13 | +| 5.4.2 | 2023-03-13 | 2121024 | 2023-06-13 | +| 5.5.0-rc1 | 2023-04-20 | 2188024 | 2023-08-10 | +| 5.5.0-rc2 | 2023-04-25 | 2193300 | 2023-08-15 | +| 5.5.0-rc3 | 2023-04-27 | 2195224 | 2023-08-17 | +| 5.5.0 | 2023-04-27 | 2196024 | 2023-08-17 | + diff --git a/depend/zcash/doc/man/zcash-cli.1 b/depend/zcash/doc/man/zcash-cli.1 index f570e8897..75815c8f2 100644 --- a/depend/zcash/doc/man/zcash-cli.1 +++ b/depend/zcash/doc/man/zcash-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1. -.TH ZCASH-CLI "1" "February 2023" "zcash-cli v5.4.0" "User Commands" +.TH ZCASH-CLI "1" "April 2023" "zcash-cli v5.5.0" "User Commands" .SH NAME -zcash-cli \- manual page for zcash-cli v5.4.0 +zcash-cli \- manual page for zcash-cli v5.5.0 .SH DESCRIPTION -Zcash RPC client version v5.4.0 +Zcash RPC client version v5.5.0 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . diff --git a/depend/zcash/doc/man/zcash-tx.1 b/depend/zcash/doc/man/zcash-tx.1 index 2510a22df..2d238e6cc 100644 --- a/depend/zcash/doc/man/zcash-tx.1 +++ b/depend/zcash/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1. -.TH ZCASH-TX "1" "February 2023" "zcash-tx v5.4.0" "User Commands" +.TH ZCASH-TX "1" "April 2023" "zcash-tx v5.5.0" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v5.4.0 +zcash-tx \- manual page for zcash-tx v5.5.0 .SH DESCRIPTION -Zcash zcash\-tx utility version v5.4.0 +Zcash zcash\-tx utility version v5.5.0 .SS "Usage:" .TP zcash\-tx [options] [commands] diff --git a/depend/zcash/doc/man/zcashd-wallet-tool.1 b/depend/zcash/doc/man/zcashd-wallet-tool.1 index 83713689b..674602617 100644 --- a/depend/zcash/doc/man/zcashd-wallet-tool.1 +++ b/depend/zcash/doc/man/zcashd-wallet-tool.1 @@ -1,7 +1,7 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1. -.TH ZCASHD-WALLET-TOOL "1" "February 2023" "zcashd-wallet-tool v5.4.0" "User Commands" +.TH ZCASHD-WALLET-TOOL "1" "April 2023" "zcashd-wallet-tool v5.5.0" "User Commands" .SH NAME -zcashd-wallet-tool \- manual page for zcashd-wallet-tool v5.4.0 +zcashd-wallet-tool \- manual page for zcashd-wallet-tool v5.5.0 .SH SYNOPSIS .B zcashd-wallet-tool [\fI\,OPTIONS\/\fR] diff --git a/depend/zcash/doc/man/zcashd.1 b/depend/zcash/doc/man/zcashd.1 index ee273ce8f..1f032c443 100644 --- a/depend/zcash/doc/man/zcashd.1 +++ b/depend/zcash/doc/man/zcashd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1. -.TH ZCASHD "1" "February 2023" "zcashd v5.4.0" "User Commands" +.TH ZCASHD "1" "April 2023" "zcashd v5.5.0" "User Commands" .SH NAME -zcashd \- manual page for zcashd v5.4.0 +zcashd \- manual page for zcashd v5.5.0 .SH DESCRIPTION -Zcash Daemon version v5.4.0 +Zcash Daemon version v5.5.0 .PP In order to ensure you are adequately protecting your privacy when using Zcash, please see . @@ -34,10 +34,10 @@ long fork (%s in cmd is replaced by message) .IP Explicitly allow the use of the specified deprecated feature. Multiple instances of this parameter are permitted; values for must be -selected from among {"none", "gbt_oldhashes", "z_getbalance", -"z_gettotalbalance", "addrtype", "getnewaddress", "getrawchangeaddress", -"legacy_privacy", "wallettxvjoinsplit", "z_getnewaddress", -"z_listaddresses"} +selected from among {"none", "deprecationinfo_deprecationheight", +"gbt_oldhashes", "z_getbalance", "z_gettotalbalance", "addrtype", +"getnewaddress", "getrawchangeaddress", "legacy_privacy", +"wallettxvjoinsplit", "z_getnewaddress", "z_listaddresses"} .HP \fB\-blocknotify=\fR .IP @@ -309,11 +309,6 @@ Enable the Sprout to Sapling migration .IP Set the Sapling migration address .HP -\fB\-mintxfee=\fR -.IP -Fees (in ZEC/kB) smaller than this are considered zero fee for -transaction creation (default: 0.00001) -.HP \fB\-orchardactionlimit=\fR .IP Set the maximum number of Orchard actions permitted in a transaction @@ -321,7 +316,11 @@ Set the maximum number of Orchard actions permitted in a transaction .HP \fB\-paytxfee=\fR .IP -Fee (in ZEC/kB) to add to transactions you send (default: 0.00) +The preferred fee rate (in ZEC per 1000 bytes) used for transactions +created by legacy APIs (sendtoaddress, sendmany, and +fundrawtransaction). If the transaction is less than 1000 bytes then the +fee rate is applied as though it were 1000 bytes. When this option is +not set, the ZIP 317 fee calculation is used. .HP \fB\-rescan\fR .IP @@ -332,19 +331,10 @@ Rescan the block chain for missing wallet transactions on startup Attempt to recover private keys from a corrupt wallet on startup (implies \fB\-rescan\fR) .HP -\fB\-sendfreetransactions\fR -.IP -Send transactions as zero\-fee transactions if possible (default: 0) -.HP \fB\-spendzeroconfchange\fR .IP Spend unconfirmed change when sending transactions (default: 1) .HP -\fB\-txconfirmtarget=\fR -.IP -If paytxfee is not set, include enough fee so transactions begin -confirmation on average within n blocks (default: 2) -.HP \fB\-txexpirydelta\fR .IP Set the number of blocks after which a transaction that has not been @@ -460,8 +450,9 @@ Prepend debug output with timestamp (default: 1) .HP \fB\-minrelaytxfee=\fR .IP -Fees (in ZEC/kB) smaller than this are considered zero fee for relaying, -mining and transaction creation (default: 0.000001) +Transactions must have at least this fee rate (in ZEC per 1000 bytes) +for relaying, mining and transaction creation (default: 0.000001). This +is not the only fee constraint. .HP \fB\-maxtxfee=\fR .IP @@ -492,18 +483,14 @@ Maximum size of data in data carrier transactions we relay and mine .PP Block creation options: .HP -\fB\-blockminsize=\fR -.IP -Set minimum block size in bytes (default: 0) -.HP \fB\-blockmaxsize=\fR .IP Set maximum block size in bytes (default: 2000000) .HP -\fB\-blockprioritysize=\fR +\fB\-blockunpaidactionlimit=\fR .IP -Set maximum size of high\-priority/low\-fee transactions in bytes -(default: 1000000) +Set the limit on unpaid actions that will be accepted in a block for +transactions paying less than the ZIP 317 fee (default: 50) .PP Mining options: .HP diff --git a/depend/zcash/doc/release-notes/release-notes-5.3.3.md b/depend/zcash/doc/release-notes/release-notes-5.3.3.md new file mode 100644 index 000000000..51053c239 --- /dev/null +++ b/depend/zcash/doc/release-notes/release-notes-5.3.3.md @@ -0,0 +1,36 @@ +Notable changes +=============== + +This hotfix remediates memory exhaustion vulnerabilities that zcashd inherited +as a fork of bitcoind. These bugs could allow an attacker to use peer-to-peer +messages to fill the memory of a node, resulting in a crash. + + +Changelog +========= + +Daira Hopwood (3): + Enable a CRollingBloomFilter to be reset to a state where it takes little memory. + Ensure that CNode::{addrKnown, filterInventoryKnown} immediately take little memory when we disconnect the node. + Improve the encapsulation of `CNode::filterInventoryKnown`. + +Greg Pfeil (1): + Remove `ResetRequestCount` + +Jon Atack (1): + p2p, rpc, test: address rate-limiting follow-ups + +Kris Nuttycombe (4): + Update release notes for v5.3.3 hotfix + Postpone dependency updates. + make-release.py: Versioning changes for 5.3.3. + make-release.py: Updated manpages for 5.3.3. + +Matt Corallo (1): + Remove useless mapRequest tracking that just effects Qt display. + +Pieter Wuille (3): + Rate limit the processing of incoming addr messages + Randomize the order of addr processing + Add logging and addr rate limiting statistics + diff --git a/depend/zcash/doc/release-notes/release-notes-5.4.1.md b/depend/zcash/doc/release-notes/release-notes-5.4.1.md new file mode 100644 index 000000000..d3f2c675c --- /dev/null +++ b/depend/zcash/doc/release-notes/release-notes-5.4.1.md @@ -0,0 +1,46 @@ +Notable changes +=============== + +`allowdeprecated` in `zcash.conf` +--------------------------------- + +In v5.0.0 a [feature deprecation framework](https://zcash.github.io/zcash/user/deprecation.html) +was released, to enable `zcashd` features to be formally deprecated and removed: + +- In stage 1, a feature is marked as deprecated, but otherwise left as-is. It + remains in this stage for at least 18 weeks. +- In stage 2, the feature is default-disabled, but can be re-enabled with the + `-allowdeprecated` config option. It remains in this stage for at least 18 + weeks. +- Finally, the feature is removed - either entirely, or (e.g. in the case of RPC + methods that were inherited from Bitcoin Core) with a "tombstone" left to + inform users of the removal (and what to use instead if applicable). + +Config options can be specified either as a `zcashd` argument (`-option=value`) +or in `zcash.conf` (as a `option=value` line). However, due to a bug in the +implementation, `allowdeprecated=feature` lines in `zcash.conf` were ignored. +The bug went unnoticed until v5.4.0, in which the first group of features moved +from stage 1 to stage 2. This hotfix release fixes the bug. + +Fixed RPC blocking and wallet view lag on reindex +------------------------------------------------- + +The known issue reported in the v5.4.0 release notes has been fixed. + + +Changelog +========= + +Jack Grigg (8): + Sleep for 200us before each ActivateBestChainStep call + Load `-allowdeprecated` settings after reading the config file + qa: Refactor `wallet_deprecation` test to extract common logic + qa: Extend `wallet_deprecation` to test `allowdeprecated` in config file + Write release notes for v5.4.1 + Postpone dependency updates for v5.4.1 + make-release.py: Versioning changes for 5.4.1. + make-release.py: Updated manpages for 5.4.1. + +Jack Grigg (1): + Adjust documentation of 200µs sleep + diff --git a/depend/zcash/doc/release-notes/release-notes-5.4.2.md b/depend/zcash/doc/release-notes/release-notes-5.4.2.md new file mode 100644 index 000000000..0deb12c8a --- /dev/null +++ b/depend/zcash/doc/release-notes/release-notes-5.4.2.md @@ -0,0 +1,36 @@ +Notable changes +=============== + +This hotfix remediates memory exhaustion vulnerabilities that zcashd inherited +as a fork of bitcoind. These bugs could allow an attacker to use peer-to-peer +messages to fill the memory of a node, resulting in a crash. + + +Changelog +========= + +Daira Hopwood (3): + Enable a CRollingBloomFilter to be reset to a state where it takes little memory. + Ensure that CNode::{addrKnown, filterInventoryKnown} immediately take little memory when we disconnect the node. + Improve the encapsulation of `CNode::filterInventoryKnown`. + +Greg Pfeil (1): + Remove `ResetRequestCount` + +Jon Atack (1): + p2p, rpc, test: address rate-limiting follow-ups + +Kris Nuttycombe (4): + Update release notes for v5.3.3 hotfix + Postpone dependency updates for v5.4.2 hotfix. + make-release.py: Versioning changes for 5.4.2. + make-release.py: Updated manpages for 5.4.2. + +Matt Corallo (1): + Remove useless mapRequest tracking that just effects Qt display. + +Pieter Wuille (3): + Rate limit the processing of incoming addr messages + Randomize the order of addr processing + Add logging and addr rate limiting statistics + diff --git a/depend/zcash/doc/release-notes/release-notes-5.5.0-rc1.md b/depend/zcash/doc/release-notes/release-notes-5.5.0-rc1.md new file mode 100644 index 000000000..bff4a9d7b --- /dev/null +++ b/depend/zcash/doc/release-notes/release-notes-5.5.0-rc1.md @@ -0,0 +1,407 @@ +Notable changes +=============== + +RPC Changes +----------- + +- `getdeprecationinfo` has several changes: + - It now returns additional information about currently deprecated and + disabled features. + - A new `end_of_service` object that contains both the block height for + end-of-service and the estimated time that the end-of-service halt is + expected to occur. Note that this height is just an approximation and + will change over time as the end-of-service block height approaches, + due to the variability in block times. The + `end_of_service` object is intended to replace the `deprecationheight` + field; see the [Deprecations](#deprecations) section for additional detail. + - This RPC method was previously only available for mainnet nodes; it is now + also available for testnet and regtest nodes, in which case it does not + return end-of-service halt information (as testnet and regtest nodes do not + have an end-of-service halt feature.) +- Several `z_sendmany`, `z_shieldcoinbase` and `z_mergetoaddress` failures have + moved from synchronous to asynchronous, so while you should already be + checking the async operation status, there are now more cases that may trigger + failure at that stage. +- The `AllowRevealedRecipients` privacy policy is now required in order to choose a + transparent change address for a transaction. This will only occur when the wallet + is unable to construct the transaction without selecting funds from the transparent + pool, so the impact of this change is that for such transactions, the user must specify + `AllowFullyTransparent`. +- The `z_shieldcoinbase` and `z_mergetoaddress` RPC methods now support an + optional privacy policy. +- The `estimatepriority` RPC call has been removed. +- The `priority_delta` argument to the `prioritisetransaction` RPC call now has + no effect and must be set to a dummy value (0 or null). + +Changes to Transaction Fee Selection +------------------------------------ + +- The `-mintxfee` and `-sendfreetransactions` options have been removed. These + options used to instruct the wallet's legacy transaction creation APIs + (`sendtoaddress`, `sendmany`, and `fundrawtransaction`) to increase fees to + this limit and to use a zero fee for "small" transactions that spend "old" + inputs, respectively. They will now cause a warning on node startup if used. + +Changes to Block Template Construction +-------------------------------------- + +We now use a new block template construction algorithm documented in +[ZIP 317](https://zips.z.cash/zip-0317#recommended-algorithm-for-block-template-construction). + +- This algorithm no longer favours transactions that were previously considered + "high priority" because they spent older inputs. The `-blockprioritysize` config + option, which configured the portion of the block reserved for these transactions, + has been removed and will now cause a warning if used. +- The `-blockminsize` option, which configured the size of a portion of the block + to be filled regardless of transaction fees or priority, has also been removed + and will cause a warning if used. +- A `-blockunpaidactionlimit` option has been added to control the limit on + "unpaid actions" that will be accepted in a block for transactions paying less + than the ZIP 317 fee. This defaults to 50. + +Removal of Priority Estimation +------------------------------ + +- Estimation of "priority" needed for a transaction to be included within a target + number of blocks, and the associated `estimatepriority` RPC call, have been + removed. The format for `fee_estimates.dat` has also changed to no longer save + these priority estimates. It will automatically be converted to the new format + which is not readable by prior versions of the software. The `-txconfirmtarget` + config option is now obsolete and has also been removed. It will cause a + warning if used. + +[Deprecations](https://zcash.github.io/zcash/user/deprecation.html) +-------------- + +The following features have been deprecated, but remain available by default. +These features may be disabled by setting `-allowdeprecated=none`. 18 weeks +after this release, these features will be disabled by default and the following +flags to `-allowdeprecated` will be required to permit their continued use: + +- `deprecationinfo_deprecationheight`: The `deprecationheight` field of + `getdeprecationinfo` has been deprecated and replaced by the `end_of_service` + object. + +Changelog +========= + +ANISH M (3): + use SOURCES_PATH instead of local git DEPENDS_SOURCES_DIR + report the git-derived version in metrics screen + Update src/metrics.cpp by removing v prefix. + +Alex Morcos (14): + Refactor CreateNewBlock to be a method of the BlockAssembler class + FIX: Account for txs already added to block in addPriorityTxs + FIX: correctly measure size of priority block + [rpc] Remove estimatepriority. + [mining] Remove -blockprioritysize. + [debug] Change -printpriority option + [cleanup] Remove estimatePriority + [rpc] sendrawtransaction no longer bypasses minRelayTxFee + [test] Remove priority from tests + [rpc] Remove priority information from mempool RPC calls + [rpc] Remove priorityDelta from prioritisetransaction + [cleanup] Remove coin age priority completely. + Allow setting minrelaytxfee to 0 + Update example zcash.conf + +Daira Emma Hopwood (34): + Remove unnecessary #include. + Adjust indentation to be consistent without changing existing code. + Repair show_help RPC test. + Fix bit-rotted code in miner tests. + Implement ZIP 317 computations. + `cargo update` + Add audits for updates to futures-* 0.3.28 and redjubjub 0.7.0. + Add `examine`, a wrapper around `std::visit` that reverses the arguments. + Use the new `examine` macro to replace all instances of `std::visit(match {...}, specimen)`, improving code readability. + Refactoring to avoid duplicated code. + Refactoring to avoid an unnecessary temporary. + Refactor that avoids using exceptions for local flow control and is simpler. + Correct the documentation of `-rpcconnect` in the example `zcash.conf`. `-rpcconnect` is only used by `zcash-cli`. + Change ZIP 401 mempool limiting to use conventional fee. + Change ZIP 401 mempool limiting to use constants decided in zcash/zips#565. + Warn on node startup if the removed `-blockprioritysize` option is set to a non-zero value. + Log (at the mempool DEBUG level) when a transaction cannot be accepted to the mempool because its modified fee is below the minimum relay fee. + Fix the dust threshold rate to three times 100 zats/1000 bytes. (We express it that way rather than 300 zats/1000 bytes, because the threshold is always rounded to an integer and then multiplied by 3.) + Fix some messages, comments, and documentation that: * used "fee" to mean "fee rate", "kB" to mean 1000 bytes, "satoshis" to mean zatoshis, or that incorrectly used "BTC" in place of "ZEC"; * used obsolete concepts such as "zero fee" or "free transaction"; or * did not accurately document their applicability. + Fix tests that failed due to the minimum relay fee being enforced. + Fix miner_tests btest. + Fix mempool_packages and prioritisetransaction RPC tests. + Implement `GetUnpaidActionCount` and `GetWeightRatio` for ZIP 317. + ZIP 317 block construction algorithm. This breaks tests which are repaired in subsequent commits. + Add assertions that `GetRandInt*` functions are called with `nMax >= 0`. All existing uses have been checked to ensure they are consistent with this assertion. + Repair `miner_tests.py` btest. + Repair some RPC tests. + mergetoaddress_helper.py: Use `generate_and_check` helper to mine a block and make sure that it contains the expected number of transactions. + mergetoaddress_helper.py: use Decimal for amounts and integers otherwise. + Remove the implementation of score-based block template construction and the `-blockminsize` option. + Add a `-blockunpaidactionlimit` config option to configure the per-block limit on unpaid actions for ZIP 317 block template construction. + miner_tests.cpp improvements. + random.h documentation improvements. + Fix/suppress clippy warnings. + +Daira Hopwood (9): + Use a more recent URL format for GitHub release archives. + Clarify that patches to a dependency are under the same license as that dependency. + Update copyright date and email for tl_expected. + Use the same convention for the tl_expected download files as for native_cctools + Refactoring to split the weighted tx tree out of mempool_limit.{cpp,h} and make it more reusable. + Minor optimization to weighted_map::remove + This PR doesn't bring in any ZIP 317 changes yet + Another minor optimization + Change spelling of prioritisation in an error message + +DeckerSU (1): + InsertBlockIndex: pass const reference on hash, instead of hash + +Dimitris Apostolou (3): + Fix typo + Update documentation link + Fix typos + +Evan Klitzke (1): + Fix automake warnings when running autogen.sh + +Greg Pfeil (81): + Show in-progress tests when rpc-tests is interrupted + Make extra newline more explicit + Apply suggestions from code review + Make pool selection order more flexible + Simplify diversifier_index_t handling + Update tests for async z_sendmany + Limit UTXOs + Some orchard fixes for wallet_tx_builder + Return anchorHeight from ResolveInputsAndPayments + Refactoring InsufficientFundsError + Ensure we don’t make Orchard change pre-NU5 + Test updates for z_sendmany WalletTxBuilder changes + Fix weakened privacy policy for transparent change + Fix some overly-strict privacy policies in btest + Apply suggestions from code review + Unify requireTransparentCoinbase handling + Rename `Get*Balance` to `Get*Total` + Remove changes that aren’t needed by z_sendmany + Improve GetRequiredPrivacyPolicy + Add release notes for (a)sync z_sendmany changes + Assert that we get a change addr for any selector + Don’t pass PrivacyPolicy to selector constructor + Address comments on WalletTxBuilder introduction + Make RPC test output more deterministic + Update WalletTxBuilder based on review + Clarify `AddressResolutionError` + Don’t permit user-provided “internal” payments + Address WalletTxBuilder PR feedback + Ensure that a WalletTxBuilder tx balances + Additional z_sendmany test cases + Address WalletTxBuilder review feedback + Apply suggestions for WalletTxBuilder from code review + Simplify SelectOVKs + Have GetRecipientPools return a copy + Remove CWallet member from WalletTxBuilder + Improve taddr no-memo check + Update src/wallet/rpcwallet.cpp + Lock notes (except Orchard) in wallet_tx_builder + Improve Doxygen for note locking + Require `AllowRevealedRecipients` for t-change + Update release-notes for transparent change restriction + Correct EditorConfig for Makefiles + Split C++ generated from Rust into own lib + Simplify canResolveOrchard logic + Support ZIP 317 fees in the zcashd wallet + Correct change handling for ZIP 317 fees + Address review feedback re: ZIP 317 in wallet + Revert "Add `AllowRevealedSenders` to fix `mempool_nu_activation.py`" + Eliminate LegacyCompat–ZTXOSelector cycle + Simplify client.cpp + Enrich zcash-cli arg conversion + Better messages on client-side zcash-cli errors + Fix accidental reversion of #6409 + Remove unnecessary explicit privacy policy + Use examine instead of std::visit + Simplify some vector initialization + Fix edge case revealed by #6409 + Rename Arg* to Param + Adjust wallet absurd fee check for ZIP 317 + Address review feedback for ZIP 317 fees in wallet + Address more ZIP 317 fee feedback + Fix zcash-cli crash when printing help message + Use null as the ZIP 317 fee sentinel instead of -1 + Add z_sendmany RPC examples with fee field + Have z_shieldcoinbase use WalletTxBuilder + Address review feedback on z_shieldcoinbase + Fix an incorrect error message in a test + Minor improvements to z_shieldcoinbase + Support ZIP 317 fees for legacy wallet operations + Remove `-mintxfee` config option + Address review comments re: legacy wallet ZIP 317 + Remove `-txconfirmtarget` config option + Restore previous `-maxtxfee` bound + Correct -maxtxfee lower bound diagnostic messages + WalletTxBuilder support for net payments + fixup! WalletTxBuilder support for net payments + Don’t test “Insufficient funds” for `z_shieldcoinbase` + Update z_mergetoaddress to use WalletTxBuilder + Many z_mergetoaddress updates + Avoid extra copy in ResolveInputsAndPayments + Address z_mergetoaddress review feedback + +Jack Grigg (77): + rust: Add `cxx` version of `RustStream` + rust: Migrate `OrchardMerkleFrontier` to `cxx` + CreateNewBlock: Leave more space for Orchard shielded coinbase + Retroactively enable ZIP 216 before NU5 activation + rust: Compile with ThinLTO + depends: Update Rust to 1.67.1 + depends: Update Clang / libcxx to LLVM 15.0.6 + Fix 1.67.1 clippy lints + depends: Evaluate `native_packages` before `packages` + qa: Fix year in postponement lines + qa: Fix `google/leveldb` tag parsing in `updatecheck.py` + qa: Handle commit IDs correctly to `updatecheck.py` + depends: `cxx 1.0.91` + depends: `native_zstd 1.5.4` + `cargo vet regenerate imports` + qa: Import Rust crate audits from ISRG + `cargo update` + qa: Postpone LevelDB 1.23 + book: Add page with release support details and EoS halt heights + Update release support book page in release process + depends: Update Rust to 1.68.0 + qa: Replace Firefox audits with aggregated Mozilla audits in registry + qa: Import Rust crate audits from ChromeOS + Move `fEnableAddrTypeField` outside `ENABLE_WALLET` + `s/string/std::string` in `init.cpp` + CI: Check that the PR branch has a sufficiently recent base for Tekton + CI: Fix permissions for Checks workflow + Add `CChainParams::RustNetwork` + wallet: Consolidate `CWalletTx` Sapling output decryption methods + wallet: Use `zcash_note_encryption` in `CWalletTx::DecryptSaplingNote` + wallet: Use `CWalletTx::DecryptSaplingNote` in more places + wallet: Use `zcash_note_encryption` in `CWallet::FindMySaplingNotes` + wallet: Use `zcash_note_encryption` in `CWalletTx::RecoverSaplingNote` + wallet: Remove recipient-side `SaplingNotePlaintext::decrypt` + CI: Fetch all history for "recent base" check + CI: Include explicit `failure()` condition in "recent base" check + CI: Use `github.head_ref` instead of `HEAD` for "recent base" check + book: Add End-of-Support heights for v5.3.3 and v5.4.2 + CI: Remove most usages of `actions-rs` actions + CI: Migrate to `cargo-vet 0.5` + cargo vet prune + CI: Provide `write` permission for `pull-requests` + CI: Check out both the base and PR branches for "recent base" check + Migrate to `zcash_primitives 0.10` + depends: `cxx 1.0.92` + depends: CMake 3.26.0 + depends: Postpone CCache updates again + cargo update + Use `RandomInvalidOutputDescription()` everywhere it makes sense + Merge most `cxx::bridge` definitions into a single bridge + Expand `CppStream` to cover all `Stream`-like C++ types + Migrate `OrchardMerkleFrontier` to use new `CppStream` APIs + build: Tolerate split LLVM versions + Use `cxx` bridge for all Orchard bundle inspection and validation + gtest: Minor improvements to `CoinsTests` + rust: Migrate Ed25519 FFI to `cxx` + Tell `cargo-vet` to ignore patched dependencies + cargo-vet: Regenerate imports + cargo-vet: Switch to Google's aggregated audits + More crate audits + Migrate to published `orchard 0.4` + qa: Fix update checker to handle `native_clang` version format + depends: CMake 3.26.3 + depends: Rust 1.68.2 + depends: `native_zstd 1.5.5` + depends: `cxx 1.0.94` + qa: Postpone dependencies we aren't updating + cargo update + Use published `zcash_primitives 0.11` and `zcash_proofs 0.11` + test: Avoid generating a chain fork in `mempool_packages` RPC test + test: Sync blocks before invalidating them in `mempool_packages` RPC test + depends: Boost 1.82.0 + qa: Postpone Clang 16.0.2 + cargo update + depends: Rust 1.69.0 + make-release.py: Versioning changes for 5.5.0-rc1. + make-release.py: Updated manpages for 5.5.0-rc1. + +Kris Nuttycombe (26): + Fix potential path or symlink traversal + Add a docker-compose.yml for prometheus/grafana metrics collection. + Apply suggestions from code review + Make all CCoinsView methods pure-virtual. + Remove `FakeCoinsViewDB` as it is identical to `CCoinsViewDummy` + Postpone dependency updates. + make-release.py: Versioning changes for 5.3.3. + make-release.py: Updated manpages for 5.3.3. + make-release.py: Updated release notes and changelog for 5.3.3. + Set urgency to `high` in Debian changelog. + Add information about deprecated features to `deprecationinfo` results. + Apply suggestions from code review + Add a wallet-aware transaction builder. + Use WalletTxBuilder for z_sendmany + Allow selectors to require transparent coinbase + Fix a longstanding zcashd build warning + Fix `make distclean` to recursively remove `rust/gen` + Improve const-ness of CChainParams retrieval by network ID + Explicitly provide CChainParams to `EnforceNodeDeprecation` + revert broken "safe extract" functionality in golden tests. + Refactor RPC privacyPolicy handling + Update to use the `ff 0.13` dependency stack. + Add `AllowRevealedSenders` to fix `mempool_nu_activation.py` + Calculate convential fee in `CreateTransaction` before stripping sigs. + Fix formating of money strings. + Use the conventional fee for prioritisation in prioritisetransactions.py + +Luke Dashjr (1): + RPC/Mining: Restore API compatibility for prioritisetransaction + +Marco Falke (1): + wallet: Remove sendfree + +Marius Kjærstad (2): + New checkpoint at block 2000000 for mainnet + Update estimated number of transactions due to Blossom NU + +Miodrag Popović (2): + Fix for broken cross-build to Windows target on Ubuntu 22.04 and Debian 11 + Update depends/hosts/mingw32.mk to use posix variant library path + +Sean Bowe (1): + Add additional audits. + +Suhas Daftuar (4): + Add tags to mempool's mapTx indices + Fix mempool limiting for PrioritiseTransaction + Use fee deltas for determining mempool acceptance + Remove GetMinRelayFee + +TrellixVulnTeam (1): + Adding tarfile member sanitization to extractall() + +Wladimir J. van der Laan (1): + Merge #7730: Remove priority estimation + +Yasser Isa (1): + DOWNLOAD_URL dynamic in fetch-params.sh + +cronicc (1): + Fix Horizen Security contact email + +instagibbs (2): + Gave miner test values constants for less error-prone values. + Corrected values + +sasha (6): + Partially revert PR #6384, but only for URLs using a git commit hash + Download `native_cctools` and its `libtapi` to meaningful filenames + Better error messages if proof parameters aren't loaded + Enable the (existing) custom error message for the invalid checksum case + Re-download parameters if they already exist but don't have correct sums + Alias Sasha->sasha in release-notes.py to avoid authors.md split + +Jack Grigg (2): + Improvements to code comments + Adjust documentation + diff --git a/depend/zcash/doc/release-notes/release-notes-5.5.0-rc2.md b/depend/zcash/doc/release-notes/release-notes-5.5.0-rc2.md new file mode 100644 index 000000000..f94ccaba9 --- /dev/null +++ b/depend/zcash/doc/release-notes/release-notes-5.5.0-rc2.md @@ -0,0 +1,458 @@ +Notable changes +=============== + +RPC Changes +----------- + +- `getdeprecationinfo` has several changes: + - It now returns additional information about currently deprecated and + disabled features. + - A new `end_of_service` object that contains both the block height for + end-of-service and the estimated time that the end-of-service halt is + expected to occur. Note that this height is just an approximation and + will change over time as the end-of-service block height approaches, + due to the variability in block times. The + `end_of_service` object is intended to replace the `deprecationheight` + field; see the [Deprecations](#deprecations) section for additional detail. + - This RPC method was previously only available for mainnet nodes; it is now + also available for testnet and regtest nodes, in which case it does not + return end-of-service halt information (as testnet and regtest nodes do not + have an end-of-service halt feature.) +- Several `z_sendmany`, `z_shieldcoinbase` and `z_mergetoaddress` failures have + moved from synchronous to asynchronous, so while you should already be + checking the async operation status, there are now more cases that may trigger + failure at that stage. +- The `AllowRevealedRecipients` privacy policy is now required in order to choose a + transparent change address for a transaction. This will only occur when the wallet + is unable to construct the transaction without selecting funds from the transparent + pool, so the impact of this change is that for such transactions, the user must specify + `AllowFullyTransparent`. +- The `z_shieldcoinbase` RPC method now supports an optional memo. +- The `z_shieldcoinbase` and `z_mergetoaddress` RPC methods now support an + optional privacy policy. +- The `z_mergetoaddress` RPC method can now merge _to_ UAs and can also send + between different shielded pools (when `AllowRevealedAmounts` is specified). +- The `estimatepriority` RPC call has been removed. +- The `priority_delta` argument to the `prioritisetransaction` RPC call now has + no effect and must be set to a dummy value (0 or null). + +Changes to Transaction Fee Selection +------------------------------------ + +- The zcashd wallet now uses the conventional transaction fee calculated according + to [ZIP 317](https://zips.z.cash/zip-0317) by default. This conventional fee + will be used unless a fee is explicitly specified in an RPC call, or for the + wallet's legacy transaction creation APIs (`sendtoaddress`, `sendmany`, and + `fundrawtransaction`) when the `-paytxfee` option is set. +- The `-mintxfee` and `-sendfreetransactions` options have been removed. These + options previously instructed the legacy transaction creation APIs to increase + fees to this limit and to use a zero fee for "small" transactions that spend + "old" inputs, respectively. They will now cause a warning on node startup if + used. + + +Changes to Block Template Construction +-------------------------------------- + +We now use a new block template construction algorithm documented in +[ZIP 317](https://zips.z.cash/zip-0317#recommended-algorithm-for-block-template-construction). + +- This algorithm no longer favours transactions that were previously considered + "high priority" because they spent older inputs. The `-blockprioritysize` config + option, which configured the portion of the block reserved for these transactions, + has been removed and will now cause a warning if used. +- The `-blockminsize` option, which configured the size of a portion of the block + to be filled regardless of transaction fees or priority, has also been removed + and will cause a warning if used. +- A `-blockunpaidactionlimit` option has been added to control the limit on + "unpaid actions" that will be accepted in a block for transactions paying less + than the ZIP 317 fee. This defaults to 50. + +Change to Transaction Relay Policy +---------------------------------- + +The allowance for "free transactions" in mempool acceptance and relay has been +removed. All transactions must pay at least the minimum relay threshold, currently +100 zatoshis per 1000 bytes up to a maximum of 1000 zatoshis, in order to be +accepted and relayed. (Individual nodes can change this using `-minrelaytxfee` +but in practice the network default needs to be adhered to.) This policy is under +review and [might be made stricter](https://zips.z.cash/zip-0317#transaction-relaying); +if that happens then the ZIP 317 conventional fee will still be sufficient for +mempool acceptance and relay. + +Removal of Priority Estimation +------------------------------ + +Estimation of "priority" needed for a transaction to be included within a target +number of blocks, and the associated `estimatepriority` RPC call, have been +removed. The format for `fee_estimates.dat` has also changed to no longer save +these priority estimates. It will automatically be converted to the new format +which is not readable by prior versions of the software. The `-txconfirmtarget` +config option is now obsolete and has also been removed. It will cause a +warning if used. + +[Deprecations](https://zcash.github.io/zcash/user/deprecation.html) +-------------- + +The following features have been deprecated, but remain available by default. +These features may be disabled by setting `-allowdeprecated=none`. 18 weeks +after this release, these features will be disabled by default and the following +flags to `-allowdeprecated` will be required to permit their continued use: + +- `deprecationinfo_deprecationheight`: The `deprecationheight` field of + `getdeprecationinfo` has been deprecated and replaced by the `end_of_service` + object. + +Changelog +========= + +ANISH M (3): + use SOURCES_PATH instead of local git DEPENDS_SOURCES_DIR + report the git-derived version in metrics screen + Update src/metrics.cpp by removing v prefix. + +Alex Morcos (14): + Refactor CreateNewBlock to be a method of the BlockAssembler class + FIX: Account for txs already added to block in addPriorityTxs + FIX: correctly measure size of priority block + [rpc] Remove estimatepriority. + [mining] Remove -blockprioritysize. + [debug] Change -printpriority option + [cleanup] Remove estimatePriority + [rpc] sendrawtransaction no longer bypasses minRelayTxFee + [test] Remove priority from tests + [rpc] Remove priority information from mempool RPC calls + [rpc] Remove priorityDelta from prioritisetransaction + [cleanup] Remove coin age priority completely. + Allow setting minrelaytxfee to 0 + Update example zcash.conf + +Charlie O'Keefe (1): + Add reference in Makefile.am to zip317.h + +Daira Emma Hopwood (38): + Remove unnecessary #include. + Adjust indentation to be consistent without changing existing code. + Repair show_help RPC test. + Fix bit-rotted code in miner tests. + Implement ZIP 317 computations. + `cargo update` + Add audits for updates to futures-* 0.3.28 and redjubjub 0.7.0. + Add `examine`, a wrapper around `std::visit` that reverses the arguments. + Use the new `examine` macro to replace all instances of `std::visit(match {...}, specimen)`, improving code readability. + Refactoring to avoid duplicated code. + Refactoring to avoid an unnecessary temporary. + Refactor that avoids using exceptions for local flow control and is simpler. + Correct the documentation of `-rpcconnect` in the example `zcash.conf`. `-rpcconnect` is only used by `zcash-cli`. + Change ZIP 401 mempool limiting to use conventional fee. + Change ZIP 401 mempool limiting to use constants decided in zcash/zips#565. + Warn on node startup if the removed `-blockprioritysize` option is set to a non-zero value. + Log (at the mempool DEBUG level) when a transaction cannot be accepted to the mempool because its modified fee is below the minimum relay fee. + Fix the dust threshold rate to three times 100 zats/1000 bytes. (We express it that way rather than 300 zats/1000 bytes, because the threshold is always rounded to an integer and then multiplied by 3.) + Fix some messages, comments, and documentation that: * used "fee" to mean "fee rate", "kB" to mean 1000 bytes, "satoshis" to mean zatoshis, or that incorrectly used "BTC" in place of "ZEC"; * used obsolete concepts such as "zero fee" or "free transaction"; or * did not accurately document their applicability. + Fix tests that failed due to the minimum relay fee being enforced. + Fix miner_tests btest. + Fix mempool_packages and prioritisetransaction RPC tests. + Implement `GetUnpaidActionCount` and `GetWeightRatio` for ZIP 317. + ZIP 317 block construction algorithm. This breaks tests which are repaired in subsequent commits. + Add assertions that `GetRandInt*` functions are called with `nMax >= 0`. All existing uses have been checked to ensure they are consistent with this assertion. + Repair `miner_tests.py` btest. + Repair some RPC tests. + mergetoaddress_helper.py: Use `generate_and_check` helper to mine a block and make sure that it contains the expected number of transactions. + mergetoaddress_helper.py: use Decimal for amounts and integers otherwise. + Remove the implementation of score-based block template construction and the `-blockminsize` option. + Add a `-blockunpaidactionlimit` config option to configure the per-block limit on unpaid actions for ZIP 317 block template construction. + miner_tests.cpp improvements. + random.h documentation improvements. + Fix/suppress clippy warnings. + Improve the `show_help.py` RPC test to include `-help-debug` (needed to test the help change in the next commit). + Improve `-printpriority` output to log the modified fee, conventional fee, size, logical action count, and unpaid action count. This reflects the changes to use the ZIP 317 block construction algorithm and de-emphasise fee rate. + Fix a build regression if `--disable-mining` is selected. + Fix a build regression if both `--disable-mining` and `--disable-wallet` are selected. + +Daira Hopwood (9): + Use a more recent URL format for GitHub release archives. + Clarify that patches to a dependency are under the same license as that dependency. + Update copyright date and email for tl_expected. + Use the same convention for the tl_expected download files as for native_cctools + Refactoring to split the weighted tx tree out of mempool_limit.{cpp,h} and make it more reusable. + Minor optimization to weighted_map::remove + This PR doesn't bring in any ZIP 317 changes yet + Another minor optimization + Change spelling of prioritisation in an error message + +DeckerSU (1): + InsertBlockIndex: pass const reference on hash, instead of hash + +Dimitris Apostolou (3): + Fix typo + Update documentation link + Fix typos + +Evan Klitzke (1): + Fix automake warnings when running autogen.sh + +Greg Pfeil (86): + Show in-progress tests when rpc-tests is interrupted + Make extra newline more explicit + Apply suggestions from code review + Make pool selection order more flexible + Simplify diversifier_index_t handling + Update tests for async z_sendmany + Limit UTXOs + Some orchard fixes for wallet_tx_builder + Return anchorHeight from ResolveInputsAndPayments + Refactoring InsufficientFundsError + Ensure we don’t make Orchard change pre-NU5 + Test updates for z_sendmany WalletTxBuilder changes + Fix weakened privacy policy for transparent change + Fix some overly-strict privacy policies in btest + Apply suggestions from code review + Unify requireTransparentCoinbase handling + Rename `Get*Balance` to `Get*Total` + Remove changes that aren’t needed by z_sendmany + Improve GetRequiredPrivacyPolicy + Add release notes for (a)sync z_sendmany changes + Assert that we get a change addr for any selector + Don’t pass PrivacyPolicy to selector constructor + Address comments on WalletTxBuilder introduction + Make RPC test output more deterministic + Update WalletTxBuilder based on review + Clarify `AddressResolutionError` + Don’t permit user-provided “internal” payments + Address WalletTxBuilder PR feedback + Ensure that a WalletTxBuilder tx balances + Additional z_sendmany test cases + Address WalletTxBuilder review feedback + Apply suggestions for WalletTxBuilder from code review + Simplify SelectOVKs + Have GetRecipientPools return a copy + Remove CWallet member from WalletTxBuilder + Improve taddr no-memo check + Update src/wallet/rpcwallet.cpp + Lock notes (except Orchard) in wallet_tx_builder + Improve Doxygen for note locking + Require `AllowRevealedRecipients` for t-change + Update release-notes for transparent change restriction + Correct EditorConfig for Makefiles + Split C++ generated from Rust into own lib + Simplify canResolveOrchard logic + Support ZIP 317 fees in the zcashd wallet + Correct change handling for ZIP 317 fees + Address review feedback re: ZIP 317 in wallet + Revert "Add `AllowRevealedSenders` to fix `mempool_nu_activation.py`" + Eliminate LegacyCompat–ZTXOSelector cycle + Simplify client.cpp + Enrich zcash-cli arg conversion + Better messages on client-side zcash-cli errors + Fix accidental reversion of #6409 + Remove unnecessary explicit privacy policy + Use examine instead of std::visit + Simplify some vector initialization + Fix edge case revealed by #6409 + Rename Arg* to Param + Adjust wallet absurd fee check for ZIP 317 + Address review feedback for ZIP 317 fees in wallet + Address more ZIP 317 fee feedback + Fix zcash-cli crash when printing help message + Use null as the ZIP 317 fee sentinel instead of -1 + Add z_sendmany RPC examples with fee field + Have z_shieldcoinbase use WalletTxBuilder + Address review feedback on z_shieldcoinbase + Fix an incorrect error message in a test + Minor improvements to z_shieldcoinbase + Support ZIP 317 fees for legacy wallet operations + Remove `-mintxfee` config option + Address review comments re: legacy wallet ZIP 317 + Remove `-txconfirmtarget` config option + Restore previous `-maxtxfee` bound + Correct -maxtxfee lower bound diagnostic messages + WalletTxBuilder support for net payments + fixup! WalletTxBuilder support for net payments + Don’t test “Insufficient funds” for `z_shieldcoinbase` + Update z_mergetoaddress to use WalletTxBuilder + Many z_mergetoaddress updates + Avoid extra copy in ResolveInputsAndPayments + Address z_mergetoaddress review feedback + Allow explicit “no memo” in z_mergetoaddress + Add `memo` parameter to `z_shieldcoinbase` + Support UA destinations in `z_mergetoaddress` + Include Orchard dest in z_mergetoaddress estimation + Update release notes + +Jack Grigg (90): + rust: Add `cxx` version of `RustStream` + rust: Migrate `OrchardMerkleFrontier` to `cxx` + CreateNewBlock: Leave more space for Orchard shielded coinbase + Retroactively enable ZIP 216 before NU5 activation + rust: Compile with ThinLTO + depends: Update Rust to 1.67.1 + depends: Update Clang / libcxx to LLVM 15.0.6 + Fix 1.67.1 clippy lints + depends: Evaluate `native_packages` before `packages` + qa: Fix year in postponement lines + qa: Fix `google/leveldb` tag parsing in `updatecheck.py` + qa: Handle commit IDs correctly to `updatecheck.py` + depends: `cxx 1.0.91` + depends: `native_zstd 1.5.4` + `cargo vet regenerate imports` + qa: Import Rust crate audits from ISRG + `cargo update` + qa: Postpone LevelDB 1.23 + book: Add page with release support details and EoS halt heights + Update release support book page in release process + depends: Update Rust to 1.68.0 + qa: Replace Firefox audits with aggregated Mozilla audits in registry + qa: Import Rust crate audits from ChromeOS + Move `fEnableAddrTypeField` outside `ENABLE_WALLET` + `s/string/std::string` in `init.cpp` + CI: Check that the PR branch has a sufficiently recent base for Tekton + CI: Fix permissions for Checks workflow + Add `CChainParams::RustNetwork` + wallet: Consolidate `CWalletTx` Sapling output decryption methods + wallet: Use `zcash_note_encryption` in `CWalletTx::DecryptSaplingNote` + wallet: Use `CWalletTx::DecryptSaplingNote` in more places + wallet: Use `zcash_note_encryption` in `CWallet::FindMySaplingNotes` + wallet: Use `zcash_note_encryption` in `CWalletTx::RecoverSaplingNote` + wallet: Remove recipient-side `SaplingNotePlaintext::decrypt` + CI: Fetch all history for "recent base" check + CI: Include explicit `failure()` condition in "recent base" check + CI: Use `github.head_ref` instead of `HEAD` for "recent base" check + book: Add End-of-Support heights for v5.3.3 and v5.4.2 + CI: Remove most usages of `actions-rs` actions + CI: Migrate to `cargo-vet 0.5` + cargo vet prune + CI: Provide `write` permission for `pull-requests` + CI: Check out both the base and PR branches for "recent base" check + Migrate to `zcash_primitives 0.10` + depends: `cxx 1.0.92` + depends: CMake 3.26.0 + depends: Postpone CCache updates again + cargo update + Use `RandomInvalidOutputDescription()` everywhere it makes sense + Merge most `cxx::bridge` definitions into a single bridge + Expand `CppStream` to cover all `Stream`-like C++ types + Migrate `OrchardMerkleFrontier` to use new `CppStream` APIs + build: Tolerate split LLVM versions + Use `cxx` bridge for all Orchard bundle inspection and validation + gtest: Minor improvements to `CoinsTests` + rust: Migrate Ed25519 FFI to `cxx` + Tell `cargo-vet` to ignore patched dependencies + cargo-vet: Regenerate imports + cargo-vet: Switch to Google's aggregated audits + More crate audits + Migrate to published `orchard 0.4` + qa: Fix update checker to handle `native_clang` version format + depends: CMake 3.26.3 + depends: Rust 1.68.2 + depends: `native_zstd 1.5.5` + depends: `cxx 1.0.94` + qa: Postpone dependencies we aren't updating + cargo update + Use published `zcash_primitives 0.11` and `zcash_proofs 0.11` + test: Avoid generating a chain fork in `mempool_packages` RPC test + test: Sync blocks before invalidating them in `mempool_packages` RPC test + depends: Boost 1.82.0 + qa: Postpone Clang 16.0.2 + cargo update + depends: Rust 1.69.0 + make-release.py: Versioning changes for 5.5.0-rc1. + make-release.py: Updated manpages for 5.5.0-rc1. + make-release.py: Updated release notes and changelog for 5.5.0-rc1. + make-release.py: Updated book for 5.5.0-rc1. + CI: Add a GitHub Actions workflow that builds zcashd for platform tiers + CI: Add caching to build workflow + depends: Ensure `native_cxxbridge` source is fetched for `rustcxx` + depends: Don't build BDB utilities on macOS + depends: Remove Fortran and LLDB components from staged `native_clang` + CI: Build with `--with-libs`, `--disable-wallet`, and `--disable-mining` + Fix `make-release.py` to write correct halt height into book + Update v5.5.0 release notes + build: Fix MinGW cross-compilation with `--disable-wallet` + make-release.py: Versioning changes for 5.5.0-rc2. + make-release.py: Updated manpages for 5.5.0-rc2. + +Kris Nuttycombe (26): + Fix potential path or symlink traversal + Add a docker-compose.yml for prometheus/grafana metrics collection. + Apply suggestions from code review + Make all CCoinsView methods pure-virtual. + Remove `FakeCoinsViewDB` as it is identical to `CCoinsViewDummy` + Postpone dependency updates. + make-release.py: Versioning changes for 5.3.3. + make-release.py: Updated manpages for 5.3.3. + make-release.py: Updated release notes and changelog for 5.3.3. + Set urgency to `high` in Debian changelog. + Add information about deprecated features to `deprecationinfo` results. + Apply suggestions from code review + Add a wallet-aware transaction builder. + Use WalletTxBuilder for z_sendmany + Allow selectors to require transparent coinbase + Fix a longstanding zcashd build warning + Fix `make distclean` to recursively remove `rust/gen` + Improve const-ness of CChainParams retrieval by network ID + Explicitly provide CChainParams to `EnforceNodeDeprecation` + revert broken "safe extract" functionality in golden tests. + Refactor RPC privacyPolicy handling + Update to use the `ff 0.13` dependency stack. + Add `AllowRevealedSenders` to fix `mempool_nu_activation.py` + Calculate convential fee in `CreateTransaction` before stripping sigs. + Fix formating of money strings. + Use the conventional fee for prioritisation in prioritisetransactions.py + +Luke Dashjr (1): + RPC/Mining: Restore API compatibility for prioritisetransaction + +Marco Falke (1): + wallet: Remove sendfree + +Marius Kjærstad (2): + New checkpoint at block 2000000 for mainnet + Update estimated number of transactions due to Blossom NU + +Miodrag Popović (2): + Fix for broken cross-build to Windows target on Ubuntu 22.04 and Debian 11 + Update depends/hosts/mingw32.mk to use posix variant library path + +Sean Bowe (1): + Add additional audits. + +Suhas Daftuar (4): + Add tags to mempool's mapTx indices + Fix mempool limiting for PrioritiseTransaction + Use fee deltas for determining mempool acceptance + Remove GetMinRelayFee + +TrellixVulnTeam (1): + Adding tarfile member sanitization to extractall() + +Wladimir J. van der Laan (1): + Merge #7730: Remove priority estimation + +Yasser Isa (1): + DOWNLOAD_URL dynamic in fetch-params.sh + +cronicc (1): + Fix Horizen Security contact email + +instagibbs (2): + Gave miner test values constants for less error-prone values. + Corrected values + +sasha (6): + Partially revert PR #6384, but only for URLs using a git commit hash + Download `native_cctools` and its `libtapi` to meaningful filenames + Better error messages if proof parameters aren't loaded + Enable the (existing) custom error message for the invalid checksum case + Re-download parameters if they already exist but don't have correct sums + Alias Sasha->sasha in release-notes.py to avoid authors.md split + +Jack Grigg (4): + Improvements to code comments + Adjust documentation + z_mergetoaddress: Include Sapling output padding in size estimate + test: Fix copyright years in new RPC tests + +teor (1): + Replace custom zcash_script TxInputStream with RustDataStream + diff --git a/depend/zcash/doc/release-notes/release-notes-5.5.0-rc3.md b/depend/zcash/doc/release-notes/release-notes-5.5.0-rc3.md new file mode 100644 index 000000000..3dadc1f19 --- /dev/null +++ b/depend/zcash/doc/release-notes/release-notes-5.5.0-rc3.md @@ -0,0 +1,467 @@ +Notable changes +=============== + +RPC Changes +----------- + +- `getdeprecationinfo` has several changes: + - It now returns additional information about currently deprecated and + disabled features. + - A new `end_of_service` object that contains both the block height for + end-of-service and the estimated time that the end-of-service halt is + expected to occur. Note that this time is just an approximation and + will change over time as the end-of-service block height approaches, + due to the variability in block times. The + `end_of_service` object is intended to replace the `deprecationheight` + field; see the [Deprecations](#deprecations) section for additional detail. + - This RPC method was previously only available for mainnet nodes; it is now + also available for testnet and regtest nodes, in which case it does not + return end-of-service halt information (as testnet and regtest nodes do not + have an end-of-service halt feature.) +- Several `z_sendmany`, `z_shieldcoinbase` and `z_mergetoaddress` failures have + moved from synchronous to asynchronous, so while you should already be + checking the async operation status, there are now more cases that may trigger + failure at that stage. +- The `AllowRevealedRecipients` privacy policy is now required in order to choose a + transparent change address for a transaction. This will only occur when the wallet + is unable to construct the transaction without selecting funds from the transparent + pool, so the impact of this change is that for such transactions, the user must specify + `AllowFullyTransparent`. +- The `z_shieldcoinbase` RPC method now supports an optional memo. +- The `z_shieldcoinbase` and `z_mergetoaddress` RPC methods now support an + optional privacy policy. +- The `z_mergetoaddress` RPC method can now merge _to_ UAs and can also send + between different shielded pools (when `AllowRevealedAmounts` is specified). +- The `estimatepriority` RPC call has been removed. +- The `priority_delta` argument to the `prioritisetransaction` RPC call now has + no effect and must be set to a dummy value (0 or null). + +Changes to Transaction Fee Selection +------------------------------------ + +- The zcashd wallet now uses the conventional transaction fee calculated according + to [ZIP 317](https://zips.z.cash/zip-0317) by default. This conventional fee + will be used unless a fee is explicitly specified in an RPC call, or for the + wallet's legacy transaction creation APIs (`sendtoaddress`, `sendmany`, and + `fundrawtransaction`) when the `-paytxfee` option is set. +- The `-mintxfee` and `-sendfreetransactions` options have been removed. These + options previously instructed the legacy transaction creation APIs to increase + fees to this limit and to use a zero fee for "small" transactions that spend + "old" inputs, respectively. They will now cause a warning on node startup if + used. + + +Changes to Block Template Construction +-------------------------------------- + +We now use a new block template construction algorithm documented in +[ZIP 317](https://zips.z.cash/zip-0317#recommended-algorithm-for-block-template-construction). + +- This algorithm no longer favours transactions that were previously considered + "high priority" because they spent older inputs. The `-blockprioritysize` config + option, which configured the portion of the block reserved for these transactions, + has been removed and will now cause a warning if used. +- The `-blockminsize` option, which configured the size of a portion of the block + to be filled regardless of transaction fees or priority, has also been removed + and will cause a warning if used. +- A `-blockunpaidactionlimit` option has been added to control the limit on + "unpaid actions" that will be accepted in a block for transactions paying less + than the ZIP 317 fee. This defaults to 50. + +Change to Transaction Relay Policy +---------------------------------- + +The allowance for "free transactions" in mempool acceptance and relay has been +removed. All transactions must pay at least the minimum relay threshold, currently +100 zatoshis per 1000 bytes up to a maximum of 1000 zatoshis, in order to be +accepted and relayed. (Individual nodes can change this using `-minrelaytxfee` +but in practice the network default needs to be adhered to.) This policy is under +review and [might be made stricter](https://zips.z.cash/zip-0317#transaction-relaying); +if that happens then the ZIP 317 conventional fee will still be sufficient for +mempool acceptance and relay. + +Removal of Priority Estimation +------------------------------ + +Estimation of "priority" needed for a transaction to be included within a target +number of blocks, and the associated `estimatepriority` RPC call, have been +removed. The format for `fee_estimates.dat` has also changed to no longer save +these priority estimates. It will automatically be converted to the new format +which is not readable by prior versions of the software. The `-txconfirmtarget` +config option is now obsolete and has also been removed. It will cause a +warning if used. + +[Deprecations](https://zcash.github.io/zcash/user/deprecation.html) +-------------- + +The following features have been deprecated, but remain available by default. +These features may be disabled by setting `-allowdeprecated=none`. 18 weeks +after this release, these features will be disabled by default and the following +flags to `-allowdeprecated` will be required to permit their continued use: + +- `deprecationinfo_deprecationheight`: The `deprecationheight` field of + `getdeprecationinfo` has been deprecated and replaced by the `end_of_service` + object. + +Changelog +========= + +ANISH M (3): + use SOURCES_PATH instead of local git DEPENDS_SOURCES_DIR + report the git-derived version in metrics screen + Update src/metrics.cpp by removing v prefix. + +Alex Morcos (14): + Refactor CreateNewBlock to be a method of the BlockAssembler class + FIX: Account for txs already added to block in addPriorityTxs + FIX: correctly measure size of priority block + [rpc] Remove estimatepriority. + [mining] Remove -blockprioritysize. + [debug] Change -printpriority option + [cleanup] Remove estimatePriority + [rpc] sendrawtransaction no longer bypasses minRelayTxFee + [test] Remove priority from tests + [rpc] Remove priority information from mempool RPC calls + [rpc] Remove priorityDelta from prioritisetransaction + [cleanup] Remove coin age priority completely. + Allow setting minrelaytxfee to 0 + Update example zcash.conf + +Charlie O'Keefe (1): + Add reference in Makefile.am to zip317.h + +Daira Emma Hopwood (38): + Remove unnecessary #include. + Adjust indentation to be consistent without changing existing code. + Repair show_help RPC test. + Fix bit-rotted code in miner tests. + Implement ZIP 317 computations. + `cargo update` + Add audits for updates to futures-* 0.3.28 and redjubjub 0.7.0. + Add `examine`, a wrapper around `std::visit` that reverses the arguments. + Use the new `examine` macro to replace all instances of `std::visit(match {...}, specimen)`, improving code readability. + Refactoring to avoid duplicated code. + Refactoring to avoid an unnecessary temporary. + Refactor that avoids using exceptions for local flow control and is simpler. + Correct the documentation of `-rpcconnect` in the example `zcash.conf`. `-rpcconnect` is only used by `zcash-cli`. + Change ZIP 401 mempool limiting to use conventional fee. + Change ZIP 401 mempool limiting to use constants decided in zcash/zips#565. + Warn on node startup if the removed `-blockprioritysize` option is set to a non-zero value. + Log (at the mempool DEBUG level) when a transaction cannot be accepted to the mempool because its modified fee is below the minimum relay fee. + Fix the dust threshold rate to three times 100 zats/1000 bytes. (We express it that way rather than 300 zats/1000 bytes, because the threshold is always rounded to an integer and then multiplied by 3.) + Fix some messages, comments, and documentation that: * used "fee" to mean "fee rate", "kB" to mean 1000 bytes, "satoshis" to mean zatoshis, or that incorrectly used "BTC" in place of "ZEC"; * used obsolete concepts such as "zero fee" or "free transaction"; or * did not accurately document their applicability. + Fix tests that failed due to the minimum relay fee being enforced. + Fix miner_tests btest. + Fix mempool_packages and prioritisetransaction RPC tests. + Implement `GetUnpaidActionCount` and `GetWeightRatio` for ZIP 317. + ZIP 317 block construction algorithm. This breaks tests which are repaired in subsequent commits. + Add assertions that `GetRandInt*` functions are called with `nMax >= 0`. All existing uses have been checked to ensure they are consistent with this assertion. + Repair `miner_tests.py` btest. + Repair some RPC tests. + mergetoaddress_helper.py: Use `generate_and_check` helper to mine a block and make sure that it contains the expected number of transactions. + mergetoaddress_helper.py: use Decimal for amounts and integers otherwise. + Remove the implementation of score-based block template construction and the `-blockminsize` option. + Add a `-blockunpaidactionlimit` config option to configure the per-block limit on unpaid actions for ZIP 317 block template construction. + miner_tests.cpp improvements. + random.h documentation improvements. + Fix/suppress clippy warnings. + Improve the `show_help.py` RPC test to include `-help-debug` (needed to test the help change in the next commit). + Improve `-printpriority` output to log the modified fee, conventional fee, size, logical action count, and unpaid action count. This reflects the changes to use the ZIP 317 block construction algorithm and de-emphasise fee rate. + Fix a build regression if `--disable-mining` is selected. + Fix a build regression if both `--disable-mining` and `--disable-wallet` are selected. + +Daira Hopwood (9): + Use a more recent URL format for GitHub release archives. + Clarify that patches to a dependency are under the same license as that dependency. + Update copyright date and email for tl_expected. + Use the same convention for the tl_expected download files as for native_cctools + Refactoring to split the weighted tx tree out of mempool_limit.{cpp,h} and make it more reusable. + Minor optimization to weighted_map::remove + This PR doesn't bring in any ZIP 317 changes yet + Another minor optimization + Change spelling of prioritisation in an error message + +DeckerSU (1): + InsertBlockIndex: pass const reference on hash, instead of hash + +Dimitris Apostolou (3): + Fix typo + Update documentation link + Fix typos + +Evan Klitzke (1): + Fix automake warnings when running autogen.sh + +Greg Pfeil (89): + Show in-progress tests when rpc-tests is interrupted + Make extra newline more explicit + Apply suggestions from code review + Make pool selection order more flexible + Simplify diversifier_index_t handling + Update tests for async z_sendmany + Limit UTXOs + Some orchard fixes for wallet_tx_builder + Return anchorHeight from ResolveInputsAndPayments + Refactoring InsufficientFundsError + Ensure we don’t make Orchard change pre-NU5 + Test updates for z_sendmany WalletTxBuilder changes + Fix weakened privacy policy for transparent change + Fix some overly-strict privacy policies in btest + Apply suggestions from code review + Unify requireTransparentCoinbase handling + Rename `Get*Balance` to `Get*Total` + Remove changes that aren’t needed by z_sendmany + Improve GetRequiredPrivacyPolicy + Add release notes for (a)sync z_sendmany changes + Assert that we get a change addr for any selector + Don’t pass PrivacyPolicy to selector constructor + Address comments on WalletTxBuilder introduction + Make RPC test output more deterministic + Update WalletTxBuilder based on review + Clarify `AddressResolutionError` + Don’t permit user-provided “internal” payments + Address WalletTxBuilder PR feedback + Ensure that a WalletTxBuilder tx balances + Additional z_sendmany test cases + Address WalletTxBuilder review feedback + Apply suggestions for WalletTxBuilder from code review + Simplify SelectOVKs + Have GetRecipientPools return a copy + Remove CWallet member from WalletTxBuilder + Improve taddr no-memo check + Update src/wallet/rpcwallet.cpp + Lock notes (except Orchard) in wallet_tx_builder + Improve Doxygen for note locking + Require `AllowRevealedRecipients` for t-change + Update release-notes for transparent change restriction + Correct EditorConfig for Makefiles + Split C++ generated from Rust into own lib + Simplify canResolveOrchard logic + Support ZIP 317 fees in the zcashd wallet + Correct change handling for ZIP 317 fees + Address review feedback re: ZIP 317 in wallet + Revert "Add `AllowRevealedSenders` to fix `mempool_nu_activation.py`" + Eliminate LegacyCompat–ZTXOSelector cycle + Simplify client.cpp + Enrich zcash-cli arg conversion + Better messages on client-side zcash-cli errors + Fix accidental reversion of #6409 + Remove unnecessary explicit privacy policy + Use examine instead of std::visit + Simplify some vector initialization + Fix edge case revealed by #6409 + Rename Arg* to Param + Adjust wallet absurd fee check for ZIP 317 + Address review feedback for ZIP 317 fees in wallet + Address more ZIP 317 fee feedback + Fix zcash-cli crash when printing help message + Use null as the ZIP 317 fee sentinel instead of -1 + Add z_sendmany RPC examples with fee field + Have z_shieldcoinbase use WalletTxBuilder + Address review feedback on z_shieldcoinbase + Fix an incorrect error message in a test + Minor improvements to z_shieldcoinbase + Support ZIP 317 fees for legacy wallet operations + Remove `-mintxfee` config option + Address review comments re: legacy wallet ZIP 317 + Remove `-txconfirmtarget` config option + Restore previous `-maxtxfee` bound + Correct -maxtxfee lower bound diagnostic messages + WalletTxBuilder support for net payments + fixup! WalletTxBuilder support for net payments + Don’t test “Insufficient funds” for `z_shieldcoinbase` + Update z_mergetoaddress to use WalletTxBuilder + Many z_mergetoaddress updates + Avoid extra copy in ResolveInputsAndPayments + Address z_mergetoaddress review feedback + Allow explicit “no memo” in z_mergetoaddress + Add `memo` parameter to `z_shieldcoinbase` + Support UA destinations in `z_mergetoaddress` + Include Orchard dest in z_mergetoaddress estimation + Update release notes + Support privacyPolicy parameters in zcash-cli + Treat "F6" in RPC calls as if no memo were provided + Support nullable strings in `zcash-cli` + +Jack Grigg (92): + rust: Add `cxx` version of `RustStream` + rust: Migrate `OrchardMerkleFrontier` to `cxx` + CreateNewBlock: Leave more space for Orchard shielded coinbase + Retroactively enable ZIP 216 before NU5 activation + rust: Compile with ThinLTO + depends: Update Rust to 1.67.1 + depends: Update Clang / libcxx to LLVM 15.0.6 + Fix 1.67.1 clippy lints + depends: Evaluate `native_packages` before `packages` + qa: Fix year in postponement lines + qa: Fix `google/leveldb` tag parsing in `updatecheck.py` + qa: Handle commit IDs correctly to `updatecheck.py` + depends: `cxx 1.0.91` + depends: `native_zstd 1.5.4` + `cargo vet regenerate imports` + qa: Import Rust crate audits from ISRG + `cargo update` + qa: Postpone LevelDB 1.23 + book: Add page with release support details and EoS halt heights + Update release support book page in release process + depends: Update Rust to 1.68.0 + qa: Replace Firefox audits with aggregated Mozilla audits in registry + qa: Import Rust crate audits from ChromeOS + Move `fEnableAddrTypeField` outside `ENABLE_WALLET` + `s/string/std::string` in `init.cpp` + CI: Check that the PR branch has a sufficiently recent base for Tekton + CI: Fix permissions for Checks workflow + Add `CChainParams::RustNetwork` + wallet: Consolidate `CWalletTx` Sapling output decryption methods + wallet: Use `zcash_note_encryption` in `CWalletTx::DecryptSaplingNote` + wallet: Use `CWalletTx::DecryptSaplingNote` in more places + wallet: Use `zcash_note_encryption` in `CWallet::FindMySaplingNotes` + wallet: Use `zcash_note_encryption` in `CWalletTx::RecoverSaplingNote` + wallet: Remove recipient-side `SaplingNotePlaintext::decrypt` + CI: Fetch all history for "recent base" check + CI: Include explicit `failure()` condition in "recent base" check + CI: Use `github.head_ref` instead of `HEAD` for "recent base" check + book: Add End-of-Support heights for v5.3.3 and v5.4.2 + CI: Remove most usages of `actions-rs` actions + CI: Migrate to `cargo-vet 0.5` + cargo vet prune + CI: Provide `write` permission for `pull-requests` + CI: Check out both the base and PR branches for "recent base" check + Migrate to `zcash_primitives 0.10` + depends: `cxx 1.0.92` + depends: CMake 3.26.0 + depends: Postpone CCache updates again + cargo update + Use `RandomInvalidOutputDescription()` everywhere it makes sense + Merge most `cxx::bridge` definitions into a single bridge + Expand `CppStream` to cover all `Stream`-like C++ types + Migrate `OrchardMerkleFrontier` to use new `CppStream` APIs + build: Tolerate split LLVM versions + Use `cxx` bridge for all Orchard bundle inspection and validation + gtest: Minor improvements to `CoinsTests` + rust: Migrate Ed25519 FFI to `cxx` + Tell `cargo-vet` to ignore patched dependencies + cargo-vet: Regenerate imports + cargo-vet: Switch to Google's aggregated audits + More crate audits + Migrate to published `orchard 0.4` + qa: Fix update checker to handle `native_clang` version format + depends: CMake 3.26.3 + depends: Rust 1.68.2 + depends: `native_zstd 1.5.5` + depends: `cxx 1.0.94` + qa: Postpone dependencies we aren't updating + cargo update + Use published `zcash_primitives 0.11` and `zcash_proofs 0.11` + test: Avoid generating a chain fork in `mempool_packages` RPC test + test: Sync blocks before invalidating them in `mempool_packages` RPC test + depends: Boost 1.82.0 + qa: Postpone Clang 16.0.2 + cargo update + depends: Rust 1.69.0 + make-release.py: Versioning changes for 5.5.0-rc1. + make-release.py: Updated manpages for 5.5.0-rc1. + make-release.py: Updated release notes and changelog for 5.5.0-rc1. + make-release.py: Updated book for 5.5.0-rc1. + CI: Add a GitHub Actions workflow that builds zcashd for platform tiers + CI: Add caching to build workflow + depends: Ensure `native_cxxbridge` source is fetched for `rustcxx` + depends: Don't build BDB utilities on macOS + depends: Remove Fortran and LLDB components from staged `native_clang` + CI: Build with `--with-libs`, `--disable-wallet`, and `--disable-mining` + Fix `make-release.py` to write correct halt height into book + Update v5.5.0 release notes + build: Fix MinGW cross-compilation with `--disable-wallet` + make-release.py: Versioning changes for 5.5.0-rc2. + make-release.py: Updated manpages for 5.5.0-rc2. + make-release.py: Updated release notes and changelog for 5.5.0-rc2. + make-release.py: Updated book for 5.5.0-rc2. + +Kris Nuttycombe (29): + Fix potential path or symlink traversal + Add a docker-compose.yml for prometheus/grafana metrics collection. + Apply suggestions from code review + Make all CCoinsView methods pure-virtual. + Remove `FakeCoinsViewDB` as it is identical to `CCoinsViewDummy` + Postpone dependency updates. + make-release.py: Versioning changes for 5.3.3. + make-release.py: Updated manpages for 5.3.3. + make-release.py: Updated release notes and changelog for 5.3.3. + Set urgency to `high` in Debian changelog. + Add information about deprecated features to `deprecationinfo` results. + Apply suggestions from code review + Add a wallet-aware transaction builder. + Use WalletTxBuilder for z_sendmany + Allow selectors to require transparent coinbase + Fix a longstanding zcashd build warning + Fix `make distclean` to recursively remove `rust/gen` + Improve const-ness of CChainParams retrieval by network ID + Explicitly provide CChainParams to `EnforceNodeDeprecation` + revert broken "safe extract" functionality in golden tests. + Refactor RPC privacyPolicy handling + Update to use the `ff 0.13` dependency stack. + Add `AllowRevealedSenders` to fix `mempool_nu_activation.py` + Calculate convential fee in `CreateTransaction` before stripping sigs. + Fix formating of money strings. + Use the conventional fee for prioritisation in prioritisetransactions.py + `z_sendmany` now accepts 6 parameters, not 5. + make-release.py: Versioning changes for 5.5.0-rc3. + make-release.py: Updated manpages for 5.5.0-rc3. + +Luke Dashjr (1): + RPC/Mining: Restore API compatibility for prioritisetransaction + +Marco Falke (1): + wallet: Remove sendfree + +Marius Kjærstad (2): + New checkpoint at block 2000000 for mainnet + Update estimated number of transactions due to Blossom NU + +Miodrag Popović (2): + Fix for broken cross-build to Windows target on Ubuntu 22.04 and Debian 11 + Update depends/hosts/mingw32.mk to use posix variant library path + +Sean Bowe (1): + Add additional audits. + +Suhas Daftuar (4): + Add tags to mempool's mapTx indices + Fix mempool limiting for PrioritiseTransaction + Use fee deltas for determining mempool acceptance + Remove GetMinRelayFee + +TrellixVulnTeam (1): + Adding tarfile member sanitization to extractall() + +Wladimir J. van der Laan (1): + Merge #7730: Remove priority estimation + +Yasser Isa (1): + DOWNLOAD_URL dynamic in fetch-params.sh + +cronicc (1): + Fix Horizen Security contact email + +instagibbs (2): + Gave miner test values constants for less error-prone values. + Corrected values + +sasha (6): + Partially revert PR #6384, but only for URLs using a git commit hash + Download `native_cctools` and its `libtapi` to meaningful filenames + Better error messages if proof parameters aren't loaded + Enable the (existing) custom error message for the invalid checksum case + Re-download parameters if they already exist but don't have correct sums + Alias Sasha->sasha in release-notes.py to avoid authors.md split + +Jack Grigg (4): + Improvements to code comments + Adjust documentation + z_mergetoaddress: Include Sapling output padding in size estimate + test: Fix copyright years in new RPC tests + +teor (2): + Replace custom zcash_script TxInputStream with RustDataStream + Change module comment in bridge.rs to doc comment + diff --git a/depend/zcash/doc/release-notes/release-notes-5.5.0.md b/depend/zcash/doc/release-notes/release-notes-5.5.0.md new file mode 100644 index 000000000..cafb7cdaf --- /dev/null +++ b/depend/zcash/doc/release-notes/release-notes-5.5.0.md @@ -0,0 +1,479 @@ +Notable changes +=============== + +RPC Changes +----------- + +- `getdeprecationinfo` has several changes: + - It now returns additional information about currently deprecated and + disabled features. + - A new `end_of_service` object that contains both the block height for + end-of-service and the estimated time that the end-of-service halt is + expected to occur. Note that this height is just an approximation and + will change over time as the end-of-service block height approaches, + due to the variability in block times. The + `end_of_service` object is intended to replace the `deprecationheight` + field; see the [Deprecations](#deprecations) section for additional detail. + - This RPC method was previously only available for mainnet nodes; it is now + also available for testnet and regtest nodes, in which case it does not + return end-of-service halt information (as testnet and regtest nodes do not + have an end-of-service halt feature.) +- Several `z_sendmany`, `z_shieldcoinbase` and `z_mergetoaddress` failures have + moved from synchronous to asynchronous, so while you should already be + checking the async operation status, there are now more cases that may trigger + failure at that stage. +- The `AllowRevealedRecipients` privacy policy is now required in order to choose a + transparent change address for a transaction. This will only occur when the wallet + is unable to construct the transaction without selecting funds from the transparent + pool, so the impact of this change is that for such transactions, the user must specify + `AllowFullyTransparent`. +- The `z_shieldcoinbase` RPC method now supports an optional memo. +- The `z_shieldcoinbase` and `z_mergetoaddress` RPC methods now support an + optional privacy policy. +- The `z_mergetoaddress` RPC method can now merge _to_ UAs and can also send + between different shielded pools (when `AllowRevealedAmounts` is specified). +- The `estimatepriority` RPC call has been removed. +- The `priority_delta` argument to the `prioritisetransaction` RPC call now has + no effect and must be set to a dummy value (0 or null). + +Changes to Transaction Fee Selection +------------------------------------ + +- The zcashd wallet now uses the conventional transaction fee calculated according + to [ZIP 317](https://zips.z.cash/zip-0317) by default. This conventional fee + will be used unless a fee is explicitly specified in an RPC call, or for the + wallet's legacy transaction creation APIs (`sendtoaddress`, `sendmany`, and + `fundrawtransaction`) when the `-paytxfee` option is set. +- The `-mintxfee` and `-sendfreetransactions` options have been removed. These + options previously instructed the legacy transaction creation APIs to increase + fees to this limit and to use a zero fee for "small" transactions that spend + "old" inputs, respectively. They will now cause a warning on node startup if + used. + + +Changes to Block Template Construction +-------------------------------------- + +We now use a new block template construction algorithm documented in +[ZIP 317](https://zips.z.cash/zip-0317#recommended-algorithm-for-block-template-construction). + +- This algorithm no longer favours transactions that were previously considered + "high priority" because they spent older inputs. The `-blockprioritysize` config + option, which configured the portion of the block reserved for these transactions, + has been removed and will now cause a warning if used. +- The `-blockminsize` option, which configured the size of a portion of the block + to be filled regardless of transaction fees or priority, has also been removed + and will cause a warning if used. +- A `-blockunpaidactionlimit` option has been added to control the limit on + "unpaid actions" that will be accepted in a block for transactions paying less + than the ZIP 317 fee. This defaults to 50. + +Change to Transaction Relay Policy +---------------------------------- + +The allowance for "free transactions" in mempool acceptance and relay has been +removed. All transactions must pay at least the minimum relay threshold, currently +100 zatoshis per 1000 bytes up to a maximum of 1000 zatoshis, in order to be +accepted and relayed. (Individual nodes can change this using `-minrelaytxfee` +but in practice the network default needs to be adhered to.) This policy is under +review and [might be made stricter](https://zips.z.cash/zip-0317#transaction-relaying); +if that happens then the ZIP 317 conventional fee will still be sufficient for +mempool acceptance and relay. + +Removal of Priority Estimation +------------------------------ + +Estimation of "priority" needed for a transaction to be included within a target +number of blocks, and the associated `estimatepriority` RPC call, have been +removed. The format for `fee_estimates.dat` has also changed to no longer save +these priority estimates. It will automatically be converted to the new format +which is not readable by prior versions of the software. The `-txconfirmtarget` +config option is now obsolete and has also been removed. It will cause a +warning if used. + +[Deprecations](https://zcash.github.io/zcash/user/deprecation.html) +-------------- + +The following features have been deprecated, but remain available by default. +These features may be disabled by setting `-allowdeprecated=none`. 18 weeks +after this release, these features will be disabled by default and the following +flags to `-allowdeprecated` will be required to permit their continued use: + +- `deprecationinfo_deprecationheight`: The `deprecationheight` field of + `getdeprecationinfo` has been deprecated and replaced by the `end_of_service` + object. + +Changelog +========= + +ANISH M (3): + use SOURCES_PATH instead of local git DEPENDS_SOURCES_DIR + report the git-derived version in metrics screen + Update src/metrics.cpp by removing v prefix. + +Alex Morcos (14): + Refactor CreateNewBlock to be a method of the BlockAssembler class + FIX: Account for txs already added to block in addPriorityTxs + FIX: correctly measure size of priority block + [rpc] Remove estimatepriority. + [mining] Remove -blockprioritysize. + [debug] Change -printpriority option + [cleanup] Remove estimatePriority + [rpc] sendrawtransaction no longer bypasses minRelayTxFee + [test] Remove priority from tests + [rpc] Remove priority information from mempool RPC calls + [rpc] Remove priorityDelta from prioritisetransaction + [cleanup] Remove coin age priority completely. + Allow setting minrelaytxfee to 0 + Update example zcash.conf + +Charlie O'Keefe (1): + Add reference in Makefile.am to zip317.h + +Daira Emma Hopwood (38): + Remove unnecessary #include. + Adjust indentation to be consistent without changing existing code. + Repair show_help RPC test. + Fix bit-rotted code in miner tests. + Implement ZIP 317 computations. + `cargo update` + Add audits for updates to futures-* 0.3.28 and redjubjub 0.7.0. + Add `examine`, a wrapper around `std::visit` that reverses the arguments. + Use the new `examine` macro to replace all instances of `std::visit(match {...}, specimen)`, improving code readability. + Refactoring to avoid duplicated code. + Refactoring to avoid an unnecessary temporary. + Refactor that avoids using exceptions for local flow control and is simpler. + Correct the documentation of `-rpcconnect` in the example `zcash.conf`. `-rpcconnect` is only used by `zcash-cli`. + Change ZIP 401 mempool limiting to use conventional fee. + Change ZIP 401 mempool limiting to use constants decided in zcash/zips#565. + Warn on node startup if the removed `-blockprioritysize` option is set to a non-zero value. + Log (at the mempool DEBUG level) when a transaction cannot be accepted to the mempool because its modified fee is below the minimum relay fee. + Fix the dust threshold rate to three times 100 zats/1000 bytes. (We express it that way rather than 300 zats/1000 bytes, because the threshold is always rounded to an integer and then multiplied by 3.) + Fix some messages, comments, and documentation that: * used "fee" to mean "fee rate", "kB" to mean 1000 bytes, "satoshis" to mean zatoshis, or that incorrectly used "BTC" in place of "ZEC"; * used obsolete concepts such as "zero fee" or "free transaction"; or * did not accurately document their applicability. + Fix tests that failed due to the minimum relay fee being enforced. + Fix miner_tests btest. + Fix mempool_packages and prioritisetransaction RPC tests. + Implement `GetUnpaidActionCount` and `GetWeightRatio` for ZIP 317. + ZIP 317 block construction algorithm. This breaks tests which are repaired in subsequent commits. + Add assertions that `GetRandInt*` functions are called with `nMax >= 0`. All existing uses have been checked to ensure they are consistent with this assertion. + Repair `miner_tests.py` btest. + Repair some RPC tests. + mergetoaddress_helper.py: Use `generate_and_check` helper to mine a block and make sure that it contains the expected number of transactions. + mergetoaddress_helper.py: use Decimal for amounts and integers otherwise. + Remove the implementation of score-based block template construction and the `-blockminsize` option. + Add a `-blockunpaidactionlimit` config option to configure the per-block limit on unpaid actions for ZIP 317 block template construction. + miner_tests.cpp improvements. + random.h documentation improvements. + Fix/suppress clippy warnings. + Improve the `show_help.py` RPC test to include `-help-debug` (needed to test the help change in the next commit). + Improve `-printpriority` output to log the modified fee, conventional fee, size, logical action count, and unpaid action count. This reflects the changes to use the ZIP 317 block construction algorithm and de-emphasise fee rate. + Fix a build regression if `--disable-mining` is selected. + Fix a build regression if both `--disable-mining` and `--disable-wallet` are selected. + +Daira Hopwood (10): + Use a more recent URL format for GitHub release archives. + Clarify that patches to a dependency are under the same license as that dependency. + Update copyright date and email for tl_expected. + Use the same convention for the tl_expected download files as for native_cctools + Refactoring to split the weighted tx tree out of mempool_limit.{cpp,h} and make it more reusable. + Minor optimization to weighted_map::remove + This PR doesn't bring in any ZIP 317 changes yet + Another minor optimization + Change spelling of prioritisation in an error message + Correction "height" -> "time" in release notes + +DeckerSU (1): + InsertBlockIndex: pass const reference on hash, instead of hash + +Dimitris Apostolou (3): + Fix typo + Update documentation link + Fix typos + +Evan Klitzke (1): + Fix automake warnings when running autogen.sh + +Greg Pfeil (89): + Show in-progress tests when rpc-tests is interrupted + Make extra newline more explicit + Apply suggestions from code review + Make pool selection order more flexible + Simplify diversifier_index_t handling + Update tests for async z_sendmany + Limit UTXOs + Some orchard fixes for wallet_tx_builder + Return anchorHeight from ResolveInputsAndPayments + Refactoring InsufficientFundsError + Ensure we don’t make Orchard change pre-NU5 + Test updates for z_sendmany WalletTxBuilder changes + Fix weakened privacy policy for transparent change + Fix some overly-strict privacy policies in btest + Apply suggestions from code review + Unify requireTransparentCoinbase handling + Rename `Get*Balance` to `Get*Total` + Remove changes that aren’t needed by z_sendmany + Improve GetRequiredPrivacyPolicy + Add release notes for (a)sync z_sendmany changes + Assert that we get a change addr for any selector + Don’t pass PrivacyPolicy to selector constructor + Address comments on WalletTxBuilder introduction + Make RPC test output more deterministic + Update WalletTxBuilder based on review + Clarify `AddressResolutionError` + Don’t permit user-provided “internal” payments + Address WalletTxBuilder PR feedback + Ensure that a WalletTxBuilder tx balances + Additional z_sendmany test cases + Address WalletTxBuilder review feedback + Apply suggestions for WalletTxBuilder from code review + Simplify SelectOVKs + Have GetRecipientPools return a copy + Remove CWallet member from WalletTxBuilder + Improve taddr no-memo check + Update src/wallet/rpcwallet.cpp + Lock notes (except Orchard) in wallet_tx_builder + Improve Doxygen for note locking + Require `AllowRevealedRecipients` for t-change + Update release-notes for transparent change restriction + Correct EditorConfig for Makefiles + Split C++ generated from Rust into own lib + Simplify canResolveOrchard logic + Support ZIP 317 fees in the zcashd wallet + Correct change handling for ZIP 317 fees + Address review feedback re: ZIP 317 in wallet + Revert "Add `AllowRevealedSenders` to fix `mempool_nu_activation.py`" + Eliminate LegacyCompat–ZTXOSelector cycle + Simplify client.cpp + Enrich zcash-cli arg conversion + Better messages on client-side zcash-cli errors + Fix accidental reversion of #6409 + Remove unnecessary explicit privacy policy + Use examine instead of std::visit + Simplify some vector initialization + Fix edge case revealed by #6409 + Rename Arg* to Param + Adjust wallet absurd fee check for ZIP 317 + Address review feedback for ZIP 317 fees in wallet + Address more ZIP 317 fee feedback + Fix zcash-cli crash when printing help message + Use null as the ZIP 317 fee sentinel instead of -1 + Add z_sendmany RPC examples with fee field + Have z_shieldcoinbase use WalletTxBuilder + Address review feedback on z_shieldcoinbase + Fix an incorrect error message in a test + Minor improvements to z_shieldcoinbase + Support ZIP 317 fees for legacy wallet operations + Remove `-mintxfee` config option + Address review comments re: legacy wallet ZIP 317 + Remove `-txconfirmtarget` config option + Restore previous `-maxtxfee` bound + Correct -maxtxfee lower bound diagnostic messages + WalletTxBuilder support for net payments + fixup! WalletTxBuilder support for net payments + Don’t test “Insufficient funds” for `z_shieldcoinbase` + Update z_mergetoaddress to use WalletTxBuilder + Many z_mergetoaddress updates + Avoid extra copy in ResolveInputsAndPayments + Address z_mergetoaddress review feedback + Allow explicit “no memo” in z_mergetoaddress + Add `memo` parameter to `z_shieldcoinbase` + Support UA destinations in `z_mergetoaddress` + Include Orchard dest in z_mergetoaddress estimation + Update release notes + Support privacyPolicy parameters in zcash-cli + Treat "F6" in RPC calls as if no memo were provided + Support nullable strings in `zcash-cli` + +Jack Grigg (92): + rust: Add `cxx` version of `RustStream` + rust: Migrate `OrchardMerkleFrontier` to `cxx` + CreateNewBlock: Leave more space for Orchard shielded coinbase + Retroactively enable ZIP 216 before NU5 activation + rust: Compile with ThinLTO + depends: Update Rust to 1.67.1 + depends: Update Clang / libcxx to LLVM 15.0.6 + Fix 1.67.1 clippy lints + depends: Evaluate `native_packages` before `packages` + qa: Fix year in postponement lines + qa: Fix `google/leveldb` tag parsing in `updatecheck.py` + qa: Handle commit IDs correctly to `updatecheck.py` + depends: `cxx 1.0.91` + depends: `native_zstd 1.5.4` + `cargo vet regenerate imports` + qa: Import Rust crate audits from ISRG + `cargo update` + qa: Postpone LevelDB 1.23 + book: Add page with release support details and EoS halt heights + Update release support book page in release process + depends: Update Rust to 1.68.0 + qa: Replace Firefox audits with aggregated Mozilla audits in registry + qa: Import Rust crate audits from ChromeOS + Move `fEnableAddrTypeField` outside `ENABLE_WALLET` + `s/string/std::string` in `init.cpp` + CI: Check that the PR branch has a sufficiently recent base for Tekton + CI: Fix permissions for Checks workflow + Add `CChainParams::RustNetwork` + wallet: Consolidate `CWalletTx` Sapling output decryption methods + wallet: Use `zcash_note_encryption` in `CWalletTx::DecryptSaplingNote` + wallet: Use `CWalletTx::DecryptSaplingNote` in more places + wallet: Use `zcash_note_encryption` in `CWallet::FindMySaplingNotes` + wallet: Use `zcash_note_encryption` in `CWalletTx::RecoverSaplingNote` + wallet: Remove recipient-side `SaplingNotePlaintext::decrypt` + CI: Fetch all history for "recent base" check + CI: Include explicit `failure()` condition in "recent base" check + CI: Use `github.head_ref` instead of `HEAD` for "recent base" check + book: Add End-of-Support heights for v5.3.3 and v5.4.2 + CI: Remove most usages of `actions-rs` actions + CI: Migrate to `cargo-vet 0.5` + cargo vet prune + CI: Provide `write` permission for `pull-requests` + CI: Check out both the base and PR branches for "recent base" check + Migrate to `zcash_primitives 0.10` + depends: `cxx 1.0.92` + depends: CMake 3.26.0 + depends: Postpone CCache updates again + cargo update + Use `RandomInvalidOutputDescription()` everywhere it makes sense + Merge most `cxx::bridge` definitions into a single bridge + Expand `CppStream` to cover all `Stream`-like C++ types + Migrate `OrchardMerkleFrontier` to use new `CppStream` APIs + build: Tolerate split LLVM versions + Use `cxx` bridge for all Orchard bundle inspection and validation + gtest: Minor improvements to `CoinsTests` + rust: Migrate Ed25519 FFI to `cxx` + Tell `cargo-vet` to ignore patched dependencies + cargo-vet: Regenerate imports + cargo-vet: Switch to Google's aggregated audits + More crate audits + Migrate to published `orchard 0.4` + qa: Fix update checker to handle `native_clang` version format + depends: CMake 3.26.3 + depends: Rust 1.68.2 + depends: `native_zstd 1.5.5` + depends: `cxx 1.0.94` + qa: Postpone dependencies we aren't updating + cargo update + Use published `zcash_primitives 0.11` and `zcash_proofs 0.11` + test: Avoid generating a chain fork in `mempool_packages` RPC test + test: Sync blocks before invalidating them in `mempool_packages` RPC test + depends: Boost 1.82.0 + qa: Postpone Clang 16.0.2 + cargo update + depends: Rust 1.69.0 + make-release.py: Versioning changes for 5.5.0-rc1. + make-release.py: Updated manpages for 5.5.0-rc1. + make-release.py: Updated release notes and changelog for 5.5.0-rc1. + make-release.py: Updated book for 5.5.0-rc1. + CI: Add a GitHub Actions workflow that builds zcashd for platform tiers + CI: Add caching to build workflow + depends: Ensure `native_cxxbridge` source is fetched for `rustcxx` + depends: Don't build BDB utilities on macOS + depends: Remove Fortran and LLDB components from staged `native_clang` + CI: Build with `--with-libs`, `--disable-wallet`, and `--disable-mining` + Fix `make-release.py` to write correct halt height into book + Update v5.5.0 release notes + build: Fix MinGW cross-compilation with `--disable-wallet` + make-release.py: Versioning changes for 5.5.0-rc2. + make-release.py: Updated manpages for 5.5.0-rc2. + make-release.py: Updated release notes and changelog for 5.5.0-rc2. + make-release.py: Updated book for 5.5.0-rc2. + +Kris Nuttycombe (33): + Fix potential path or symlink traversal + Add a docker-compose.yml for prometheus/grafana metrics collection. + Apply suggestions from code review + Make all CCoinsView methods pure-virtual. + Remove `FakeCoinsViewDB` as it is identical to `CCoinsViewDummy` + Postpone dependency updates. + make-release.py: Versioning changes for 5.3.3. + make-release.py: Updated manpages for 5.3.3. + make-release.py: Updated release notes and changelog for 5.3.3. + Set urgency to `high` in Debian changelog. + Add information about deprecated features to `deprecationinfo` results. + Apply suggestions from code review + Add a wallet-aware transaction builder. + Use WalletTxBuilder for z_sendmany + Allow selectors to require transparent coinbase + Fix a longstanding zcashd build warning + Fix `make distclean` to recursively remove `rust/gen` + Improve const-ness of CChainParams retrieval by network ID + Explicitly provide CChainParams to `EnforceNodeDeprecation` + revert broken "safe extract" functionality in golden tests. + Refactor RPC privacyPolicy handling + Update to use the `ff 0.13` dependency stack. + Add `AllowRevealedSenders` to fix `mempool_nu_activation.py` + Calculate convential fee in `CreateTransaction` before stripping sigs. + Fix formating of money strings. + Use the conventional fee for prioritisation in prioritisetransactions.py + `z_sendmany` now accepts 6 parameters, not 5. + make-release.py: Versioning changes for 5.5.0-rc3. + make-release.py: Updated manpages for 5.5.0-rc3. + make-release.py: Updated release notes and changelog for 5.5.0-rc3. + make-release.py: Updated book for 5.5.0-rc3. + make-release.py: Versioning changes for 5.5.0. + make-release.py: Updated manpages for 5.5.0. + +Luke Dashjr (1): + RPC/Mining: Restore API compatibility for prioritisetransaction + +Marco Falke (1): + wallet: Remove sendfree + +Marius Kjærstad (2): + New checkpoint at block 2000000 for mainnet + Update estimated number of transactions due to Blossom NU + +Miodrag Popović (2): + Fix for broken cross-build to Windows target on Ubuntu 22.04 and Debian 11 + Update depends/hosts/mingw32.mk to use posix variant library path + +Sean Bowe (1): + Add additional audits. + +Suhas Daftuar (4): + Add tags to mempool's mapTx indices + Fix mempool limiting for PrioritiseTransaction + Use fee deltas for determining mempool acceptance + Remove GetMinRelayFee + +TrellixVulnTeam (1): + Adding tarfile member sanitization to extractall() + +Wladimir J. van der Laan (1): + Merge #7730: Remove priority estimation + +Yasser Isa (1): + DOWNLOAD_URL dynamic in fetch-params.sh + +cronicc (1): + Fix Horizen Security contact email + +instagibbs (2): + Gave miner test values constants for less error-prone values. + Corrected values + +sasha (13): + Partially revert PR #6384, but only for URLs using a git commit hash + Download `native_cctools` and its `libtapi` to meaningful filenames + Better error messages if proof parameters aren't loaded + Enable the (existing) custom error message for the invalid checksum case + Re-download parameters if they already exist but don't have correct sums + Alias Sasha->sasha in release-notes.py to avoid authors.md split + Update `smoke_tests.py` to run against 5.5.0, using `allowdeprecated` + Use more restrictive privacy policies in `smoke_tests.py` + Use default values for `z_mergetoaddress` again + Don't hardcode 0.00001 explicitly + Change output format for `smoke_tests.py` + `DEFAULT_FEE` -> `LEGACY_DEFAULT_FEE` in `smoke_tests.py` + use `AllowRevealedRecipients` in `smoke_tests.py` case 4w + +Jack Grigg (4): + Improvements to code comments + Adjust documentation + z_mergetoaddress: Include Sapling output padding in size estimate + test: Fix copyright years in new RPC tests + +teor (2): + Replace custom zcash_script TxInputStream with RustDataStream + Change module comment in bridge.rs to doc comment + diff --git a/depend/zcash/qa/pull-tester/rpc-tests.py b/depend/zcash/qa/pull-tester/rpc-tests.py index 0852c4810..08e698807 100755 --- a/depend/zcash/qa/pull-tester/rpc-tests.py +++ b/depend/zcash/qa/pull-tester/rpc-tests.py @@ -30,6 +30,8 @@ # These tests involve enough shielded spends (consuming all CPU # cores) that we can't run them in parallel. 'mergetoaddress_sapling.py', + 'mergetoaddress_ua_nu5.py', + 'mergetoaddress_ua_sapling.py', 'wallet_shieldingcoinbase.py', ] @@ -186,6 +188,7 @@ def main(): Help text and arguments for individual test script:''', formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') + parser.add_argument('--deterministic', '-d', action='store_true', help='make the output a bit closer to deterministic in order to compare runs.') parser.add_argument('--exclude', '-x', help='specify a comma-seperated-list of scripts to exclude. Do not include the .py extension in the name.') parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests') parser.add_argument('--force', '-f', action='store_true', help='run tests even on platforms where they are disabled by default (e.g. windows).') @@ -295,10 +298,11 @@ def main(): config["environment"]["EXEEXT"], args.jobs, args.coverage, + args.deterministic, passon_args) sys.exit(not all_passed) -def run_tests(test_handler, test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=False, args=[]): +def run_tests(test_handler, test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=False, deterministic=False, args=[]): BOLD = ("","") if os.name == 'posix': # primitive formatting on supported @@ -333,22 +337,41 @@ def run_tests(test_handler, test_list, src_dir, build_dir, exeext, jobs=1, enabl job_queue = test_handler(jobs, tests_dir, test_list, flags) max_len_name = len(max(test_list, key=len)) - results = BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "PASSED", "DURATION") + BOLD[0] - for _ in range(len(test_list)): - (name, stdout, stderr, passed, duration) = job_queue.get_next() - all_passed = all_passed and passed - time_sum += duration - - print('\n' + BOLD[1] + name + BOLD[0] + ":") - print('' if passed else stdout + '\n', end='') - print('' if stderr == '' else 'stderr:\n' + stderr + '\n', end='') - print("Pass: %s%s%s, Duration: %s s\n" % (BOLD[1], passed, BOLD[0], duration)) - - results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration) - - results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(6), time_sum) + BOLD[0] - print(results) - print("\nRuntime: %s s" % (int(time.time() - time0))) + results = [] + try: + for _ in range(len(test_list)): + (name, stdout, stderr, passed, duration) = job_queue.get_next(deterministic) + all_passed = all_passed and passed + time_sum += duration + + print('\n' + BOLD[1] + name + BOLD[0] + ":") + print('' if passed else stdout + '\n', end='') + print('' if stderr == '' else 'stderr:\n' + stderr + '\n', end='') + print("Pass: %s%s%s" % (BOLD[1], passed, BOLD[0]), end='') + if deterministic: + print("\n", end='') + else: + print(", Duration: %s s" % (duration,)) + + new_result = "%s | %s" % (name.ljust(max_len_name), str(passed).ljust(6)) + if not deterministic: + new_result += (" | %s s" % (duration,)) + results.append(new_result) + except (InterruptedError, KeyboardInterrupt): + print('\nThe following tests were running when interrupted:') + for j in job_queue.jobs: + print("•", j[0]) + print('\n', end='') + + header = "%s | PASSED" % ("TEST".ljust(max_len_name),) + footer = "%s | %s" % ("ALL".ljust(max_len_name), str(all_passed).ljust(6)) + if not deterministic: + header += " | DURATION" + footer += " | %s s (accumulated)\nRuntime: %s s" % (time_sum, int(time.time() - time0)) + print( + BOLD[1] + header + BOLD[0] + "\n\n" + + "\n".join(sorted(results)) + "\n" + + BOLD[1] + footer + BOLD[0]) if coverage: coverage.report_rpc_coverage() @@ -383,7 +406,7 @@ def start_test(self, args, stdout, stderr): stdout=stdout, stderr=stderr) - def get_next(self): + def get_next(self, deterministic): while self.num_running < self.num_jobs and self.test_list: # Add tests self.num_running += 1 @@ -417,7 +440,8 @@ def get_next(self): self.num_running -= 1 self.jobs.remove(j) return name, stdout, stderr, passed, int(time.time() - time0) - print('.', end='', flush=True) + if not deterministic: + print('.', end='', flush=True) class RPCCoverage(object): diff --git a/depend/zcash/qa/rpc-tests/addressindex.py b/depend/zcash/qa/rpc-tests/addressindex.py index 3e55c4b11..6d5251e63 100755 --- a/depend/zcash/qa/rpc-tests/addressindex.py +++ b/depend/zcash/qa/rpc-tests/addressindex.py @@ -50,6 +50,7 @@ def __init__(self): def setup_network(self): base_args = [ + '-minrelaytxfee=0', '-debug', '-txindex', '-experimentalfeatures', diff --git a/depend/zcash/qa/rpc-tests/finalorchardroot.py b/depend/zcash/qa/rpc-tests/finalorchardroot.py index 4c004bc58..f40cf3758 100755 --- a/depend/zcash/qa/rpc-tests/finalorchardroot.py +++ b/depend/zcash/qa/rpc-tests/finalorchardroot.py @@ -33,6 +33,7 @@ def __init__(self): def setup_network(self, split=False): self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-txindex', # Avoid JSONRPC error: No information available about transaction '-reindex', # Required due to enabling -txindex nuparams(NU5_BRANCH_ID, 200), diff --git a/depend/zcash/qa/rpc-tests/finalsaplingroot.py b/depend/zcash/qa/rpc-tests/finalsaplingroot.py index 6d07f75f3..59909f3ef 100755 --- a/depend/zcash/qa/rpc-tests/finalsaplingroot.py +++ b/depend/zcash/qa/rpc-tests/finalsaplingroot.py @@ -33,6 +33,7 @@ def __init__(self): def setup_network(self, split=False): self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-txindex', # Avoid JSONRPC error: No information available about transaction '-reindex', # Required due to enabling -txindex nuparams(NU5_BRANCH_ID, 210), diff --git a/depend/zcash/qa/rpc-tests/fundrawtransaction.py b/depend/zcash/qa/rpc-tests/fundrawtransaction.py index 94debab79..5f3a53250 100755 --- a/depend/zcash/qa/rpc-tests/fundrawtransaction.py +++ b/depend/zcash/qa/rpc-tests/fundrawtransaction.py @@ -425,7 +425,7 @@ def run_test(self): mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) - # send 1.2 BTC to msig addr + # send 1.2 ZEC to multisig address txId = self.nodes[0].sendtoaddress(mSigObj, 1.2) self.sync_all() self.nodes[1].generate(1) diff --git a/depend/zcash/qa/rpc-tests/invalidtxrequest.py b/depend/zcash/qa/rpc-tests/invalidtxrequest.py index 8ec448f50..369bc4d9c 100755 --- a/depend/zcash/qa/rpc-tests/invalidtxrequest.py +++ b/depend/zcash/qa/rpc-tests/invalidtxrequest.py @@ -17,6 +17,9 @@ # Use the ComparisonTestFramework with 1 node: only use --testbinary. class InvalidTxRequestTest(ComparisonTestFramework): + def __init__(self): + super().__init__() + self.additional_args = ['-minrelaytxfee=0'] ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' diff --git a/depend/zcash/qa/rpc-tests/mempool_limit.py b/depend/zcash/qa/rpc-tests/mempool_limit.py index 9a3c2d5c2..874f238b5 100755 --- a/depend/zcash/qa/rpc-tests/mempool_limit.py +++ b/depend/zcash/qa/rpc-tests/mempool_limit.py @@ -10,9 +10,9 @@ get_coinbase_address, fail, start_nodes, - wait_and_assert_operationid_status, - DEFAULT_FEE + wait_and_assert_operationid_status ) +from test_framework.zip317 import conventional_fee from decimal import Decimal from time import sleep @@ -27,11 +27,11 @@ class MempoolLimit(BitcoinTestFramework): def setup_nodes(self): extra_args = [ - BASE_ARGS + ['-mempooltxcostlimit=8000'], # 2 transactions at min cost - BASE_ARGS + ['-mempooltxcostlimit=8000'], # 2 transactions at min cost - BASE_ARGS + ['-mempooltxcostlimit=8000'], # 2 transactions at min cost + BASE_ARGS + ['-mempooltxcostlimit=20000'], # 2 transactions at min cost + BASE_ARGS + ['-mempooltxcostlimit=20000'], # 2 transactions at min cost + BASE_ARGS + ['-mempooltxcostlimit=20000'], # 2 transactions at min cost # Let node 3 hold one more transaction - BASE_ARGS + ['-mempooltxcostlimit=12000'], # 3 transactions at min cost + BASE_ARGS + ['-mempooltxcostlimit=30000'], # 3 transactions at min cost ] return start_nodes(self.num_nodes, self.options.tmpdir, extra_args) @@ -67,17 +67,18 @@ def run_test(self): zaddr1 = self.nodes[0].z_getnewaddress('sapling') zaddr2 = self.nodes[0].z_getnewaddress('sapling') zaddr3 = self.nodes[0].z_getnewaddress('sapling') + fee = conventional_fee(2) print("Filling mempool...") opid1 = self.nodes[1].z_sendmany( get_coinbase_address(self.nodes[1]), - [{"address": zaddr1, "amount": Decimal('10.0') - DEFAULT_FEE}], - 1, DEFAULT_FEE, 'AllowRevealedSenders') + [{"address": zaddr1, "amount": Decimal('10.0') - fee}], + 1, fee, 'AllowRevealedSenders') wait_and_assert_operationid_status(self.nodes[1], opid1) opid2 = self.nodes[2].z_sendmany( get_coinbase_address(self.nodes[2]), - [{"address": zaddr2, "amount": Decimal('10.0') - DEFAULT_FEE}], - 1, DEFAULT_FEE, 'AllowRevealedSenders') + [{"address": zaddr2, "amount": Decimal('10.0') - fee}], + 1, fee, 'AllowRevealedSenders') wait_and_assert_operationid_status(self.nodes[2], opid2) self.sync_all() @@ -86,8 +87,8 @@ def run_test(self): print("Adding one more transaction...") opid3 = self.nodes[3].z_sendmany( get_coinbase_address(self.nodes[3]), - [{"address": zaddr3, "amount": Decimal('10.0') - DEFAULT_FEE}], - 1, DEFAULT_FEE, 'AllowRevealedSenders') + [{"address": zaddr3, "amount": Decimal('10.0') - fee}], + 1, fee, 'AllowRevealedSenders') wait_and_assert_operationid_status(self.nodes[3], opid3) # The mempools are no longer guaranteed to be in a consistent state, so we cannot sync sleep(5) @@ -105,9 +106,9 @@ def run_test(self): print("Checking mempool size reset after block mined...") self.check_mempool_sizes(0) zaddr4 = self.nodes[0].z_getnewaddress('sapling') - opid4 = self.nodes[0].z_sendmany(zaddr1, [{"address": zaddr4, "amount": Decimal('10.0') - 2*DEFAULT_FEE}], 1) + opid4 = self.nodes[0].z_sendmany(zaddr1, [{"address": zaddr4, "amount": Decimal('10.0') - 2*fee}], 1) wait_and_assert_operationid_status(self.nodes[0], opid4) - opid5 = self.nodes[0].z_sendmany(zaddr2, [{"address": zaddr4, "amount": Decimal('10.0') - 2*DEFAULT_FEE}], 1) + opid5 = self.nodes[0].z_sendmany(zaddr2, [{"address": zaddr4, "amount": Decimal('10.0') - 2*fee}], 1) wait_and_assert_operationid_status(self.nodes[0], opid5) self.sync_all() diff --git a/depend/zcash/qa/rpc-tests/mempool_nu_activation.py b/depend/zcash/qa/rpc-tests/mempool_nu_activation.py index 983021372..43c8691a3 100755 --- a/depend/zcash/qa/rpc-tests/mempool_nu_activation.py +++ b/depend/zcash/qa/rpc-tests/mempool_nu_activation.py @@ -30,10 +30,12 @@ def __init__(self): def setup_network(self): args = [ - "-checkmempool", - "-debug=mempool", - "-blockmaxsize=4000", + '-minrelaytxfee=0', + '-checkmempool', + '-debug=mempool', + '-blockmaxsize=4000', '-allowdeprecated=getnewaddress', + '-allowdeprecated=legacy_privacy', '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', nuparams(BLOSSOM_BRANCH_ID, 200), @@ -61,7 +63,7 @@ def run_test(self): node1_taddr = get_coinbase_address(self.nodes[1]) node0_zaddr = self.nodes[0].z_getnewaddress('sapling') recipients = [{'address': node0_zaddr, 'amount': Decimal('10')}] - myopid = self.nodes[1].z_sendmany(node1_taddr, recipients, 1, 0, 'AllowRevealedSenders') + myopid = self.nodes[1].z_sendmany(node1_taddr, recipients, 1, 0) print(wait_and_assert_operationid_status(self.nodes[1], myopid)) self.sync_all() self.nodes[0].generate(1) diff --git a/depend/zcash/qa/rpc-tests/mempool_packages.py b/depend/zcash/qa/rpc-tests/mempool_packages.py index 5b549a585..8084a7e91 100755 --- a/depend/zcash/qa/rpc-tests/mempool_packages.py +++ b/depend/zcash/qa/rpc-tests/mempool_packages.py @@ -16,18 +16,21 @@ sync_blocks, sync_mempools, ) +from test_framework.mininode import COIN +from test_framework.zip317 import conventional_fee def satoshi_round(amount): return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) class MempoolPackagesTest(BitcoinTestFramework): + maxorphantx = 120 def setup_network(self): base_args = [ - "-maxorphantx=1000", - "-relaypriority=0", - "-debug", - "-allowdeprecated=getnewaddress", + '-minrelaytxfee=0', + '-maxorphantx=%d' % (self.maxorphantx,), + '-debug', + '-allowdeprecated=getnewaddress', ] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, base_args)) @@ -59,7 +62,7 @@ def run_test(self): vout = utxo[0]['vout'] value = utxo[0]['amount'] - fee = Decimal("0.00005") + fee = conventional_fee(2) # 100 transactions off a confirmed tx should be fine chain = [] for i in range(100): @@ -74,24 +77,60 @@ def run_test(self): descendant_count = 1 descendant_fees = 0 descendant_size = 0 - SATOSHIS = 100000000 for x in reversed(chain): assert_equal(mempool[x]['descendantcount'], descendant_count) descendant_fees += mempool[x]['fee'] - assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees) + assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']) + assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN) descendant_size += mempool[x]['size'] assert_equal(mempool[x]['descendantsize'], descendant_size) descendant_count += 1 + # Check that descendant modified fees includes fee deltas from + # prioritisetransaction + self.nodes[0].prioritisetransaction(chain[-1], 0, 1000) + mempool = self.nodes[0].getrawmempool(True) + + descendant_fees = 0 + for x in reversed(chain): + descendant_fees += mempool[x]['fee'] + assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000) + # Adding one more transaction on to the chain should fail. try: self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1) except JSONRPCException: print("too-long-ancestor-chain successfully rejected") + # Check that prioritising a tx before it's added to the mempool works + [blockhash] = self.nodes[0].generate(1) + # Ensure that node 1 receives this block before we invalidate it. Otherwise there + # is a race between node 1 sending a getdata to node 0, and node 0 invalidating + # the block, that when triggered causes: + # - node 0 to ignore node 1's "old" getdata; + # - node 1 to timeout and disconnect node 0; + # - node 0 and node 1 to have different chain tips, so sync_blocks times out. + self.sync_all() + assert_equal(self.nodes[0].getrawmempool(True), {}) + self.nodes[0].prioritisetransaction(chain[-1], None, 2000) + self.nodes[0].invalidateblock(blockhash) + mempool = self.nodes[0].getrawmempool(True) + + descendant_fees = 0 + for x in reversed(chain): + descendant_fees += mempool[x]['fee'] + if (x == chain[-1]): + assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002)) + assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 2000) + # TODO: check that node1's mempool is as expected + # Reconsider the above block to clear the mempool again before the next test phase. + self.nodes[0].reconsiderblock(blockhash) + assert_equal(self.nodes[0].getbestblockhash(), blockhash) + assert_equal(self.nodes[0].getrawmempool(True), {}) + # TODO: test ancestor size limits # Now test descendant chain limits @@ -106,18 +145,18 @@ def run_test(self): for i in range(10): transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value}) - for i in range(1000): + for i in range(self.maxorphantx): utxo = transaction_package.pop(0) try: (txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) for j in range(10): transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value}) - if i == 998: + if i == self.maxorphantx-2: mempool = self.nodes[0].getrawmempool(True) - assert_equal(mempool[parent_transaction]['descendantcount'], 1000) + assert_equal(mempool[parent_transaction]['descendantcount'], self.maxorphantx) except JSONRPCException as e: print(e.error['message']) - assert_equal(i, 999) + assert_equal(i, self.maxorphantx-1) print("tx that would create too large descendant package successfully rejected") # TODO: check that node1's mempool is as expected @@ -127,7 +166,8 @@ def run_test(self): # Test reorg handling # First, the basics: self.nodes[0].generate(1) - sync_blocks(self.nodes) + print("syncing blocks") + sync_blocks(self.nodes, timeout=480) self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash()) self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash()) diff --git a/depend/zcash/qa/rpc-tests/mempool_reorg.py b/depend/zcash/qa/rpc-tests/mempool_reorg.py index b9af8b19c..06e9d1f5b 100755 --- a/depend/zcash/qa/rpc-tests/mempool_reorg.py +++ b/depend/zcash/qa/rpc-tests/mempool_reorg.py @@ -24,9 +24,10 @@ def __init__(self): def setup_network(self): args = [ - "-checkmempool", - "-debug=mempool", - "-allowdeprecated=getnewaddress", + '-minrelaytxfee=0', + '-checkmempool', + '-debug=mempool', + '-allowdeprecated=getnewaddress', ] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, args)) diff --git a/depend/zcash/qa/rpc-tests/mempool_resurrect_test.py b/depend/zcash/qa/rpc-tests/mempool_resurrect_test.py index c7e63a016..36012974e 100755 --- a/depend/zcash/qa/rpc-tests/mempool_resurrect_test.py +++ b/depend/zcash/qa/rpc-tests/mempool_resurrect_test.py @@ -24,7 +24,12 @@ def __init__(self): def setup_network(self): # Just need one node for this test - args = ["-checkmempool", "-debug=mempool", "-allowdeprecated=getnewaddress"] + args = [ + '-minrelaytxfee=0', + '-checkmempool', + '-debug=mempool', + '-allowdeprecated=getnewaddress', + ] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, args)) self.is_network_split = False diff --git a/depend/zcash/qa/rpc-tests/mempool_spendcoinbase.py b/depend/zcash/qa/rpc-tests/mempool_spendcoinbase.py index eeec2d561..43da9f905 100755 --- a/depend/zcash/qa/rpc-tests/mempool_spendcoinbase.py +++ b/depend/zcash/qa/rpc-tests/mempool_spendcoinbase.py @@ -30,9 +30,10 @@ def __init__(self): def setup_network(self): # Just need one node for this test args = [ - "-checkmempool", - "-debug=mempool", - "-allowdeprecated=getnewaddress", + '-minrelaytxfee=0', + '-checkmempool', + '-debug=mempool', + '-allowdeprecated=getnewaddress', ] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, args)) diff --git a/depend/zcash/qa/rpc-tests/mempool_tx_expiry.py b/depend/zcash/qa/rpc-tests/mempool_tx_expiry.py index 526328768..16c128a98 100755 --- a/depend/zcash/qa/rpc-tests/mempool_tx_expiry.py +++ b/depend/zcash/qa/rpc-tests/mempool_tx_expiry.py @@ -12,6 +12,7 @@ from test_framework.util import assert_equal, \ connect_nodes_bi, sync_blocks, start_nodes, \ wait_and_assert_operationid_status, DEFAULT_FEE +from test_framework.zip317 import conventional_fee from decimal import Decimal @@ -21,14 +22,14 @@ class MempoolTxExpiryTest(BitcoinTestFramework): def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, - [[ - "-txexpirydelta=%d" % TX_EXPIRY_DELTA, - "-debug=mempool", - "-allowdeprecated=getnewaddress", - "-allowdeprecated=z_getnewaddress", - "-allowdeprecated=z_gettotalbalance", - ]] * self.num_nodes) + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', + '-txexpirydelta=%d' % TX_EXPIRY_DELTA, + '-debug=mempool', + '-allowdeprecated=getnewaddress', + '-allowdeprecated=z_getnewaddress', + '-allowdeprecated=z_gettotalbalance', + ]] * self.num_nodes) # Test before, at, and after expiry block # chain is at block height 199 when run_test executes @@ -93,7 +94,7 @@ def run_test(self): # Create transactions blockheight = self.nodes[0].getblockchaininfo()['blocks'] - zsendamount = Decimal('1.0') - DEFAULT_FEE + zsendamount = Decimal('1.0') - conventional_fee(2) recipients = [] recipients.append({"address": z_bob, "amount": zsendamount}) myopid = self.nodes[0].z_sendmany(z_alice, recipients, 1) diff --git a/depend/zcash/qa/rpc-tests/mergetoaddress_helper.py b/depend/zcash/qa/rpc-tests/mergetoaddress_helper.py index cbaee02b5..7d1bb61e7 100755 --- a/depend/zcash/qa/rpc-tests/mergetoaddress_helper.py +++ b/depend/zcash/qa/rpc-tests/mergetoaddress_helper.py @@ -9,12 +9,12 @@ from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, connect_nodes_bi, fail, \ - initialize_chain_clean, start_node, sync_blocks, sync_mempools, \ + initialize_chain_clean, start_node, \ wait_and_assert_operationid_status, DEFAULT_FEE +from test_framework.zip317 import conventional_fee from decimal import Decimal - def assert_mergetoaddress_exception(expected_error_msg, merge_to_address_lambda): try: merge_to_address_lambda() @@ -23,19 +23,15 @@ def assert_mergetoaddress_exception(expected_error_msg, merge_to_address_lambda) except Exception as e: fail("Expected JSONRPCException. Found %s" % repr(e)) else: - fail("Expected exception: %s" % expected_error_msg) + fail("Expected exception: “%s”, but didn’t fail" % expected_error_msg) class MergeToAddressHelper: - def __init__(self, addr_type, any_zaddr, utxos_to_generate, utxos_in_tx1, utxos_in_tx2): - self.addr_type = addr_type + def __init__(self, new_address, any_zaddr): + self.new_address = new_address self.any_zaddr = [any_zaddr] self.any_zaddr_or_utxo = [any_zaddr, "ANY_TADDR"] - # utxos_to_generate, utxos_in_tx1, utxos_in_tx2 have to do with testing transaction size limits - self.utxos_to_generate = utxos_to_generate - self.utxos_in_tx1 = utxos_in_tx1 - self.utxos_in_tx2 = utxos_in_tx2 def setup_chain(self, test): print("Initializing test directory "+test.options.tmpdir) @@ -43,8 +39,8 @@ def setup_chain(self, test): def setup_network(self, test, additional_args=[]): args = [ + '-minrelaytxfee=0', '-debug=zrpcunsafe', - '-limitancestorcount=%d' % self.utxos_to_generate, '-allowdeprecated=getnewaddress', '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', @@ -61,6 +57,10 @@ def setup_network(self, test, additional_args=[]): test.sync_all() def run_test(self, test): + def generate_and_check(node, expected_transactions): + [blockhash] = node.generate(1) + assert_equal(len(node.getblock(blockhash)['tx']), expected_transactions) + print("Mining blocks...") test.nodes[0].generate(1) @@ -69,8 +69,8 @@ def run_test(self, test): test.nodes[0].generate(4) test.sync_all() walletinfo = test.nodes[0].getwalletinfo() - assert_equal(walletinfo['immature_balance'], 50) - assert_equal(walletinfo['balance'], 0) + assert_equal(walletinfo['immature_balance'], Decimal('50')) + assert_equal(walletinfo['balance'], Decimal('0')) test.sync_all() test.nodes[2].generate(1) test.nodes[2].getnewaddress() @@ -80,16 +80,16 @@ def run_test(self, test): test.sync_all() test.nodes[1].generate(101) test.sync_all() - assert_equal(test.nodes[0].getbalance(), 50) - assert_equal(test.nodes[1].getbalance(), 10) - assert_equal(test.nodes[2].getbalance(), 30) + assert_equal(test.nodes[0].getbalance(), Decimal('50')) + assert_equal(test.nodes[1].getbalance(), Decimal('10')) + assert_equal(test.nodes[2].getbalance(), Decimal('30')) # Shield the coinbase - myzaddr = test.nodes[0].z_getnewaddress(self.addr_type) + myzaddr = self.new_address(test, 0) result = test.nodes[0].z_shieldcoinbase("*", myzaddr, 0) wait_and_assert_operationid_status(test.nodes[0], result['opid']) test.sync_all() - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 2) test.sync_all() # Prepare some UTXOs and notes for merging @@ -97,14 +97,14 @@ def run_test(self, test): mytaddr2 = test.nodes[0].getnewaddress() mytaddr3 = test.nodes[0].getnewaddress() result = test.nodes[0].z_sendmany(myzaddr, [ - {'address': do_not_shield_taddr, 'amount': 10}, - {'address': mytaddr, 'amount': 10}, - {'address': mytaddr2, 'amount': 10}, - {'address': mytaddr3, 'amount': 10}, + {'address': do_not_shield_taddr, 'amount': Decimal('10')}, + {'address': mytaddr, 'amount': Decimal('10')}, + {'address': mytaddr2, 'amount': Decimal('10')}, + {'address': mytaddr3, 'amount': Decimal('10')}, ], 1, 0, 'AllowRevealedRecipients') wait_and_assert_operationid_status(test.nodes[0], result) test.sync_all() - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 2) test.sync_all() # Merging will fail because from arguments need to be in an array @@ -128,9 +128,9 @@ def run_test(self, test): "Amount out of range", lambda: test.nodes[0].z_mergetoaddress(self.any_zaddr_or_utxo, myzaddr, Decimal('21000000.00000001'))) - # Merging will fail because fee is larger than sum of UTXOs + # Merging will fail because fee is larger than `-maxtxfee` assert_mergetoaddress_exception( - "Insufficient funds, have 50.00, which is less than miners fee 999.00", + "Fee (999.00 ZEC) is greater than the maximum fee allowed by this instance (0.10 ZEC). Run zcashd with `-maxtxfee` to adjust this limit.", lambda: test.nodes[0].z_mergetoaddress(self.any_zaddr_or_utxo, myzaddr, 999)) # Merging will fail because transparent limit parameter must be at least 0 @@ -164,206 +164,183 @@ def run_test(self, test): lambda: test.nodes[0].z_mergetoaddress(["ANY_SPROUT", "ANY_SAPLING"], mytaddr)) # Merge UTXOs from node 0 of value 30, default fee - result = test.nodes[0].z_mergetoaddress([mytaddr, mytaddr2, mytaddr3], myzaddr) + result = test.nodes[0].z_mergetoaddress([mytaddr, mytaddr2, mytaddr3], myzaddr, None, None, None, None, 'AllowRevealedSenders') wait_and_assert_operationid_status(test.nodes[0], result['opid']) test.sync_all() - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 2) test.sync_all() # Confirm balances and that do_not_shield_taddr containing funds of 10 was left alone - assert_equal(test.nodes[0].getbalance(), 10) - assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) - assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('40.0') - DEFAULT_FEE) - assert_equal(test.nodes[1].getbalance(), 40) - assert_equal(test.nodes[2].getbalance(), 30) + assert_equal(test.nodes[0].getbalance(), Decimal('10')) + assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10')) + assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('40') - conventional_fee(4)) + assert_equal(test.nodes[1].getbalance(), Decimal('40')) + assert_equal(test.nodes[2].getbalance(), Decimal('30')) # Shield all notes to another z-addr - myzaddr2 = test.nodes[0].z_getnewaddress(self.addr_type) + myzaddr2 = self.new_address(test, 0) result = test.nodes[0].z_mergetoaddress(self.any_zaddr, myzaddr2, 0) - assert_equal(result["mergingUTXOs"], Decimal('0')) - assert_equal(result["remainingUTXOs"], Decimal('0')) - assert_equal(result["mergingNotes"], Decimal('2')) - assert_equal(result["remainingNotes"], Decimal('0')) + assert_equal(result["mergingUTXOs"], 0) + assert_equal(result["remainingUTXOs"], 0) + assert_equal(result["mergingNotes"], 2) + assert_equal(result["remainingNotes"], 0) wait_and_assert_operationid_status(test.nodes[0], result['opid']) test.sync_all() - blockhash = test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 2) test.sync_all() - assert_equal(len(test.nodes[0].getblock(blockhash[0])['tx']), 2) - assert_equal(test.nodes[0].z_getbalance(myzaddr), 0) - assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('40.0') - DEFAULT_FEE) + assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('0')) + assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('40') - conventional_fee(4)) # Shield coinbase UTXOs from any node 2 taddr, and set fee to 0 result = test.nodes[2].z_shieldcoinbase("*", myzaddr, 0) wait_and_assert_operationid_status(test.nodes[2], result['opid']) test.sync_all() - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 2) test.sync_all() - assert_equal(test.nodes[0].getbalance(), 10) + assert_equal(test.nodes[0].getbalance(), Decimal('10')) assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('30')) - assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('40.0') - DEFAULT_FEE) - assert_equal(test.nodes[1].getbalance(), 60) - assert_equal(test.nodes[2].getbalance(), 0) + assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('40') - conventional_fee(4)) + assert_equal(test.nodes[1].getbalance(), Decimal('60')) + assert_equal(test.nodes[2].getbalance(), Decimal('0')) # Merge all notes from node 0 into a node 0 taddr, and set fee to 0 - result = test.nodes[0].z_mergetoaddress(self.any_zaddr, mytaddr, 0) + result = test.nodes[0].z_mergetoaddress(self.any_zaddr, mytaddr, 0, None, None, None, 'AllowRevealedRecipients') wait_and_assert_operationid_status(test.nodes[0], result['opid']) test.sync_all() - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 2) test.sync_all() - assert_equal(test.nodes[0].getbalance(), Decimal('80.0') - DEFAULT_FEE) - assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) - assert_equal(test.nodes[0].z_getbalance(mytaddr), Decimal('70.0') - DEFAULT_FEE) - assert_equal(test.nodes[0].z_getbalance(myzaddr), 0) - assert_equal(test.nodes[0].z_getbalance(myzaddr2), 0) - assert_equal(test.nodes[1].getbalance(), 70) - assert_equal(test.nodes[2].getbalance(), 0) + assert_equal(test.nodes[0].getbalance(), Decimal('80') - conventional_fee(4)) + assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10')) + assert_equal(test.nodes[0].z_getbalance(mytaddr), Decimal('70') - conventional_fee(4)) + assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('0')) + assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('0')) + assert_equal(test.nodes[1].getbalance(), Decimal('70')) + assert_equal(test.nodes[2].getbalance(), Decimal('0')) # Merge all node 0 UTXOs together into a node 1 taddr, and set fee to 0 test.nodes[1].getnewaddress() # Ensure we have an empty address n1taddr = test.nodes[1].getnewaddress() - result = test.nodes[0].z_mergetoaddress(["ANY_TADDR"], n1taddr, 0) + result = test.nodes[0].z_mergetoaddress(["ANY_TADDR"], n1taddr, 0, None, None, None, 'AllowFullyTransparent') wait_and_assert_operationid_status(test.nodes[0], result['opid']) test.sync_all() - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 2) test.sync_all() + assert_equal(0, len(test.nodes[0].z_listunspent(0))) - assert_equal(test.nodes[0].getbalance(), 0) - assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), 0) - assert_equal(test.nodes[0].z_getbalance(mytaddr), 0) - assert_equal(test.nodes[0].z_getbalance(myzaddr), 0) - assert_equal(test.nodes[1].getbalance(), Decimal('160.0') - DEFAULT_FEE) - assert_equal(test.nodes[1].z_getbalance(n1taddr), Decimal('80.0') - DEFAULT_FEE) - assert_equal(test.nodes[2].getbalance(), 0) + assert_equal(test.nodes[0].getbalance(), Decimal('0')) + assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('0')) + assert_equal(test.nodes[0].z_getbalance(mytaddr), Decimal('0')) + assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('0')) + assert_equal(test.nodes[1].getbalance(), Decimal('160') - conventional_fee(4)) + assert_equal(test.nodes[1].z_getbalance(n1taddr), Decimal('80') - conventional_fee(4)) + assert_equal(test.nodes[2].getbalance(), Decimal('0')) - # Generate self.utxos_to_generate regular UTXOs on node 0, and 20 regular UTXOs on node 2 + # Generate 5 regular UTXOs on node 0, and 20 regular UTXOs on node 2. mytaddr = test.nodes[0].getnewaddress() n2taddr = test.nodes[2].getnewaddress() - test.nodes[1].generate(1000) + test.nodes[1].generate(20) test.sync_all() - for i in range(self.utxos_to_generate): - test.nodes[1].sendtoaddress(mytaddr, 1) + for i in range(5): + test.nodes[1].sendtoaddress(mytaddr, Decimal('1')) for i in range(20): - test.nodes[1].sendtoaddress(n2taddr, 1) - test.nodes[1].generate(1) + test.nodes[1].sendtoaddress(n2taddr, Decimal('1')) + generate_and_check(test.nodes[1], 26) test.sync_all() - # Merging the UTXOs will conditionally occur over two transactions, since max tx size is 100,000 bytes before Sapling and 2,000,000 after. - # We don't verify mergingTransparentValue as UTXOs are not selected in any specific order, so value can change on each test run. - # We set an unrealistically high limit parameter of 99999, to verify that max tx size will constrain the number of UTXOs. - result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, 0, 99999) - assert_equal(result["mergingUTXOs"], self.utxos_in_tx1) - assert_equal(result["remainingUTXOs"], self.utxos_in_tx2) - assert_equal(result["mergingNotes"], Decimal('0')) - assert_equal(result["mergingShieldedValue"], Decimal('0')) - assert_equal(result["remainingNotes"], Decimal('0')) - assert_equal(result["remainingShieldedValue"], Decimal('0')) - remainingTransparentValue = result["remainingTransparentValue"] + # This z_mergetoaddress and the one below result in two notes in myzaddr. + result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, DEFAULT_FEE, None, None, None, 'AllowRevealedSenders') + assert_equal(result["mergingUTXOs"], 5) + assert_equal(result["remainingUTXOs"], 0) + assert_equal(result["mergingNotes"], 0) + assert_equal(result["remainingNotes"], 0) wait_and_assert_operationid_status(test.nodes[0], result['opid']) - # For sapling we do not check that this occurs over two transactions because of the time that it would take - if self.utxos_in_tx2 > 0: - # Verify that UTXOs are locked (not available for selection) by queuing up another merging operation - result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, 0, 0) - assert_equal(result["mergingUTXOs"], self.utxos_in_tx2) - assert_equal(result["mergingTransparentValue"], Decimal(remainingTransparentValue)) - assert_equal(result["remainingUTXOs"], Decimal('0')) - assert_equal(result["remainingTransparentValue"], Decimal('0')) - assert_equal(result["mergingNotes"], Decimal('0')) - assert_equal(result["mergingShieldedValue"], Decimal('0')) - assert_equal(result["remainingNotes"], Decimal('0')) - assert_equal(result["remainingShieldedValue"], Decimal('0')) - wait_and_assert_operationid_status(test.nodes[0], result['opid']) - - # sync_all() invokes sync_mempool() but node 2's mempool limit will cause tx1 and tx2 to be rejected. - # So instead, we sync on blocks and mempool for node 0 and node 1, and after a new block is generated - # which mines tx1 and tx2, all nodes will have an empty mempool which can then be synced. - sync_blocks(test.nodes[:2]) - sync_mempools(test.nodes[:2]) - # Generate enough blocks to ensure all transactions are mined - while test.nodes[1].getmempoolinfo()['size'] > 0: - test.nodes[1].generate(1) - test.sync_all() - - # Verify maximum number of UTXOs which node 2 can shield is not limited - # when the limit parameter is set to 0. - expected_to_merge = 20 - expected_remaining = 0 - - result = test.nodes[2].z_mergetoaddress([n2taddr], myzaddr, DEFAULT_FEE, 0) - assert_equal(result["mergingUTXOs"], expected_to_merge) - assert_equal(result["remainingUTXOs"], expected_remaining) - assert_equal(result["mergingNotes"], Decimal('0')) - assert_equal(result["remainingNotes"], Decimal('0')) + # Verify maximum number of UTXOs is not limited when the limit parameter is set to 0. + result = test.nodes[2].z_mergetoaddress([n2taddr], myzaddr, DEFAULT_FEE, 0, None, None, 'AllowRevealedSenders') + assert_equal(result["mergingUTXOs"], 20) + assert_equal(result["remainingUTXOs"], 0) + assert_equal(result["mergingNotes"], 0) + assert_equal(result["remainingNotes"], 0) wait_and_assert_operationid_status(test.nodes[2], result['opid']) test.sync_all() - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 3) test.sync_all() + assert_equal(2, len(test.nodes[0].z_listunspent())) # Verify maximum number of UTXOs which node 0 can shield is set by default limit parameter of 50 mytaddr = test.nodes[0].getnewaddress() for i in range(100): test.nodes[1].sendtoaddress(mytaddr, 1) - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 101) test.sync_all() - result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, DEFAULT_FEE) - assert_equal(result["mergingUTXOs"], Decimal('50')) - assert_equal(result["remainingUTXOs"], Decimal('50')) - assert_equal(result["mergingNotes"], Decimal('0')) + result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, conventional_fee(52), None, None, None, 'AllowRevealedSenders') + assert_equal(result["mergingUTXOs"], 50) + assert_equal(result["remainingUTXOs"], 50) + assert_equal(result["mergingNotes"], 0) # Remaining notes are only counted if we are trying to merge any notes - assert_equal(result["remainingNotes"], Decimal('0')) + assert_equal(result["remainingNotes"], 0) wait_and_assert_operationid_status(test.nodes[0], result['opid']) + assert_equal(2, len(test.nodes[0].z_listunspent())) + assert_equal(3, len(test.nodes[0].z_listunspent(0))) + # Verify maximum number of UTXOs which node 0 can shield can be set by the limit parameter - result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, DEFAULT_FEE, 33) - assert_equal(result["mergingUTXOs"], Decimal('33')) - assert_equal(result["remainingUTXOs"], Decimal('17')) - assert_equal(result["mergingNotes"], Decimal('0')) + result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, conventional_fee(35), 33, None, None, 'AllowRevealedSenders') + assert_equal(result["mergingUTXOs"], 33) + assert_equal(result["remainingUTXOs"], 17) + assert_equal(result["mergingNotes"], 0) # Remaining notes are only counted if we are trying to merge any notes - assert_equal(result["remainingNotes"], Decimal('0')) + assert_equal(result["remainingNotes"], 0) wait_and_assert_operationid_status(test.nodes[0], result['opid']) - # Don't sync node 2 which rejects the tx due to its mempooltxinputlimit - sync_blocks(test.nodes[:2]) - sync_mempools(test.nodes[:2]) - test.nodes[1].generate(1) - test.sync_all() - - # Verify maximum number of notes which node 0 can shield can be set by the limit parameter - # Also check that we can set off a second merge before the first one is complete - - # myzaddr will have 5 notes if testing before to Sapling activation and 4 otherwise - num_notes = len(test.nodes[0].z_listunspent(0)) - result1 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, DEFAULT_FEE, 50, 2) - result2 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, DEFAULT_FEE, 50, 2) - - # First merge should select from all notes - assert_equal(result1["mergingUTXOs"], Decimal('0')) - # Remaining UTXOs are only counted if we are trying to merge any UTXOs - assert_equal(result1["remainingUTXOs"], Decimal('0')) - assert_equal(result1["mergingNotes"], Decimal('2')) - assert_equal(result1["remainingNotes"], num_notes - 2) - - # Second merge should ignore locked notes - assert_equal(result2["mergingUTXOs"], Decimal('0')) - assert_equal(result2["remainingUTXOs"], Decimal('0')) - assert_equal(result2["mergingNotes"], Decimal('2')) - assert_equal(result2["remainingNotes"], num_notes - 4) - wait_and_assert_operationid_status(test.nodes[0], result1['opid']) - wait_and_assert_operationid_status(test.nodes[0], result2['opid']) - test.sync_all() - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 3) test.sync_all() + assert_equal(4, len(test.nodes[0].z_listunspent())) + + # NB: We can’t yet merge from UAs, so ensure we’re not before running these cases + if (myzaddr[0] != 'u'): + # Also check that we can set off a second merge before the first one is complete + result1 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, DEFAULT_FEE, 50, 2) + + # First merge should select from all notes + assert_equal(result1["mergingUTXOs"], 0) + # Remaining UTXOs are only counted if we are trying to merge any UTXOs + assert_equal(result1["remainingUTXOs"], 0) + assert_equal(result1["mergingNotes"], 2) + assert_equal(result1["remainingNotes"], 2) + + # Second merge should ignore locked notes + result2 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, DEFAULT_FEE, 50, 2) + assert_equal(result2["mergingUTXOs"], 0) + assert_equal(result2["remainingUTXOs"], 0) + assert_equal(result2["mergingNotes"], 2) + assert_equal(result2["remainingNotes"], 0) + wait_and_assert_operationid_status(test.nodes[0], result1['opid']) + wait_and_assert_operationid_status(test.nodes[0], result2['opid']) + + test.sync_all() + generate_and_check(test.nodes[1], 3) + test.sync_all() + + # Shield both UTXOs and notes to a z-addr + result = test.nodes[0].z_mergetoaddress(self.any_zaddr_or_utxo, myzaddr, DEFAULT_FEE, 10, 2, None, 'AllowRevealedSenders') + assert_equal(result["mergingUTXOs"], 10) + assert_equal(result["remainingUTXOs"], 7) + assert_equal(result["mergingNotes"], 2) + assert_equal(result["remainingNotes"], 0) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) + else: + # Shield both UTXOs and notes to a z-addr + result = test.nodes[0].z_mergetoaddress(self.any_zaddr_or_utxo, myzaddr, DEFAULT_FEE, 10, 2, None, 'AllowRevealedSenders') + assert_equal(result["mergingUTXOs"], 10) + assert_equal(result["remainingUTXOs"], 7) + assert_equal(result["mergingNotes"], 2) + assert_equal(result["remainingNotes"], 2) + wait_and_assert_operationid_status(test.nodes[0], result['opid']) - # Shield both UTXOs and notes to a z-addr - result = test.nodes[0].z_mergetoaddress(self.any_zaddr_or_utxo, myzaddr, 0, 10, 2) - assert_equal(result["mergingUTXOs"], Decimal('10')) - assert_equal(result["remainingUTXOs"], Decimal('7')) - assert_equal(result["mergingNotes"], Decimal('2')) - assert_equal(result["remainingNotes"], num_notes - 4) - wait_and_assert_operationid_status(test.nodes[0], result['opid']) test.sync_all() - test.nodes[1].generate(1) + generate_and_check(test.nodes[1], 2) test.sync_all() diff --git a/depend/zcash/qa/rpc-tests/mergetoaddress_mixednotes.py b/depend/zcash/qa/rpc-tests/mergetoaddress_mixednotes.py index 0217bb047..968d5b5fb 100755 --- a/depend/zcash/qa/rpc-tests/mergetoaddress_mixednotes.py +++ b/depend/zcash/qa/rpc-tests/mergetoaddress_mixednotes.py @@ -18,8 +18,10 @@ def __init__(self): def setup_nodes(self): self.num_nodes = 4 return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-anchorconfirmations=1', '-allowdeprecated=getnewaddress', + '-allowdeprecated=legacy_privacy', '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', '-allowdeprecated=z_gettotalbalance', diff --git a/depend/zcash/qa/rpc-tests/mergetoaddress_sapling.py b/depend/zcash/qa/rpc-tests/mergetoaddress_sapling.py index 80c6d9a17..1cdeec6fc 100755 --- a/depend/zcash/qa/rpc-tests/mergetoaddress_sapling.py +++ b/depend/zcash/qa/rpc-tests/mergetoaddress_sapling.py @@ -6,17 +6,19 @@ from test_framework.test_framework import BitcoinTestFramework from mergetoaddress_helper import MergeToAddressHelper +def get_new_address(test, node): + return test.nodes[node].z_getnewaddress('sapling') class MergeToAddressSapling (BitcoinTestFramework): - # 13505 would be the maximum number of utxos based on the transaction size limits for Sapling - # but testing this causes the test to take an indeterminately long time to run. - helper = MergeToAddressHelper('sapling', 'ANY_SAPLING', 800, 800, 0) + helper = MergeToAddressHelper(get_new_address, 'ANY_SAPLING') def setup_chain(self): self.helper.setup_chain(self) def setup_network(self, split=False): - self.helper.setup_network(self, ['-anchorconfirmations=1']) + self.helper.setup_network(self, [ + '-anchorconfirmations=1', + ]) def run_test(self): self.helper.run_test(self) diff --git a/depend/zcash/qa/rpc-tests/mergetoaddress_ua_nu5.py b/depend/zcash/qa/rpc-tests/mergetoaddress_ua_nu5.py new file mode 100755 index 000000000..d9f0a7b28 --- /dev/null +++ b/depend/zcash/qa/rpc-tests/mergetoaddress_ua_nu5.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php . + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import NU5_BRANCH_ID, nuparams +from mergetoaddress_helper import MergeToAddressHelper + +def get_new_address(test, node): + account = test.nodes[node].z_getnewaccount()['account'] + # TODO: Since we can’t merge from Orchard, the existing tests don’t work if we merge _to_ + # Orchard, so exclude it from the UA for now. + return test.nodes[node].z_getaddressforaccount(account, ['p2pkh', 'sapling'])['address'] + +class MergeToAddressUANU5 (BitcoinTestFramework): + # TODO: Until we can merge from Orchard, we just use 'ANY_SAPLING' as the wildcard here, since + # we don’t have an `'ANY_ORCHARD'` yet and `'ANY_SPROUT'` isn’t compatible with Orchard. + helper = MergeToAddressHelper(get_new_address, 'ANY_SAPLING') + + def setup_chain(self): + self.helper.setup_chain(self) + + def setup_network(self, split=False): + self.helper.setup_network(self, [ + '-anchorconfirmations=1', + nuparams(NU5_BRANCH_ID, 109), + ]) + + def run_test(self): + self.helper.run_test(self) + + +if __name__ == '__main__': + MergeToAddressUANU5().main() diff --git a/depend/zcash/qa/rpc-tests/mergetoaddress_ua_sapling.py b/depend/zcash/qa/rpc-tests/mergetoaddress_ua_sapling.py new file mode 100755 index 000000000..1a67b59fb --- /dev/null +++ b/depend/zcash/qa/rpc-tests/mergetoaddress_ua_sapling.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php . + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import NU5_BRANCH_ID, nuparams +from mergetoaddress_helper import MergeToAddressHelper + +def get_new_address(test, node): + account = test.nodes[node].z_getnewaccount()['account'] + return test.nodes[node].z_getaddressforaccount(account)['address'] + +class MergeToAddressUASapling (BitcoinTestFramework): + helper = MergeToAddressHelper(get_new_address, 'ANY_SAPLING') + + def setup_chain(self): + self.helper.setup_chain(self) + + def setup_network(self, split=False): + self.helper.setup_network(self, [ + '-anchorconfirmations=1', + nuparams(NU5_BRANCH_ID, 99999), + ]) + + def run_test(self): + self.helper.run_test(self) + + +if __name__ == '__main__': + MergeToAddressUASapling().main() diff --git a/depend/zcash/qa/rpc-tests/merkle_blocks.py b/depend/zcash/qa/rpc-tests/merkle_blocks.py index f08f74580..9778b7d58 100755 --- a/depend/zcash/qa/rpc-tests/merkle_blocks.py +++ b/depend/zcash/qa/rpc-tests/merkle_blocks.py @@ -24,8 +24,9 @@ def __init__(self): def setup_network(self): base_args = [ - "-debug", - "-allowdeprecated=getnewaddress", + '-minrelaytxfee=0', + '-debug', + '-allowdeprecated=getnewaddress', ] self.nodes = [] # Nodes 0/1 are "wallet" nodes diff --git a/depend/zcash/qa/rpc-tests/mining_shielded_coinbase.py b/depend/zcash/qa/rpc-tests/mining_shielded_coinbase.py index 203c78ba1..118ec4f9f 100755 --- a/depend/zcash/qa/rpc-tests/mining_shielded_coinbase.py +++ b/depend/zcash/qa/rpc-tests/mining_shielded_coinbase.py @@ -30,15 +30,16 @@ def __init__(self): def start_node_with(self, index, extra_args=[]): args = [ + '-minrelaytxfee=0', nuparams(BLOSSOM_BRANCH_ID, 1), nuparams(HEARTWOOD_BRANCH_ID, 10), nuparams(CANOPY_BRANCH_ID, 20), nuparams(NU5_BRANCH_ID, 20), - "-nurejectoldversions=false", - "-allowdeprecated=getnewaddress", - "-allowdeprecated=z_getnewaddress", - "-allowdeprecated=z_getbalance", - "-allowdeprecated=z_gettotalbalance", + '-nurejectoldversions=false', + '-allowdeprecated=getnewaddress', + '-allowdeprecated=z_getnewaddress', + '-allowdeprecated=z_getbalance', + '-allowdeprecated=z_gettotalbalance', ] return start_node(index, self.options.tmpdir, args + extra_args) diff --git a/depend/zcash/qa/rpc-tests/orchard_reorg.py b/depend/zcash/qa/rpc-tests/orchard_reorg.py index aad4a4a57..420118393 100755 --- a/depend/zcash/qa/rpc-tests/orchard_reorg.py +++ b/depend/zcash/qa/rpc-tests/orchard_reorg.py @@ -32,6 +32,7 @@ def __init__(self): def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(BLOSSOM_BRANCH_ID, 1), nuparams(HEARTWOOD_BRANCH_ID, 5), nuparams(CANOPY_BRANCH_ID, 5), diff --git a/depend/zcash/qa/rpc-tests/p2p_txexpiringsoon.py b/depend/zcash/qa/rpc-tests/p2p_txexpiringsoon.py index 2e53f9b43..c0c127e5d 100755 --- a/depend/zcash/qa/rpc-tests/p2p_txexpiringsoon.py +++ b/depend/zcash/qa/rpc-tests/p2p_txexpiringsoon.py @@ -21,6 +21,7 @@ def __init__(self): def setup_network(self): self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-allowdeprecated=getnewaddress', ]] * self.num_nodes) connect_nodes_bi(self.nodes, 0, 1) diff --git a/depend/zcash/qa/rpc-tests/prioritisetransaction.py b/depend/zcash/qa/rpc-tests/prioritisetransaction.py index 72634b3ef..be2bab11d 100755 --- a/depend/zcash/qa/rpc-tests/prioritisetransaction.py +++ b/depend/zcash/qa/rpc-tests/prioritisetransaction.py @@ -4,146 +4,143 @@ # file COPYING or https://www.opensource.org/licenses/mit-license.php . from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import ( - assert_equal, - connect_nodes, - initialize_chain_clean, - start_node, - sync_blocks, - sync_mempools, -) +from test_framework.util import assert_equal, start_nodes from test_framework.mininode import COIN +from test_framework.zip317 import DEFAULT_BLOCK_UNPAID_ACTION_LIMIT, MARGINAL_FEE, ZIP_317_FEE import time +from decimal import Decimal -class PrioritiseTransactionTest (BitcoinTestFramework): +class PrioritiseTransactionTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.cache_behavior = 'clean' - def setup_chain(self): - print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 4) - - def setup_network(self, split=False): - self.nodes = [] - # Start nodes with tiny block size of 11kb + def setup_nodes(self): args = [ - "-blockprioritysize=7000", - "-blockmaxsize=11000", - "-maxorphantx=1000", - "-relaypriority=true", - "-printpriority=1", - "-limitancestorcount=900", - "-allowdeprecated=getnewaddress", + '-paytxfee=0.000001', + '-printpriority=1', + '-allowdeprecated=getnewaddress', ] - self.nodes.append(start_node(0, self.options.tmpdir, args)) - self.nodes.append(start_node(1, self.options.tmpdir, args)) - connect_nodes(self.nodes[1], 0) - self.is_network_split=False + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[ + args, + args + ['-blockunpaidactionlimit=25'], + args, + args, + ]) + + def run_test(self): + # Slow start is switched off for regtest, and we're before Blossom, + # but the halving interval is only 144 blocks. + + # For the first test the miner subsidy is 10 ZEC. + self.test(self.nodes[0], Decimal("10"), DEFAULT_BLOCK_UNPAID_ACTION_LIMIT) + assert_equal(152, self.nodes[0].getblockcount()) + + # For the second test the miner subsidy is 6.25 ZEC. + # (The Founders' Reward has expired and there are no funding streams.) + self.test(self.nodes[1], Decimal("6.25"), 25) + + def test(self, mining_node, miner_subsidy, block_unpaid_action_limit): + print("Testing with -blockunpaidactionlimit=%d" % (block_unpaid_action_limit,)) + + def in_template(block_template, txid): + res = any([tx['hash'] == txid for tx in block_template['transactions']]) + print("Checking block template... %s" % (res,)) + return res + + def eventually_in_template(txid): + for tries in range(2): + time.sleep(11) + block_template = mining_node.getblocktemplate() + if in_template(block_template, txid): return True + return False + + # Make sure we have enough mature funds on mining_node. + blocks = 100 + block_unpaid_action_limit + 1 + print("Mining %d blocks..." % (blocks,)) + mining_node.generate(blocks) self.sync_all() - def run_test (self): - # tx priority is calculated: priority = sum(input_value_in_base_units * input_age)/size_in_bytes - - print("Mining 11kb blocks...") - self.nodes[0].generate(501) - - base_fee = self.nodes[0].getnetworkinfo()['relayfee'] - - # 11 kb blocks will only hold about 50 txs, so this will fill mempool with older txs - taddr = self.nodes[1].getnewaddress() - for _ in range(900): - self.nodes[0].sendtoaddress(taddr, 0.1) - self.nodes[0].generate(1) - sync_blocks(self.nodes) - # With a rate of either 7tx/s or 14tx/s per peer (depending on whether - # the connection is inbound or outbound), syncing this many transactions - # could take up to 128s. So use a higher timeout on the mempool sync. - sync_mempools(self.nodes, timeout=200) - - # Create tx of lower value to be prioritized on node 0 - # Older transactions get mined first, so this lower value, newer tx is unlikely to be mined without prioritisation - priority_tx_0 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) - - # Check that priority_tx_0 is not in block_template() prior to prioritisation - block_template = self.nodes[0].getblocktemplate() - in_block_template = False - for tx in block_template['transactions']: - if tx['hash'] == priority_tx_0: - in_block_template = True - break - assert_equal(in_block_template, False) - - priority_success = self.nodes[0].prioritisetransaction(priority_tx_0, 1000, int(3 * base_fee * COIN)) + node2_initial_balance = self.nodes[2].getbalance() + node3_initial_balance = self.nodes[3].getbalance() + + # Create a tx that will not be mined unless prioritised. + # We spend `block_unpaid_action_limit` mining rewards, ensuring that + # tx has exactly `block_unpaid_action_limit + 1` logical actions, + # because one extra input will be needed to pay the fee. + # Since we've set -paytxfee to pay only the relay fee rate, the fee + # will be less than the marginal fee, so these are all unpaid actions. + amount = miner_subsidy * block_unpaid_action_limit + assert_equal(amount + miner_subsidy, mining_node.getbalance()) + tx = mining_node.sendtoaddress(self.nodes[2].getnewaddress(), amount) + + mempool = mining_node.getrawmempool(True) + assert(tx in mempool) + fee_zats = int(mempool[tx]['fee'] * COIN) + assert(fee_zats < MARGINAL_FEE) + tx_verbose = mining_node.getrawtransaction(tx, 1) + assert_equal(block_unpaid_action_limit + 1, len(tx_verbose['vin'])) + + # Check that tx is not in a new block template prior to prioritisation. + block_template = mining_node.getblocktemplate() + assert_equal(in_template(block_template, tx), False) + + def send_fully_paid_transaction(): + # Sending a new transaction will make getblocktemplate refresh within 5s. + # We use z_sendmany so that it will pay the conventional fee (ignoring -paytxfee) + # and not take up an unpaid action. + recipients = [{'address': self.nodes[3].getnewaddress(), 'amount': Decimal("0.1")}] + mining_node.z_sendmany('ANY_TADDR', recipients, 0, ZIP_317_FEE, 'AllowFullyTransparent') + + # Prioritising it on node 2 has no effect on mining_node. + self.sync_all() + priority_success = self.nodes[2].prioritisetransaction(tx, 0, MARGINAL_FEE) assert(priority_success) - - # Check that prioritized transaction is not in getblocktemplate() - # (not updated because no new txns) - in_block_template = False - block_template = self.nodes[0].getblocktemplate() - for tx in block_template['transactions']: - if tx['hash'] == priority_tx_0: - in_block_template = True - break - assert_equal(in_block_template, False) - - # Sending a new transaction will make getblocktemplate refresh within 10s - self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) - - # Check that prioritized transaction is not in getblocktemplate() - # (too soon) - in_block_template = False - block_template = self.nodes[0].getblocktemplate() - for tx in block_template['transactions']: - if tx['hash'] == priority_tx_0: - in_block_template = True - break - assert_equal(in_block_template, False) - - # Check that prioritized transaction is in getblocktemplate() - # getblocktemplate() will refresh after 1 min, or after 10 sec if new transaction is added to mempool - # Mempool is probed every 10 seconds. We'll give getblocktemplate() a maximum of 30 seconds to refresh - block_template = self.nodes[0].getblocktemplate() - start = time.time(); - in_block_template = False - while in_block_template == False: - for tx in block_template['transactions']: - if tx['hash'] == priority_tx_0: - in_block_template = True - break - if time.time() - start > 30: - raise AssertionError("Test timed out because prioritised transaction was not returned by getblocktemplate within 30 seconds.") - time.sleep(1) - block_template = self.nodes[0].getblocktemplate() - - assert(in_block_template) - - # Node 1 doesn't get the next block, so this *shouldn't* be mined despite being prioritized on node 1 - priority_tx_1 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 0.1) - self.nodes[1].prioritisetransaction(priority_tx_1, 1000, int(3 * base_fee * COIN)) - - # Mine block on node 0 - blk_hash = self.nodes[0].generate(1) - block = self.nodes[0].getblock(blk_hash[0]) + mempool = self.nodes[2].getrawmempool(True) + assert_equal(fee_zats + MARGINAL_FEE, mempool[tx]['modifiedfee'] * COIN) self.sync_all() + send_fully_paid_transaction() + assert_equal(eventually_in_template(tx), False) - # Check that priority_tx_0 was mined - mempool = self.nodes[0].getrawmempool() - assert_equal(priority_tx_0 in block['tx'], True) - assert_equal(priority_tx_0 in mempool, False) - - # Check that priority_tx_1 was not mined - assert_equal(priority_tx_1 in mempool, True) - assert_equal(priority_tx_1 in block['tx'], False) - - # Mine a block on node 1 and sync - blk_hash_1 = self.nodes[1].generate(1) - block_1 = self.nodes[1].getblock(blk_hash_1[0]) + # Now prioritise it on mining_node, but short by one zatoshi. + priority_success = mining_node.prioritisetransaction(tx, 0, MARGINAL_FEE - fee_zats - 1) + assert(priority_success) + mempool = mining_node.getrawmempool(True) + assert_equal(MARGINAL_FEE - 1, mempool[tx]['modifiedfee'] * COIN) + send_fully_paid_transaction() + assert_equal(eventually_in_template(tx), False) + + # Finally, prioritise it on mining_node by the one extra zatoshi (this also checks + # that prioritisation is cumulative). + priority_success = mining_node.prioritisetransaction(tx, 0, 1) + assert(priority_success) + mempool = mining_node.getrawmempool(True) + assert_equal(MARGINAL_FEE, mempool[tx]['modifiedfee'] * COIN) + + # The block template will refresh after 1 minute, or after 5 seconds if a new + # transaction is added to the mempool. As long as there is less than a minute + # between the getblocktemplate() calls, it should not have been updated yet. + block_template = mining_node.getblocktemplate() + assert_equal(in_template(block_template, tx), False) + + # Check that the prioritised transaction eventually gets into a new block template. + send_fully_paid_transaction() + assert_equal(eventually_in_template(tx), True) + + # Mine a block on node 0. + blk_hash = mining_node.generate(1) + block = mining_node.getblock(blk_hash[0]) + assert_equal(tx in block['tx'], True) self.sync_all() - # Check to see if priority_tx_1 is now mined - mempool_1 = self.nodes[1].getrawmempool() - assert_equal(priority_tx_1 in mempool_1, False) - assert_equal(priority_tx_1 in block_1['tx'], True) + # Check that tx was mined and that node 1 received the funds. + mempool = mining_node.getrawmempool() + assert_equal(mempool, []) + assert_equal(self.nodes[2].getbalance(), node2_initial_balance + amount) + # Check that all of the fully paid transactions were mined. + assert_equal(self.nodes[3].getbalance(), node3_initial_balance + Decimal("0.3")) if __name__ == '__main__': PrioritiseTransactionTest().main() diff --git a/depend/zcash/qa/rpc-tests/pruning.py b/depend/zcash/qa/rpc-tests/pruning.py index eb459e428..f4994f4ae 100755 --- a/depend/zcash/qa/rpc-tests/pruning.py +++ b/depend/zcash/qa/rpc-tests/pruning.py @@ -66,7 +66,7 @@ def setup_network(self): self.address[0] = self.nodes[0].getnewaddress() self.address[1] = self.nodes[1].getnewaddress() - # Determine default relay fee + # Determine default relay fee rate self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"] connect_nodes(self.nodes[0], 1) diff --git a/depend/zcash/qa/rpc-tests/rawtransactions.py b/depend/zcash/qa/rpc-tests/rawtransactions.py index 4686a5b1a..d97eaea25 100755 --- a/depend/zcash/qa/rpc-tests/rawtransactions.py +++ b/depend/zcash/qa/rpc-tests/rawtransactions.py @@ -115,7 +115,7 @@ def run_test(self): #use balance deltas instead of absolute values bal = self.nodes[2].getbalance() - # send 1.2 BTC to msig adr + # send 1.2 ZEC to multisig address txId = self.nodes[0].sendtoaddress(mSigObj, 1.2); self.sync_all() self.nodes[0].generate(1) diff --git a/depend/zcash/qa/rpc-tests/regtest_signrawtransaction.py b/depend/zcash/qa/rpc-tests/regtest_signrawtransaction.py index 61187ff66..1d9a66131 100755 --- a/depend/zcash/qa/rpc-tests/regtest_signrawtransaction.py +++ b/depend/zcash/qa/rpc-tests/regtest_signrawtransaction.py @@ -29,11 +29,11 @@ def run_test(self): self.sync_all() # Create and sign Sapling transaction. - # If the incorrect consensus branch id is selected, there will be a signing error. + # If the incorrect consensus branch id is selected, there will be a signing error. opid = self.nodes[1].z_sendmany( taddr, [{'address': zaddr1, 'amount': 1}], - 1, DEFAULT_FEE, 'AllowRevealedSenders') + 1, DEFAULT_FEE, 'AllowFullyTransparent') wait_and_assert_operationid_status(self.nodes[1], opid) if __name__ == '__main__': diff --git a/depend/zcash/qa/rpc-tests/remove_sprout_shielding.py b/depend/zcash/qa/rpc-tests/remove_sprout_shielding.py index a54a0b9dd..99d82f7f0 100755 --- a/depend/zcash/qa/rpc-tests/remove_sprout_shielding.py +++ b/depend/zcash/qa/rpc-tests/remove_sprout_shielding.py @@ -21,7 +21,8 @@ import logging HAS_CANOPY = [ - '-nurejectoldversions=false', + '-minrelaytxfee=0', + '-nurejectoldversions=false', '-anchorconfirmations=1', nuparams(BLOSSOM_BRANCH_ID, 205), nuparams(HEARTWOOD_BRANCH_ID, 210), @@ -33,80 +34,96 @@ ] class RemoveSproutShieldingTest (BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 4 + self.cache_behavior = 'sprout' def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[HAS_CANOPY] * self.num_nodes) - def run_test (self): - + def run_test(self): # Generate blocks up to Heartwood activation logging.info("Generating initial blocks. Current height is 200, advance to 210 (activate Heartwood but not Canopy)") self.nodes[0].generate(10) self.sync_all() - # Shield coinbase to Sprout on node 0. Should pass - sprout_addr = self.nodes[0].z_getnewaddress('sprout') - sprout_addr_node2 = self.nodes[2].z_getnewaddress('sprout') - myopid = self.nodes[0].z_shieldcoinbase(get_coinbase_address(self.nodes[0]), sprout_addr, 0)['opid'] - wait_and_assert_operationid_status(self.nodes[0], myopid) - print("taddr -> Sprout z_shieldcoinbase tx accepted before Canopy on node 0") + n0_sprout_addr0 = self.nodes[0].listaddresses()[0]['sprout']['addresses'][0] + + # Attempt to shield coinbase to Sprout on node 0. Should fail; + # transfers to Sprout are no longer supported + try: + self.nodes[0].z_shieldcoinbase(get_coinbase_address(self.nodes[0]), n0_sprout_addr0, 0)['opid'] + except JSONRPCException as e: + errorString = e.error['message']; + unsupported_sprout_msg = "Sending funds into the Sprout pool is no longer supported." + assert_equal(unsupported_sprout_msg, errorString) self.nodes[0].generate(1) self.sync_all() - assert_equal(self.nodes[0].z_getbalance(sprout_addr), Decimal('10')) - # Fund taddr_0 from shielded coinbase on node 0 - taddr_0 = self.nodes[0].getnewaddress() + # Check that we have the expected balance from the cached setup + assert_equal(self.nodes[0].z_getbalance(n0_sprout_addr0), Decimal('50')) + + # Fund n0_taddr0 from previously existing Sprout funds on node 0 + n0_taddr0 = self.nodes[0].getnewaddress() for _ in range(3): - recipients = [{"address": taddr_0, "amount": Decimal('1')}] - myopid = self.nodes[0].z_sendmany(sprout_addr, recipients, 1, 0, 'AllowRevealedRecipients') + recipients = [{"address": n0_taddr0, "amount": Decimal('1')}] + myopid = self.nodes[0].z_sendmany(n0_sprout_addr0, recipients, 1, 0, 'AllowRevealedRecipients') wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() self.nodes[0].generate(1) self.sync_all() + assert_equal(self.nodes[0].z_getbalance(n0_taddr0), Decimal('3')) + + # Create mergetoaddress taddr -> Sprout transaction, should fail + n1_sprout_addr0 = self.nodes[1].z_getnewaddress('sprout') + assert_raises_message( + JSONRPCException, + "Sending funds into the Sprout pool is no longer supported.", + self.nodes[0].z_mergetoaddress, + ["ANY_TADDR"], n1_sprout_addr0, 0) + + self.nodes[0].generate(1) + self.sync_all() - # Create mergetoaddress taddr -> Sprout transaction and mine on node 0 before it is Canopy-aware. Should pass - merge_tx_0 = self.nodes[0].z_mergetoaddress(["ANY_TADDR"], self.nodes[1].z_getnewaddress('sprout')) - wait_and_assert_operationid_status(self.nodes[0], merge_tx_0['opid']) - print("taddr -> Sprout z_mergetoaddress tx accepted before Canopy on node 0") + # Send some funds back to n0_taddr0 + recipients = [{"address": n0_taddr0, "amount": Decimal('1')}] + myopid = self.nodes[0].z_sendmany(n0_sprout_addr0, recipients, 1, 0, 'AllowRevealedRecipients') + wait_and_assert_operationid_status(self.nodes[0], myopid) # Mine to one block before Canopy activation on node 0; adding value # to the Sprout pool will fail now since the transaction must be # included in the next (or later) block, after Canopy has activated. - self.nodes[0].generate(5) + self.sync_all() + self.nodes[0].generate(4) self.sync_all() assert_equal(self.nodes[0].getblockchaininfo()['upgrades']['e9ff75a6']['status'], 'pending') + assert_equal(self.nodes[0].z_getbalance(n0_taddr0), Decimal('4')) # Shield coinbase to Sprout on node 0. Should fail - sprout_addr = self.nodes[0].z_getnewaddress('sprout') + n0_coinbase_taddr = get_coinbase_address(self.nodes[0]) + n0_sprout_addr1 = self.nodes[0].z_getnewaddress('sprout') assert_raises_message( JSONRPCException, - "Sprout shielding is not supported after Canopy", + "Sending funds into the Sprout pool is no longer supported.", self.nodes[0].z_shieldcoinbase, - get_coinbase_address(self.nodes[0]), sprout_addr, 0) + n0_coinbase_taddr, n0_sprout_addr1, 0) print("taddr -> Sprout z_shieldcoinbase tx rejected at Canopy activation on node 0") # Create taddr -> Sprout z_sendmany transaction on node 0. Should fail - sprout_addr = self.nodes[1].z_getnewaddress('sprout') - assert_raises_message( - JSONRPCException, - "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}]) + n1_sprout_addr1 = self.nodes[1].z_getnewaddress('sprout') + recipients = [{"address": n1_sprout_addr1, "amount": Decimal('1')}] + myopid = self.nodes[0].z_sendmany(n0_taddr0, recipients, 1, 0) + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", unsupported_sprout_msg) print("taddr -> Sprout z_sendmany tx rejected at Canopy activation on node 0") # Create z_mergetoaddress [taddr, Sprout] -> Sprout transaction on node 0. Should fail assert_raises_message( JSONRPCException, - "Sprout shielding is not supported after Canopy", + "Sending funds into the Sprout pool is no longer supported.", self.nodes[0].z_mergetoaddress, ["ANY_TADDR", "ANY_SPROUT"], self.nodes[1].z_getnewaddress('sprout')) - print("[taddr, Sprout] -> Sprout z_mergetoaddress tx rejected at Canopy activation on node 0") - - # Create z_mergetoaddress Sprout -> Sprout transaction on node 0. Should pass - merge_tx_1 = self.nodes[0].z_mergetoaddress(["ANY_SPROUT"], self.nodes[1].z_getnewaddress('sprout')) - wait_and_assert_operationid_status(self.nodes[0], merge_tx_1['opid']) - print("Sprout -> Sprout z_mergetoaddress tx accepted at Canopy activation on node 0") # Activate Canopy self.nodes[0].generate(1) @@ -126,17 +143,5 @@ def run_test (self): wait_and_assert_operationid_status(self.nodes[0], myopid) print("taddr -> Sapling z_shieldcoinbase tx accepted after Canopy on node 0") - # Mine to one block before NU5 activation. - self.nodes[0].generate(4) - self.sync_all() - - # Create z_mergetoaddress Sprout -> Sprout transaction on node 1. Should pass - merge_tx_2 = self.nodes[1].z_mergetoaddress(["ANY_SPROUT"], sprout_addr_node2) - wait_and_assert_operationid_status(self.nodes[1], merge_tx_2['opid']) - print("Sprout -> Sprout z_mergetoaddress tx accepted at NU5 activation on node 1") - - self.nodes[1].generate(1) - self.sync_all() - if __name__ == '__main__': RemoveSproutShieldingTest().main() diff --git a/depend/zcash/qa/rpc-tests/rpcbind_test.py b/depend/zcash/qa/rpc-tests/rpcbind_test.py index 56c2a1041..36e1c0564 100755 --- a/depend/zcash/qa/rpc-tests/rpcbind_test.py +++ b/depend/zcash/qa/rpc-tests/rpcbind_test.py @@ -4,7 +4,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . -# Test for -rpcbind, as well as -rpcallowip and -rpcconnect +# Test for -rpcbind and -rpcallowip # Dependency: python-bitcoinrpc diff --git a/depend/zcash/qa/rpc-tests/shorter_block_times.py b/depend/zcash/qa/rpc-tests/shorter_block_times.py index 103e58177..f96dc3a53 100755 --- a/depend/zcash/qa/rpc-tests/shorter_block_times.py +++ b/depend/zcash/qa/rpc-tests/shorter_block_times.py @@ -23,6 +23,7 @@ def __init__(self): def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(BLOSSOM_BRANCH_ID, 106), '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_gettotalbalance', diff --git a/depend/zcash/qa/rpc-tests/show_help.py b/depend/zcash/qa/rpc-tests/show_help.py index a51a5f41d..9658fc77b 100755 --- a/depend/zcash/qa/rpc-tests/show_help.py +++ b/depend/zcash/qa/rpc-tests/show_help.py @@ -40,15 +40,22 @@ -allowdeprecated= Explicitly allow the use of the specified deprecated feature. Multiple instances of this parameter are permitted; values for must be - selected from among {"none", "gbt_oldhashes", "addrtype", + selected from among {"none", "deprecationinfo_deprecationheight", + "gbt_oldhashes", "z_getbalance", "z_gettotalbalance", "addrtype", "getnewaddress", "getrawchangeaddress", "legacy_privacy", - "wallettxvjoinsplit", "z_getbalance", "z_getnewaddress", - "z_gettotalbalance", "z_listaddresses"} + "wallettxvjoinsplit", "z_getnewaddress", "z_listaddresses"} -blocknotify= Execute command when the best block changes (%s in cmd is replaced by block hash) +| -blocksonly +| Whether to reject transactions from network peers. Automatic broadcast +| and rebroadcast of any transactions from inbound peers is disabled, +| unless '-whitelistforcerelay' is '1', in which case whitelisted peers' +| transactions will be relayed. RPC transactions are not affected. +| (default: 0) +| -checkblocks= How many blocks to check at startup (default: 288, 0 = all) @@ -198,6 +205,10 @@ Support filtering of blocks and transaction with bloom filters (default: 1) +| -enforcenodebloom +| Enforce minimum protocol version to limit use of bloom filters (default: +| 0) +| -port= Listen for connections on (default: 8233 or testnet: 18233) @@ -257,16 +268,16 @@ -migrationdestaddress= Set the Sapling migration address - -mintxfee= - Fees (in ZEC/kB) smaller than this are considered zero fee for - transaction creation (default: 0.00001) - -orchardactionlimit= Set the maximum number of Orchard actions permitted in a transaction (default 50) -paytxfee= - Fee (in ZEC/kB) to add to transactions you send (default: 0.00) + The preferred fee rate (in ZEC per 1000 bytes) used for transactions + created by legacy APIs (sendtoaddress, sendmany, and + fundrawtransaction). If the transaction is less than 1000 bytes then the + fee rate is applied as though it were 1000 bytes. When this option is + not set, the ZIP 317 fee calculation is used. -rescan Rescan the block chain for missing wallet transactions on startup @@ -275,16 +286,9 @@ Attempt to recover private keys from a corrupt wallet on startup (implies -rescan) - -sendfreetransactions - Send transactions as zero-fee transactions if possible (default: 0) - -spendzeroconfchange Spend unconfirmed change when sending transactions (default: 1) - -txconfirmtarget= - If paytxfee is not set, include enough fee so transactions begin - confirmation on average within n blocks (default: 2) - -txexpirydelta Set the number of blocks after which a transaction that has not been mined will become invalid (min: 4, default: 20 (pre-Blossom) or 40 @@ -316,6 +320,18 @@ with `-walletrequirebackup=false` to allow generation of spending keys even if the backup has not yet been confirmed. +|Wallet debugging/testing options: +| +| -dblogsize= +| Flush wallet database activity from memory to disk log every +| megabytes (default: 100) +| +| -flushwallet +| Run a thread to flush wallet periodically (default: 1) +| +| -privdb +| Sets the DB_PRIVATE flag in the wallet db environment (default: 1) +| ZeroMQ notification options: -zmqpubhashblock=
@@ -356,6 +372,58 @@ -uacomment= Append comment to the user agent string +| -checkblockindex +| Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, +| chainActive and mapBlocksUnlinked occasionally. (default: 0) +| +| -checkmempool= +| Run checks every transactions (default: 0) +| +| -checkpoints +| Disable expensive verification for known chain history (default: 1) +| +| -disablesafemode +| Disable safemode, override a real safe mode event (default: 0) +| +| -testsafemode +| Force safe mode (default: 0) +| +| -dropmessagestest= +| Randomly drop 1 of every network messages +| +| -fuzzmessagestest= +| Randomly fuzz 1 of every network messages +| +| -stopafterblockimport +| Stop running after importing blocks from disk (default: 0) +| +| -limitancestorcount= +| Do not accept transactions if number of in-mempool ancestors is or +| more (default: 100) +| +| -limitancestorsize= +| Do not accept transactions whose size with all in-mempool ancestors +| exceeds kilobytes (default: 1800) +| +| -limitdescendantcount= +| Do not accept transactions if any ancestor would have or more +| in-mempool descendants (default: 1000) +| +| -limitdescendantsize= +| Do not accept transactions if any ancestor would have more than +| kilobytes of in-mempool descendants (default: 5000). +| +| -nuparams=hexBranchId:activationHeight +| Use given activation height for specified network upgrade (regtest-only) +| +| -nurejectoldversions +| Reject peers that don't know about the current epoch (regtest-only) +| (default: 1) +| +| -fundingstream=streamId:startHeight:endHeight:comma_delimited_addresses +| Use given addresses for block subsidy share paid to the funding stream +| with id (regtest-only) +| -debug= Output debugging information (default: 0, supplying is optional). If is not supplied or if = 1, output @@ -368,6 +436,9 @@ -experimentalfeatures Enable use of experimental features +| -nodebug +| Turn off debugging messages, same as -debug=0 +| -help-debug Show all debugging options (usage: --help -help-debug) @@ -377,9 +448,25 @@ -logtimestamps Prepend debug output with timestamp (default: 1) +| -clockoffset= +| Applies offset of seconds to the actual time. Incompatible with +| -mocktime (default: 0) +| +| -mocktime= +| Replace actual time with seconds since epoch. Incompatible with +| -clockoffset (default: 0) +| +| -maxsigcachesize= +| Limit total size of signature and bundle caches to MiB (default: 32) +| +| -maxtipage= +| Maximum tip age in seconds to consider node in initial block download +| (default: 86400) +| -minrelaytxfee= - Fees (in ZEC/kB) smaller than this are considered zero fee for relaying, - mining and transaction creation (default: 0.000001) + Transactions must have at least this fee rate (in ZEC per 1000 bytes) + for relaying, mining and transaction creation (default: 0.000001). This + is not the only fee constraint. -maxtxfee= Maximum total fees (in ZEC) to use in a single wallet transaction or raw @@ -389,11 +476,21 @@ -printtoconsole Send trace/debug info to console instead of the debug log +| -printpriority +| Log the modified fee, conventional fee, size, number of logical actions, +| and number of unpaid actions for each transaction when mining blocks +| (default: 0) +| Chain selection options: -testnet Use the test chain +| -regtest +| Enter regression test mode, which uses a special chain in which blocks +| can be solved instantly. This is intended for regression testing tools +| and app development. +| Node relay options: -datacarrier @@ -405,16 +502,16 @@ Block creation options: - -blockminsize= - Set minimum block size in bytes (default: 0) - -blockmaxsize= Set maximum block size in bytes (default: 2000000) - -blockprioritysize= - Set maximum size of high-priority/low-fee transactions in bytes - (default: 1000000) + -blockunpaidactionlimit= + Set the limit on unpaid actions that will be accepted in a block for + transactions paying less than the ZIP 317 fee (default: 50) +| -blockversion= +| Override block version to test forking scenarios (default: 4) +| Mining options: -gen @@ -476,6 +573,12 @@ -rpcthreads= Set the number of threads to service RPC calls (default: 4) +| -rpcworkqueue= +| Set the depth of the work queue to service RPC calls (default: 16) +| +| -rpcservertimeout= +| Timeout during HTTP requests (default: 30) +| Metrics Options (only if -daemon and -printtoconsole are not set): -showmetrics @@ -502,16 +605,15 @@ class ShowHelpTest(BitcoinTestFramework): def setup_network(self): self.nodes = [] - def show_help(self): + def show_help(self, expected, extra_args): with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stdout: - args = [ zcashd_binary(), "--help" ] + args = [ zcashd_binary(), "--help" ] + extra_args process = subprocess.run(args, stdout=log_stdout) assert_equal(process.returncode, 0) log_stdout.seek(0) stdout = log_stdout.read().decode('utf-8') # Skip the first line which contains version information. actual = stdout.split('\n', 1)[1] - expected = help_message changed = False @@ -536,10 +638,14 @@ def show_help(self): 'expected', 'actual', )) - raise AssertionError('Help text has changed:\n' + diff) + raise AssertionError("'%s' text has changed:\n%s" % (' '.join(args), diff)) def run_test(self): - self.show_help() + expected = "".join([line + "\n" for line in help_message.splitlines() if not line.startswith("|")]) + self.show_help(expected, []) + + expected_debug = "".join([line.lstrip("|") + "\n" for line in help_message.splitlines()]) + self.show_help(expected_debug, ["-help-debug"]) if __name__ == '__main__': ShowHelpTest().main() diff --git a/depend/zcash/qa/rpc-tests/smartfees.py b/depend/zcash/qa/rpc-tests/smartfees.py index cb4039829..f2d00c4dd 100755 --- a/depend/zcash/qa/rpc-tests/smartfees.py +++ b/depend/zcash/qa/rpc-tests/smartfees.py @@ -78,12 +78,13 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee return (completetx, fee) def split_inputs(from_node, txins, txouts, initial_split = False): - ''' - We need to generate a lot of very small inputs so we can generate a ton of transactions - and they will have low priority. + """ + We need to generate a lot of inputs so we can generate a ton of transactions. This function takes an input from txins, and creates and sends a transaction which splits the value into 2 outputs which are appended to txouts. - ''' + Previously this was designed to be small inputs so they wouldn't have + a high coin age when the notion of priority still existed. + """ prevtxout = txins.pop() inputs = [] outputs = {} @@ -150,16 +151,16 @@ def __init__(self): def setup_network(self): ''' We'll setup the network to have 3 nodes that all mine with different parameters. - But first we need to use one node to create a lot of small low priority outputs + But first we need to use one node to create a lot of outputs which we will use to generate our transactions. ''' self.nodes = [] # Use node0 to mine blocks for input splitting self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", - "-relaypriority=0", "-whitelist=127.0.0.1"])) + "-whitelist=127.0.0.1"])) print("This test is time consuming, please be patient") - print("Splitting inputs to small size so we can generate low priority tx's") + print("Splitting inputs so we can generate tx's") self.txouts = [] self.txouts2 = [] # Split a coinbase into two transaction puzzle outputs @@ -188,18 +189,16 @@ def setup_network(self): # Now we can connect the other nodes, didn't want to connect them earlier # so the estimates would not be affected by the splitting transactions - # Node1 mines small blocks but that are bigger than the expected transaction rate, - # and allows free transactions. + # Node1 mines small blocks but that are bigger than the expected transaction rate. # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes, # (17k is room enough for 110 or so transactions) self.nodes.append(start_node(1, self.options.tmpdir, - ["-blockprioritysize=1500", "-blockmaxsize=18000", - "-maxorphantx=1000", "-relaypriority=0", "-debug=estimatefee"])) + ["-blockmaxsize=18000", "-maxorphantx=1000", "-debug=estimatefee"])) connect_nodes(self.nodes[1], 0) # Node2 is a stingy miner, that # produces too small blocks (room for only 70 or so transactions) - node2args = ["-blockprioritysize=0", "-blockmaxsize=12000", "-maxorphantx=1000", "-relaypriority=0"] + node2args = ["-blockmaxsize=12000", "-maxorphantx=1000"] self.nodes.append(start_node(2, self.options.tmpdir, node2args)) connect_nodes(self.nodes[0], 2) diff --git a/depend/zcash/qa/rpc-tests/test_framework/test_framework.py b/depend/zcash/qa/rpc-tests/test_framework/test_framework.py index 431825260..216069a46 100755 --- a/depend/zcash/qa/rpc-tests/test_framework/test_framework.py +++ b/depend/zcash/qa/rpc-tests/test_framework/test_framework.py @@ -190,6 +190,7 @@ def __init__(self): super().__init__() self.num_nodes = 1 self.cache_behavior = 'clean' + self.additional_args = [] def add_options(self, parser): parser.add_option("--testbinary", dest="testbinary", @@ -202,7 +203,7 @@ def add_options(self, parser): def setup_network(self): self.nodes = start_nodes( self.num_nodes, self.options.tmpdir, - extra_args=[['-debug', '-whitelist=127.0.0.1']] * self.num_nodes, + extra_args=[['-debug', '-whitelist=127.0.0.1'] + self.additional_args] * self.num_nodes, binary=[self.options.testbinary] + [self.options.refbinary]*(self.num_nodes-1)) diff --git a/depend/zcash/qa/rpc-tests/test_framework/util.py b/depend/zcash/qa/rpc-tests/test_framework/util.py index 143ff9027..e741af8db 100644 --- a/depend/zcash/qa/rpc-tests/test_framework/util.py +++ b/depend/zcash/qa/rpc-tests/test_framework/util.py @@ -101,10 +101,10 @@ def rpc_port(n): return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES) def check_json_precision(): - """Make sure json library being used does not lose precision converting BTC values""" + """Make sure json library being used does not lose precision converting ZEC values""" n = Decimal("20000000.00000003") - satoshis = int(json.loads(json.dumps(float(n)))*1.0e8) - if satoshis != 2000000000000003: + zatoshis = int(json.loads(json.dumps(float(n)))*1.0e8) + if zatoshis != 2000000000000003: raise RuntimeError("JSON encode/decode loses precision") def bytes_to_hex_str(byte_str): @@ -552,47 +552,6 @@ def make_change(from_node, amount_in, amount_out, fee): outputs[from_node.getnewaddress()] = change return outputs -def send_zeropri_transaction(from_node, to_node, amount, fee): - """ - Create&broadcast a zero-priority transaction. - Returns (txid, hex-encoded-txdata) - Ensures transaction is zero-priority by first creating a send-to-self, - then using its output - """ - - # Create a send-to-self with confirmed inputs: - self_address = from_node.getnewaddress() - (total_in, inputs) = gather_inputs(from_node, amount+fee*2) - outputs = make_change(from_node, total_in, amount+fee, fee) - outputs[self_address] = float(amount+fee) - - self_rawtx = from_node.createrawtransaction(inputs, outputs) - self_signresult = from_node.signrawtransaction(self_rawtx) - self_txid = from_node.sendrawtransaction(self_signresult["hex"], True) - - vout = find_output(from_node, self_txid, amount+fee) - # Now immediately spend the output to create a 1-input, 1-output - # zero-priority transaction: - inputs = [ { "txid" : self_txid, "vout" : vout } ] - outputs = { to_node.getnewaddress() : float(amount) } - - rawtx = from_node.createrawtransaction(inputs, outputs) - signresult = from_node.signrawtransaction(rawtx) - txid = from_node.sendrawtransaction(signresult["hex"], True) - - return (txid, signresult["hex"]) - -def random_zeropri_transaction(nodes, amount, min_fee, fee_increment, fee_variants): - """ - Create a random zero-priority transaction. - Returns (txid, hex-encoded-transaction-data, fee) - """ - from_node = random.choice(nodes) - to_node = random.choice(nodes) - fee = min_fee + fee_increment*random.randint(0,fee_variants) - (txid, txhex) = send_zeropri_transaction(from_node, to_node, amount, fee) - return (txid, txhex, fee) - def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants): """ Create a random transaction. diff --git a/depend/zcash/qa/rpc-tests/test_framework/zip317.py b/depend/zcash/qa/rpc-tests/test_framework/zip317.py new file mode 100644 index 000000000..7e8cd904a --- /dev/null +++ b/depend/zcash/qa/rpc-tests/test_framework/zip317.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php . + +# +# zip317.py +# +# Utilities for ZIP 317 conventional fee specification, as defined in https://zips.z.cash/zip-0317. +# + +from test_framework.mininode import COIN +from decimal import Decimal + +# The fee per logical action, in zatoshis. See https://zips.z.cash/zip-0317#fee-calculation. +MARGINAL_FEE = 5000 + +# The lower bound on the number of logical actions in a tx, for purposes of fee calculation. See +# https://zips.z.cash/zip-0317#fee-calculation. +GRACE_ACTIONS = 2 + +# Limits the relative probability of picking a given transaction to be at most `WEIGHT_RATIO_CAP` +# times greater than a transaction that pays exactly the conventional fee. See +# https://zips.z.cash/zip-0317#recommended-algorithm-for-block-template-construction +WEIGHT_RATIO_CAP = 4 + +# Default limit on the number of unpaid actions in a block. See +# https://zips.z.cash/zip-0317#recommended-algorithm-for-block-template-construction +DEFAULT_BLOCK_UNPAID_ACTION_LIMIT = 50 + +# The zcashd RPC sentinel value to indicate the conventional_fee when a positional argument is +# required. +ZIP_317_FEE = None + +def conventional_fee_zats(logical_actions): + return MARGINAL_FEE * max(GRACE_ACTIONS, logical_actions) + +def conventional_fee(logical_actions): + return Decimal(conventional_fee_zats(logical_actions)) / COIN diff --git a/depend/zcash/qa/rpc-tests/turnstile.py b/depend/zcash/qa/rpc-tests/turnstile.py index 47441a221..56384d73a 100755 --- a/depend/zcash/qa/rpc-tests/turnstile.py +++ b/depend/zcash/qa/rpc-tests/turnstile.py @@ -41,6 +41,7 @@ from decimal import Decimal BASE_ARGS = [ + '-minrelaytxfee=0', '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', ] diff --git a/depend/zcash/qa/rpc-tests/txn_doublespend.py b/depend/zcash/qa/rpc-tests/txn_doublespend.py index 3f24b03a7..3e55edb4a 100755 --- a/depend/zcash/qa/rpc-tests/txn_doublespend.py +++ b/depend/zcash/qa/rpc-tests/txn_doublespend.py @@ -26,6 +26,7 @@ def add_options(self, parser): def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-allowdeprecated=getnewaddress', ]] * self.num_nodes) @@ -44,7 +45,7 @@ def run_test(self): # Coins are sent to node1_address node1_address = self.nodes[1].getnewaddress("") - # First: use raw transaction API to send (starting_balance - (mining_reward - 2)) BTC to node1_address, + # First: use raw transaction API to send (starting_balance - (mining_reward - 2)) ZEC to node1_address, # but don't broadcast: (total_in, inputs) = gather_inputs(self.nodes[0], (starting_balance - (mining_reward - 2))) change_address = self.nodes[0].getnewaddress("") diff --git a/depend/zcash/qa/rpc-tests/wallet.py b/depend/zcash/qa/rpc-tests/wallet.py index 97dc50838..bac5d5b2b 100755 --- a/depend/zcash/qa/rpc-tests/wallet.py +++ b/depend/zcash/qa/rpc-tests/wallet.py @@ -8,6 +8,7 @@ from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, start_nodes, start_node, \ connect_nodes_bi, sync_blocks, sync_mempools +from test_framework.zip317 import conventional_fee from decimal import Decimal @@ -20,6 +21,7 @@ def __init__(self): def setup_network(self, split=False): self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-allowdeprecated=getnewaddress', '-allowdeprecated=z_getbalance', ]] * 3) @@ -100,7 +102,9 @@ def run_test (self): # Catch an attempt to send a transaction with an absurdly high fee. # Send 1.0 ZEC from an utxo of value 10.0 ZEC but don't specify a change output, so then - # the change of 9.0 ZEC becomes the fee, which is greater than estimated fee of 0.0021 ZEC. + # the change of 9.0 ZEC becomes the fee, which is considered to be absurdly high because + # the fee is more than 4 times the conventional fee. + assert(Decimal("9.0") > 4*conventional_fee(1)) inputs = [] outputs = {} for utxo in node2utxos: diff --git a/depend/zcash/qa/rpc-tests/wallet_accounts.py b/depend/zcash/qa/rpc-tests/wallet_accounts.py index 140f1050f..bf5f657e4 100755 --- a/depend/zcash/qa/rpc-tests/wallet_accounts.py +++ b/depend/zcash/qa/rpc-tests/wallet_accounts.py @@ -22,7 +22,8 @@ # Test wallet accounts behaviour class WalletAccountsTest(BitcoinTestFramework): def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, [[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 210), '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', diff --git a/depend/zcash/qa/rpc-tests/wallet_changeaddresses.py b/depend/zcash/qa/rpc-tests/wallet_changeaddresses.py index a490adc75..4f8e12ae8 100755 --- a/depend/zcash/qa/rpc-tests/wallet_changeaddresses.py +++ b/depend/zcash/qa/rpc-tests/wallet_changeaddresses.py @@ -25,6 +25,7 @@ def setup_chain(self): def setup_network(self): args = [ + '-minrelaytxfee=0', nuparams(SAPLING_BRANCH_ID, 1), '-txindex', # Avoid JSONRPC error: No information available about transaction '-allowdeprecated=getnewaddress', @@ -44,7 +45,7 @@ def run_test(self): midAddr = self.nodes[0].z_getnewaddress('sapling') myopid = self.nodes[0].z_shieldcoinbase(get_coinbase_address(self.nodes[0]), midAddr, 0)['opid'] wait_and_assert_operationid_status(self.nodes[0], myopid) - + self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -87,7 +88,7 @@ def check_change_taddr_reuse(target, policy): print() print('Checking z_sendmany(taddr->Sapling)') - check_change_taddr_reuse(saplingAddr, 'AllowRevealedSenders') + check_change_taddr_reuse(saplingAddr, 'AllowFullyTransparent') print() print('Checking z_sendmany(taddr->taddr)') check_change_taddr_reuse(taddr, 'AllowFullyTransparent') diff --git a/depend/zcash/qa/rpc-tests/wallet_changeindicator.py b/depend/zcash/qa/rpc-tests/wallet_changeindicator.py index 3e70ef1ae..bf289ac86 100755 --- a/depend/zcash/qa/rpc-tests/wallet_changeindicator.py +++ b/depend/zcash/qa/rpc-tests/wallet_changeindicator.py @@ -23,6 +23,7 @@ def generate_and_sync(self): def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-allowdeprecated=getnewaddress', '-allowdeprecated=z_getnewaddress', ]] * self.num_nodes) diff --git a/depend/zcash/qa/rpc-tests/wallet_deprecation.py b/depend/zcash/qa/rpc-tests/wallet_deprecation.py index e735470d0..121bb0420 100755 --- a/depend/zcash/qa/rpc-tests/wallet_deprecation.py +++ b/depend/zcash/qa/rpc-tests/wallet_deprecation.py @@ -12,22 +12,46 @@ ) from test_framework.authproxy import JSONRPCException +import os.path + +# Pick a subset of the deprecated RPC methods to test with. This test assumes that +# the deprecation feature name is the same as the RPC method name, and that the RPC +# method works without any arguments. +TESTABLE_FEATURES = [ + "z_gettotalbalance", + "getnewaddress", + "z_getnewaddress", +] + # Test wallet address behaviour across network upgrades class WalletDeprecationTest(BitcoinTestFramework): def __init__(self): super().__init__() self.num_nodes = 1 + def setup_chain(self): + super().setup_chain() + # Save a copy of node 0's zcash.conf + with open(os.path.join(self.options.tmpdir, "node0", "zcash.conf"), 'r', encoding='utf8') as f: + self.conf_lines = f.readlines() + def setup_network(self): - self.setup_network_internal([]) + self.setup_network_with_args([]) - def setup_network_internal(self, allowed_deprecated = []): + def setup_network_with_args(self, allowed_deprecated): dep_args = ["-allowdeprecated=" + v for v in allowed_deprecated] self.nodes = start_nodes( self.num_nodes, self.options.tmpdir, extra_args=[dep_args] * self.num_nodes) + def setup_network_with_config(self, allowed_deprecated): + conf_lines = self.conf_lines + ["allowdeprecated={}\n".format(v) for v in allowed_deprecated] + with open(os.path.join(self.options.tmpdir, "node0", "zcash.conf"), 'w', encoding='utf8') as f: + f.writelines(conf_lines) + + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) + def verify_enabled(self, function): try: getattr(self.nodes[0], function)() @@ -50,43 +74,42 @@ def verify_disabled(self, function): "failed with '%s'" % errorString if len(errorString) > 0 else "succeeded", )) + def test_case(self, start_mode, features_to_allow, expected_state, default_enabled, default_disabled): + stop_nodes(self.nodes) + wait_bitcoinds() + start_mode(features_to_allow) + + for function in default_enabled: + if function in TESTABLE_FEATURES: + expected_state(function) + for function in default_disabled: + if function in TESTABLE_FEATURES: + expected_state(function) + def run_test(self): - # Pick a subset of the deprecated RPC methods to test with. This test assumes that - # the deprecation feature name is the same as the RPC method name. - DEFAULT_ENABLED = [ - ] - DEFAULT_DISABLED = [ - "getnewaddress", - "z_getnewaddress", - ] + dep_info = self.nodes[0].getdeprecationinfo() + default_enabled = dep_info['deprecated_features'] + default_disabled = dep_info['disabled_features'] + + for function in TESTABLE_FEATURES: + assert(function in default_enabled or function in default_disabled) # RPC methods that are deprecated but enabled by default should succeed - for function in DEFAULT_ENABLED: - self.verify_enabled(function) + for function in default_enabled: + if function in TESTABLE_FEATURES: + self.verify_enabled(function) # RPC methods that are deprecated and not enabled by default should fail - for function in DEFAULT_DISABLED: - self.verify_disabled(function) - - # restart with a specific selection of deprecated methods enabled - stop_nodes(self.nodes) - wait_bitcoinds() - self.setup_network_internal(DEFAULT_DISABLED) - - for function in DEFAULT_ENABLED: - self.verify_enabled(function) - for function in DEFAULT_DISABLED: - self.verify_enabled(function) + for function in default_disabled: + if function in TESTABLE_FEATURES: + self.verify_disabled(function) - # restart with no deprecated methods enabled - stop_nodes(self.nodes) - wait_bitcoinds() - self.setup_network_internal(["none"]) + for start_mode in (self.setup_network_with_args, self.setup_network_with_config): + # restart with a specific selection of deprecated methods enabled + self.test_case(start_mode, default_disabled, self.verify_enabled, default_enabled, default_disabled) - for function in DEFAULT_ENABLED: - self.verify_disabled(function) - for function in DEFAULT_DISABLED: - self.verify_disabled(function) + # restart with no deprecated methods enabled + self.test_case(start_mode, ["none"], self.verify_disabled, default_enabled, default_disabled) if __name__ == '__main__': WalletDeprecationTest().main() diff --git a/depend/zcash/qa/rpc-tests/wallet_doublespend.py b/depend/zcash/qa/rpc-tests/wallet_doublespend.py index 4654b58ed..868f706af 100755 --- a/depend/zcash/qa/rpc-tests/wallet_doublespend.py +++ b/depend/zcash/qa/rpc-tests/wallet_doublespend.py @@ -25,7 +25,8 @@ def __init__(self): self.num_nodes = 4 def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, [[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 201), ]] * self.num_nodes) diff --git a/depend/zcash/qa/rpc-tests/wallet_isfromme.py b/depend/zcash/qa/rpc-tests/wallet_isfromme.py index 70d09062d..c38c95be0 100755 --- a/depend/zcash/qa/rpc-tests/wallet_isfromme.py +++ b/depend/zcash/qa/rpc-tests/wallet_isfromme.py @@ -26,6 +26,7 @@ def setup_chain(self): def setup_network(self, split=False): self.nodes = start_nodes(1, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(OVERWINTER_BRANCH_ID, 1), nuparams(SAPLING_BRANCH_ID, 1), nuparams(BLOSSOM_BRANCH_ID, 1), diff --git a/depend/zcash/qa/rpc-tests/wallet_listnotes.py b/depend/zcash/qa/rpc-tests/wallet_listnotes.py index cbc7898f6..065005bd9 100755 --- a/depend/zcash/qa/rpc-tests/wallet_listnotes.py +++ b/depend/zcash/qa/rpc-tests/wallet_listnotes.py @@ -24,7 +24,8 @@ def __init__(self): self.cache_behavior = 'sprout' def setup_nodes(self): - return start_nodes(4, self.options.tmpdir, [[ + return start_nodes(4, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 215), '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', diff --git a/depend/zcash/qa/rpc-tests/wallet_listreceived.py b/depend/zcash/qa/rpc-tests/wallet_listreceived.py index 3d87dba6c..c39786b0a 100755 --- a/depend/zcash/qa/rpc-tests/wallet_listreceived.py +++ b/depend/zcash/qa/rpc-tests/wallet_listreceived.py @@ -13,10 +13,10 @@ connect_nodes_bi, nuparams, DEFAULT_FEE, - DEFAULT_FEE_ZATS, NU5_BRANCH_ID, ) from test_framework.util import wait_and_assert_operationid_status, start_nodes +from test_framework.zip317 import conventional_fee, conventional_fee_zats from decimal import Decimal my_memo_str = 'c0ffee' # stay awake @@ -32,14 +32,13 @@ def __init__(self): self.cache_behavior = 'clean' def setup_network(self): - self.nodes = start_nodes( - self.num_nodes, self.options.tmpdir, - extra_args=[[ - nuparams(NU5_BRANCH_ID, 225), - '-allowdeprecated=getnewaddress', - '-allowdeprecated=z_getnewaddress', - ]] * self.num_nodes - ) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', + nuparams(NU5_BRANCH_ID, 225), + '-allowdeprecated=getnewaddress', + '-allowdeprecated=z_getnewaddress', + ]] * self.num_nodes) + connect_nodes_bi(self.nodes, 0, 1) connect_nodes_bi(self.nodes, 1, 2) connect_nodes_bi(self.nodes, 0, 2) @@ -67,7 +66,7 @@ def test_received_sapling(self, height): opid = self.nodes[1].z_sendmany(taddr, [ {'address': zaddr1, 'amount': 1, 'memo': my_memo}, {'address': zaddrExt, 'amount': 2}, - ], 1, DEFAULT_FEE, 'AllowRevealedSenders') + ], 1, DEFAULT_FEE, 'AllowFullyTransparent') txid = wait_and_assert_operationid_status(self.nodes[1], opid) self.sync_all() @@ -157,8 +156,8 @@ def test_received_sapling(self, height): outputs = sorted(pt['outputs'], key=lambda x: x['valueZat']) assert_equal(outputs[0]['pool'], 'sapling') assert_equal(outputs[0]['address'], zaddr1) - assert_equal(outputs[0]['value'], Decimal('0.4') - DEFAULT_FEE) - assert_equal(outputs[0]['valueZat'], 40000000 - DEFAULT_FEE_ZATS) + assert_equal(outputs[0]['value'], Decimal('0.4') - conventional_fee(2)) + assert_equal(outputs[0]['valueZat'], 40000000 - conventional_fee_zats(2)) assert_equal(outputs[0]['output'], 1) assert_equal(outputs[0]['outgoing'], False) assert_equal(outputs[0]['memo'], no_memo) @@ -178,8 +177,8 @@ def test_received_sapling(self, height): assert_equal(2, len(r), "zaddr1 Should have received 2 notes") r = sorted(r, key = lambda received: received['amount']) assert_equal(txid, r[0]['txid']) - assert_equal(Decimal('0.4')-DEFAULT_FEE, r[0]['amount']) - assert_equal(40000000-DEFAULT_FEE_ZATS, r[0]['amountZat']) + assert_equal(Decimal('0.4')-conventional_fee(2), r[0]['amount']) + assert_equal(40000000-conventional_fee_zats(2), r[0]['amountZat']) assert_equal(r[0]['change'], True, "Note valued at (0.4-"+str(DEFAULT_FEE)+") should be change") assert_equal(no_memo, r[0]['memo']) @@ -309,7 +308,7 @@ def test_received_orchard(self, height): opid = self.nodes[1].z_sendmany(taddr, [ {'address': uao, 'amount': 1, 'memo': my_memo}, {'address': uaso, 'amount': 2}, - ], 1, 0, 'AllowRevealedSenders') + ], 1, 0, 'AllowFullyTransparent') txid0 = wait_and_assert_operationid_status(self.nodes[1], opid) self.sync_all() @@ -382,8 +381,8 @@ def test_received_orchard(self, height): # Verify that we observe the change output assert_equal(outputs[2]['pool'], 'orchard') - assert_equal(outputs[2]['value'], Decimal('0.49999')) - assert_equal(outputs[2]['valueZat'], 49999000) + assert_equal(outputs[2]['value'], Decimal('0.5') - conventional_fee(3)) + assert_equal(outputs[2]['valueZat'], 50000000 - conventional_fee_zats(3)) assert_equal(outputs[2]['outgoing'], False) assert_equal(outputs[2]['walletInternal'], True) assert_equal(outputs[2]['memo'], no_memo) diff --git a/depend/zcash/qa/rpc-tests/wallet_listunspent.py b/depend/zcash/qa/rpc-tests/wallet_listunspent.py index 96f149817..c0c2255f9 100755 --- a/depend/zcash/qa/rpc-tests/wallet_listunspent.py +++ b/depend/zcash/qa/rpc-tests/wallet_listunspent.py @@ -20,7 +20,8 @@ def unspent_total(unspent): class WalletListUnspent(BitcoinTestFramework): def setup_nodes(self): - return start_nodes(4, self.options.tmpdir, [[ + return start_nodes(4, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 201), '-allowdeprecated=getnewaddress', ]] * 4) @@ -82,7 +83,7 @@ def expected_matured_at_height(height): opid = self.nodes[0].z_sendmany( 'ANY_TADDR', [{'address': n1uaddr, 'amount': 2}], - 1, 0, 'AllowRevealedSenders') + 1, 0, 'AllowFullyTransparent') wait_and_assert_operationid_status(self.nodes[0], opid) self.nodes[0].generate(2) @@ -92,7 +93,7 @@ def expected_matured_at_height(height): opid = self.nodes[0].z_sendmany( 'ANY_TADDR', [{'address': n1uaddr, 'amount': 3}], - 1, 0, 'AllowRevealedSenders') + 1, 0, 'AllowFullyTransparent') wait_and_assert_operationid_status(self.nodes[0], opid) self.nodes[0].generate(2) diff --git a/depend/zcash/qa/rpc-tests/wallet_nullifiers.py b/depend/zcash/qa/rpc-tests/wallet_nullifiers.py index 938945bc6..1a16df993 100755 --- a/depend/zcash/qa/rpc-tests/wallet_nullifiers.py +++ b/depend/zcash/qa/rpc-tests/wallet_nullifiers.py @@ -7,6 +7,7 @@ from test_framework.util import assert_equal, assert_true, bitcoind_processes, \ connect_nodes_bi, start_node, start_nodes, wait_and_assert_operationid_status, \ get_coinbase_address, DEFAULT_FEE +from test_framework.zip317 import conventional_fee from decimal import Decimal @@ -92,7 +93,7 @@ def run_test (self): # check zaddr balance zsendmany2notevalue = Decimal('2.0') - zsendmanyfee = DEFAULT_FEE + zsendmanyfee = conventional_fee(2) zaddrremaining = zsendmanynotevalue - zsendmany2notevalue - zsendmanyfee assert_equal(self.nodes[3].z_getbalance(myzaddr3), zsendmany2notevalue) assert_equal(self.nodes[2].z_getbalance(myzaddr), zaddrremaining) @@ -117,7 +118,7 @@ def run_test (self): # check zaddr balance zsendmany3notevalue = Decimal('1.0') - zaddrremaining2 = zaddrremaining - zsendmany3notevalue - zsendmanyfee + zaddrremaining2 = zaddrremaining - zsendmany3notevalue - DEFAULT_FEE assert_equal(self.nodes[1].z_getbalance(myzaddr), zaddrremaining2) assert_equal(self.nodes[2].z_getbalance(myzaddr), zaddrremaining2) diff --git a/depend/zcash/qa/rpc-tests/wallet_orchard.py b/depend/zcash/qa/rpc-tests/wallet_orchard.py index 8122b268c..01c27fcd3 100755 --- a/depend/zcash/qa/rpc-tests/wallet_orchard.py +++ b/depend/zcash/qa/rpc-tests/wallet_orchard.py @@ -22,7 +22,8 @@ def __init__(self): self.num_nodes = 4 def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, [[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 210), ]] * self.num_nodes) diff --git a/depend/zcash/qa/rpc-tests/wallet_orchard_change.py b/depend/zcash/qa/rpc-tests/wallet_orchard_change.py index bded74614..fb4487b48 100755 --- a/depend/zcash/qa/rpc-tests/wallet_orchard_change.py +++ b/depend/zcash/qa/rpc-tests/wallet_orchard_change.py @@ -22,7 +22,8 @@ def __init__(self): self.num_nodes = 4 def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, [[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 205), '-regtestwalletsetbestchaineveryblock', ]] * self.num_nodes) diff --git a/depend/zcash/qa/rpc-tests/wallet_orchard_init.py b/depend/zcash/qa/rpc-tests/wallet_orchard_init.py index b5db9f4e1..75d7746f8 100755 --- a/depend/zcash/qa/rpc-tests/wallet_orchard_init.py +++ b/depend/zcash/qa/rpc-tests/wallet_orchard_init.py @@ -27,7 +27,8 @@ def __init__(self): self.num_nodes = 4 def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, [[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 205), '-regtestwalletsetbestchaineveryblock' ]] * self.num_nodes) diff --git a/depend/zcash/qa/rpc-tests/wallet_orchard_persistence.py b/depend/zcash/qa/rpc-tests/wallet_orchard_persistence.py index f27a94442..99470dccb 100755 --- a/depend/zcash/qa/rpc-tests/wallet_orchard_persistence.py +++ b/depend/zcash/qa/rpc-tests/wallet_orchard_persistence.py @@ -24,7 +24,8 @@ def __init__(self): self.num_nodes = 4 def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, [[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 201), ]] * self.num_nodes) diff --git a/depend/zcash/qa/rpc-tests/wallet_orchard_reindex.py b/depend/zcash/qa/rpc-tests/wallet_orchard_reindex.py index c1a3a9686..a9a60b223 100755 --- a/depend/zcash/qa/rpc-tests/wallet_orchard_reindex.py +++ b/depend/zcash/qa/rpc-tests/wallet_orchard_reindex.py @@ -21,6 +21,7 @@ import time BASE_ARGS = [ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 210), '-regtestwalletsetbestchaineveryblock', ] diff --git a/depend/zcash/qa/rpc-tests/wallet_parsing_amounts.py b/depend/zcash/qa/rpc-tests/wallet_parsing_amounts.py index bdfdf42c3..ff792b934 100755 --- a/depend/zcash/qa/rpc-tests/wallet_parsing_amounts.py +++ b/depend/zcash/qa/rpc-tests/wallet_parsing_amounts.py @@ -70,19 +70,6 @@ def run_test(self): print(errorString) assert(False) - # This fee is larger than the default fee and since amount=0 - # it should trigger error - fee = Decimal('0.1') - recipients = [ {"address": myzaddr, "amount": Decimal('0.0') } ] - minconf = 1 - errorString = '' - - try: - myopid = self.nodes[0].z_sendmany(myzaddr, recipients, minconf, fee) - except JSONRPCException as e: - errorString = e.error['message'] - assert('Small transaction amount' in errorString) - # This fee is less than default and greater than amount, but still valid fee = Decimal('0.0000001') recipients = [ {"address": myzaddr, "amount": Decimal('0.00000001') } ] diff --git a/depend/zcash/qa/rpc-tests/wallet_persistence.py b/depend/zcash/qa/rpc-tests/wallet_persistence.py index f0b9f8be8..74085e6b0 100755 --- a/depend/zcash/qa/rpc-tests/wallet_persistence.py +++ b/depend/zcash/qa/rpc-tests/wallet_persistence.py @@ -22,6 +22,7 @@ def setup_chain(self): def setup_network(self, split=False): self.nodes = start_nodes(4, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', '-allowdeprecated=z_gettotalbalance', diff --git a/depend/zcash/qa/rpc-tests/wallet_sapling.py b/depend/zcash/qa/rpc-tests/wallet_sapling.py index dd605d0dc..f95569c73 100755 --- a/depend/zcash/qa/rpc-tests/wallet_sapling.py +++ b/depend/zcash/qa/rpc-tests/wallet_sapling.py @@ -4,7 +4,6 @@ # file COPYING or https://www.opensource.org/licenses/mit-license.php . from test_framework.test_framework import BitcoinTestFramework -from test_framework.authproxy import JSONRPCException from test_framework.util import ( assert_equal, get_coinbase_address, @@ -20,6 +19,7 @@ class WalletSaplingTest(BitcoinTestFramework): def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-allowdeprecated=getnewaddress', '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', @@ -55,10 +55,6 @@ def run_test(self): self.sync_all() - # Verify priority of tx is MAX_PRIORITY, defined as 1E+16 (10000000000000000) - mempool = self.nodes[0].getrawmempool(True) - assert(Decimal(mempool[mytxid]['startingpriority']) == Decimal('1E+16')) - # Shield another coinbase UTXO myopid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders') mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) @@ -81,11 +77,6 @@ def run_test(self): mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() - - # Verify priority of tx is MAX_PRIORITY, defined as 1E+16 (10000000000000000) - mempool = self.nodes[0].getrawmempool(True) - assert(Decimal(mempool[mytxid]['startingpriority']) == Decimal('1E+16')) - self.nodes[2].generate(1) self.sync_all() @@ -105,11 +96,6 @@ def run_test(self): mytxid = wait_and_assert_operationid_status(self.nodes[1], myopid) self.sync_all() - - # Verify priority of tx is MAX_PRIORITY, defined as 1E+16 (10000000000000000) - mempool = self.nodes[1].getrawmempool(True) - assert(Decimal(mempool[mytxid]['startingpriority']) == Decimal('1E+16')) - self.nodes[2].generate(1) self.sync_all() @@ -170,16 +156,13 @@ def run_test(self): # Make sure we get a useful error when trying to send to both sprout and sapling node4_sproutaddr = self.nodes[3].z_getnewaddress('sprout') node4_saplingaddr = self.nodes[3].z_getnewaddress('sapling') - try: - self.nodes[1].z_sendmany( - taddr1, - [{'address': node4_sproutaddr, 'amount': Decimal('2.5')}, - {'address': node4_saplingaddr, 'amount': Decimal('2.5') - DEFAULT_FEE}], - 1, DEFAULT_FEE, 'AllowRevealedSenders' - ) - raise AssertionError("Should have thrown an exception") - except JSONRPCException as e: - assert_equal("Sending funds into the Sprout value pool is not supported by z_sendmany", e.error['message']) + myopid = self.nodes[1].z_sendmany( + taddr1, + [{'address': node4_sproutaddr, 'amount': Decimal('2.5')}, + {'address': node4_saplingaddr, 'amount': Decimal('2.5') - DEFAULT_FEE}], + 1, DEFAULT_FEE, 'AllowRevealedSenders' + ) + wait_and_assert_operationid_status(self.nodes[1], myopid, "failed", "Sending funds into the Sprout pool is no longer supported.") if __name__ == '__main__': WalletSaplingTest().main() diff --git a/depend/zcash/qa/rpc-tests/wallet_sendmany_any_taddr.py b/depend/zcash/qa/rpc-tests/wallet_sendmany_any_taddr.py index d4cab6d7a..64df0b662 100755 --- a/depend/zcash/qa/rpc-tests/wallet_sendmany_any_taddr.py +++ b/depend/zcash/qa/rpc-tests/wallet_sendmany_any_taddr.py @@ -19,13 +19,13 @@ # Test ANY_TADDR special string in z_sendmany class WalletSendManyAnyTaddr(BitcoinTestFramework): def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, - [[ - "-txexpirydelta=%d" % TX_EXPIRY_DELTA, - "-allowdeprecated=getnewaddress", - "-allowdeprecated=z_getnewaddress", - "-allowdeprecated=z_getbalance", - ]] * self.num_nodes) + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', + '-txexpirydelta=%d' % TX_EXPIRY_DELTA, + '-allowdeprecated=getnewaddress', + '-allowdeprecated=z_getnewaddress', + '-allowdeprecated=z_getbalance', + ]] * self.num_nodes) def run_test(self): # Sanity-check the test harness @@ -77,7 +77,7 @@ def run_test(self): self.nodes[3].z_sendmany( 'ANY_TADDR', [{'address': recipient, 'amount': 100}], - 1, DEFAULT_FEE, 'AllowRevealedSenders'), + 1, DEFAULT_FEE, 'AllowFullyTransparent'), ) self.sync_all() @@ -97,7 +97,7 @@ def run_test(self): self.nodes[3].z_sendmany( 'ANY_TADDR', [{'address': recipient, 'amount': 20}], - 1, DEFAULT_FEE, 'AllowRevealedSenders'), + 1, DEFAULT_FEE, 'AllowFullyTransparent'), ) self.sync_all() @@ -112,7 +112,7 @@ def run_test(self): 'ANY_TADDR', [{'address': recipient, 'amount': 20}], 1, DEFAULT_FEE, 'AllowRevealedSenders') - wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient funds: have 14.99998, need 20.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.") + wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient funds: have 14.99998, need 20.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR, any transparent recipients are included, or if the `privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker.") # Create an expired transaction on node 3. self.split_network() @@ -144,7 +144,7 @@ def run_test(self): 'ANY_TADDR', [{'address': recipient, 'amount': 13}], 1, DEFAULT_FEE, 'AllowRevealedSenders'), - "failed", "Insufficient funds: have 0.00, need 13.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.") + "failed", "Insufficient funds: have 0.00, need 13.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR, any transparent recipients are included, or if the `privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker.") if __name__ == '__main__': WalletSendManyAnyTaddr().main() diff --git a/depend/zcash/qa/rpc-tests/wallet_shieldcoinbase.py b/depend/zcash/qa/rpc-tests/wallet_shieldcoinbase.py index b9a87470c..478d4c622 100755 --- a/depend/zcash/qa/rpc-tests/wallet_shieldcoinbase.py +++ b/depend/zcash/qa/rpc-tests/wallet_shieldcoinbase.py @@ -9,6 +9,7 @@ start_node, connect_nodes_bi, sync_blocks, sync_mempools, \ wait_and_assert_operationid_status, get_coinbase_address, DEFAULT_FEE, \ NU5_BRANCH_ID, nuparams +from test_framework.zip317 import conventional_fee from decimal import Decimal @@ -19,6 +20,7 @@ def setup_chain(self): def setup_network(self, split=False): args = [ + '-minrelaytxfee=0', '-regtestprotectcoinbase', '-debug=zrpcunsafe', nuparams(NU5_BRANCH_ID, self.nu5_activation), @@ -69,7 +71,7 @@ def run_test (self): self.nodes[2].z_shieldcoinbase(mytaddr, myzaddr) except JSONRPCException as e: errorString = e.error['message'] - assert_equal("Could not find any coinbase funds to shield" in errorString, True) + assert_equal(errorString, "Invalid from address, no payment source found for address.") # Shielding will fail because fee is negative try: @@ -83,14 +85,7 @@ def run_test (self): self.nodes[0].z_shieldcoinbase("*", myzaddr, Decimal('21000000.00000001')) except JSONRPCException as e: errorString = e.error['message'] - assert_equal("Amount out of range" in errorString, True) - - # Shielding will fail because fee is larger than sum of utxos - try: - self.nodes[0].z_shieldcoinbase("*", myzaddr, 999) - except JSONRPCException as e: - errorString = e.error['message'] - assert_equal("Insufficient coinbase funds" in errorString, True) + assert_equal(errorString, "Amount out of range") # Shielding will fail because limit parameter must be at least 0 try: @@ -116,7 +111,7 @@ def run_test (self): # Confirm balances and that do_not_shield_taddr containing funds of 10 was left alone assert_equal(self.nodes[0].getbalance(), 10) assert_equal(self.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) - self.test_check_balance_zaddr(self.nodes[0], Decimal('40.0') - DEFAULT_FEE) + self.test_check_balance_zaddr(self.nodes[0], Decimal('40.0') - conventional_fee(4)) assert_equal(self.nodes[1].getbalance(), 20) assert_equal(self.nodes[2].getbalance(), 30) @@ -128,7 +123,7 @@ def run_test (self): self.sync_all() assert_equal(self.nodes[0].getbalance(), 10) - self.test_check_balance_zaddr(self.nodes[0], Decimal('70.0') - DEFAULT_FEE) + self.test_check_balance_zaddr(self.nodes[0], Decimal('70.0') - conventional_fee(4)) assert_equal(self.nodes[1].getbalance(), 30) assert_equal(self.nodes[2].getbalance(), 0) @@ -175,13 +170,13 @@ def verify_locking(first, second, limit): self.nodes[0].generate(200) self.sync_all() mytaddr = get_coinbase_address(self.nodes[0], 100) - result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, DEFAULT_FEE) + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, DEFAULT_FEE, None, 'DEADBEEF') assert_equal(result["shieldingUTXOs"], Decimal('50')) assert_equal(result["remainingUTXOs"], Decimal('50')) wait_and_assert_operationid_status(self.nodes[0], result['opid']) # Verify maximum number of utxos which node 0 can shield can be set by the limit parameter - result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, DEFAULT_FEE, 33) + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, DEFAULT_FEE, 33, None) assert_equal(result["shieldingUTXOs"], Decimal('33')) assert_equal(result["remainingUTXOs"], Decimal('17')) wait_and_assert_operationid_status(self.nodes[0], result['opid']) @@ -192,4 +187,4 @@ def verify_locking(first, second, limit): self.sync_all() # Note, no "if __name__ == '__main__" and call the test here; it's called from -# pool-specific derived classes in wallet_shieldcoinbase_*.py \ No newline at end of file +# pool-specific derived classes in wallet_shieldcoinbase_*.py diff --git a/depend/zcash/qa/rpc-tests/wallet_shieldingcoinbase.py b/depend/zcash/qa/rpc-tests/wallet_shieldingcoinbase.py index 5a42ea31b..963525dd7 100755 --- a/depend/zcash/qa/rpc-tests/wallet_shieldingcoinbase.py +++ b/depend/zcash/qa/rpc-tests/wallet_shieldingcoinbase.py @@ -10,6 +10,7 @@ start_nodes, connect_nodes_bi, wait_and_assert_operationid_status, \ wait_and_assert_operationid_status_result, get_coinbase_address, \ check_node_log, DEFAULT_FEE +from test_framework.zip317 import conventional_fee, WEIGHT_RATIO_CAP, ZIP_317_FEE import sys import timeit @@ -35,9 +36,11 @@ def setup_chain(self): # Start nodes with -regtestshieldcoinbase to set fCoinbaseMustBeShielded to true. def setup_network(self, split=False): self.nodes = start_nodes(4, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-regtestshieldcoinbase', '-debug=zrpcunsafe', '-allowdeprecated=getnewaddress', + '-allowdeprecated=legacy_privacy', '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', '-allowdeprecated=z_gettotalbalance', @@ -103,7 +106,7 @@ def run_test (self): recipients = [] recipients.append({"address":myzaddr, "amount":Decimal('1.23456789')}) - myopid = self.nodes[0].z_sendmany(mytaddr, recipients, 10, DEFAULT_FEE, 'AllowRevealedSenders') + myopid = self.nodes[0].z_sendmany(mytaddr, recipients, 10, DEFAULT_FEE, 'AllowFullyTransparent') error_result = wait_and_assert_operationid_status_result( self.nodes[0], myopid, "failed", @@ -124,6 +127,7 @@ def run_test (self): self.nodes[3].z_importviewingkey(myviewingkey, "no") # This send will succeed. We send two coinbase utxos totalling 20.0 less a default fee, with no change. + # (This tx fits within the block unpaid action limit.) shieldvalue = Decimal('20.0') - DEFAULT_FEE recipients = [] recipients.append({"address":myzaddr, "amount": shieldvalue}) @@ -178,16 +182,17 @@ def run_test (self): # check balances (the z_sendmany consumes 3 coinbase utxos) resp = self.nodes[0].z_gettotalbalance() assert_equal(Decimal(resp["transparent"]), Decimal('20.0')) - assert_equal(Decimal(resp["private"]), Decimal('20.0') - DEFAULT_FEE) - assert_equal(Decimal(resp["total"]), Decimal('40.0') - DEFAULT_FEE) + assert_equal(Decimal(resp["private"]), shieldvalue) + assert_equal(Decimal(resp["total"]), Decimal('20.0') + shieldvalue) - # The Sprout value pool should reflect the send + # The Sapling value pool should reflect the send saplingvalue = shieldvalue check_value_pool(self.nodes[0], 'sapling', saplingvalue) # A custom fee of 0 is okay. Here the node will send the note value back to itself. + # (This tx fits within the block unpaid action limit.) recipients = [] - recipients.append({"address":myzaddr, "amount": Decimal('20.0') - DEFAULT_FEE}) + recipients.append({"address":myzaddr, "amount": saplingvalue}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('0.0')) mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() @@ -195,8 +200,8 @@ def run_test (self): self.sync_all() resp = self.nodes[0].z_gettotalbalance() assert_equal(Decimal(resp["transparent"]), Decimal('20.0')) - assert_equal(Decimal(resp["private"]), Decimal('20.0') - DEFAULT_FEE) - assert_equal(Decimal(resp["total"]), Decimal('40.0') - DEFAULT_FEE) + assert_equal(Decimal(resp["private"]), saplingvalue) + assert_equal(Decimal(resp["total"]), Decimal('20.0') + saplingvalue) # The Sapling value pool should be unchanged check_value_pool(self.nodes[0], 'sapling', saplingvalue) @@ -208,12 +213,8 @@ def run_test (self): myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, DEFAULT_FEE, 'AllowRevealedRecipients') mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) assert(mytxid is not None) - self.sync_all() - - # check that priority of the tx sending from a zaddr is not 0 - mempool = self.nodes[0].getrawmempool(True) - assert(Decimal(mempool[mytxid]['startingpriority']) >= Decimal('1000000000000')) + self.sync_all() self.nodes[1].generate(1) self.sync_all() @@ -221,18 +222,18 @@ def run_test (self): saplingvalue -= unshieldvalue + DEFAULT_FEE resp = self.nodes[0].z_gettotalbalance() assert_equal(Decimal(resp["transparent"]), Decimal('30.0')) - assert_equal(Decimal(resp["private"]), Decimal('10.0') - 2*DEFAULT_FEE) - assert_equal(Decimal(resp["total"]), Decimal('40.0') - 2*DEFAULT_FEE) + assert_equal(Decimal(resp["private"]), saplingvalue) + assert_equal(Decimal(resp["total"]), Decimal('30.0') + saplingvalue) check_value_pool(self.nodes[0], 'sapling', saplingvalue) # z_sendmany will return an error if there is transparent change output considered dust. # UTXO selection in z_sendmany sorts in ascending order, so smallest utxos are consumed first. # At this point in time, unspent notes all have a value of 10.0. recipients = [] - amount = Decimal('10.0') - DEFAULT_FEE - Decimal('0.00000001') # this leaves change at 1 zatoshi less than dust threshold + amount = Decimal('10.0') - conventional_fee(2) - Decimal('0.00000001') # this leaves change at 1 zatoshi less than dust threshold recipients.append({"address":self.nodes[0].getnewaddress(), "amount":amount }) myopid = self.nodes[0].z_sendmany(mytaddr, recipients, 1) - wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 10.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054); note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 10.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054); note that coinbase outputs will not be selected if you specify ANY_TADDR, any transparent recipients are included, or if the `privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker.") # Send will fail because send amount is too big, even when including coinbase utxos errorString = "" @@ -246,9 +247,9 @@ def run_test (self): recipients = [] recipients.append({"address":self.nodes[1].getnewaddress(), "amount":Decimal('10000.0')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients, 1) - wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 10.00, need 10000.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 10.00, need 10000.0001; note that coinbase outputs will not be selected if you specify ANY_TADDR, any transparent recipients are included, or if the `privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker.") myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, DEFAULT_FEE, 'AllowRevealedRecipients') - wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 9.99998, need 10000.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR or if any transparent recipients are included.") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient funds: have 9.99998, need 10000.00001; note that coinbase outputs will not be selected if you specify ANY_TADDR, any transparent recipients are included, or if the `privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker.") # Send will fail because of insufficient funds unless sender uses coinbase utxos try: @@ -257,14 +258,11 @@ def run_test (self): errorString = e.error['message'] assert_equal("Insufficient funds, coinbase funds can only be spent after they have been sent to a zaddr" in errorString, True) - # Verify that mempools accept tx with joinsplits which have at least the default z_sendmany fee. - # If this test passes, it confirms that issue #1851 has been resolved, where sending from - # a zaddr to 1385 taddr recipients fails because the default fee was considered too low - # given the tx size, resulting in mempool rejection. + # Verify that mempools accept tx with shielded outputs and that pay at least the conventional fee. errorString = '' recipients = [] - num_t_recipients = 2500 - amount_per_recipient = Decimal('0.00000546') # dust threshold + num_t_recipients = 1998 # stay just under the absurdly-high-fee error + amount_per_recipient = Decimal('0.00000054') # dust threshold # Note that regtest chainparams does not require standard tx, so setting the amount to be # less than the dust threshold, e.g. 0.00000001 will not result in mempool rejection. start_time = timeit.default_timer() @@ -283,7 +281,7 @@ def run_test (self): self.nodes[0].getinfo() # Issue #2263 Workaround END - myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, DEFAULT_FEE, 'AllowRevealedRecipients') + myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, ZIP_317_FEE, 'AllowRevealedRecipients') try: wait_and_assert_operationid_status(self.nodes[0], myopid) except JSONRPCException as e: @@ -297,9 +295,11 @@ def run_test (self): self.nodes[1].generate(1) self.sync_all() + many_recipient_fee = conventional_fee(2+num_t_recipients) # 2+ for padded Sapling input/change + # check balance node2balance = amount_per_recipient * num_t_recipients - saplingvalue -= node2balance + DEFAULT_FEE + saplingvalue -= node2balance + many_recipient_fee assert_equal(self.nodes[2].getbalance(), node2balance) check_value_pool(self.nodes[0], 'sapling', saplingvalue) @@ -317,12 +317,11 @@ def run_test (self): errorString = e.error['message'] assert_equal("Amount out of range" in errorString, True) - # Send will fail because fee is larger than sum of outputs - try: - self.nodes[0].z_sendmany(myzaddr, recipients, 1, (amount_per_recipient * num_t_recipients) + Decimal('0.00000001')) - except JSONRPCException as e: - errorString = e.error['message'] - assert_equal("is greater than the sum of outputs" in errorString, True) + # Send will fail because fee is more than `WEIGHT_RATIO_CAP * conventional_fee` + num_fewer_recipients = 400 + fewer_recipients = recipients[0:num_fewer_recipients] + myopid = self.nodes[0].z_sendmany(myzaddr, fewer_recipients, 1, (WEIGHT_RATIO_CAP * conventional_fee(2+num_fewer_recipients)) + Decimal('0.00000001')) + wait_and_assert_operationid_status(self.nodes[0], myopid, 'failed', 'Fee 0.08040001 is greater than 4 times the conventional fee for this tx (which is 0.0201). There is no prioritisation benefit to a fee this large (see https://zips.z.cash/zip-0317#recommended-algorithm-for-block-template-construction), and it likely indicates a mistake in setting the fee.') # Send will succeed because the balance of non-coinbase utxos is 10.0 try: diff --git a/depend/zcash/qa/rpc-tests/wallet_treestate.py b/depend/zcash/qa/rpc-tests/wallet_treestate.py index e1afb60c5..29f65b492 100755 --- a/depend/zcash/qa/rpc-tests/wallet_treestate.py +++ b/depend/zcash/qa/rpc-tests/wallet_treestate.py @@ -7,6 +7,7 @@ from test_framework.util import assert_equal, initialize_chain_clean, \ start_nodes, connect_nodes_bi, wait_and_assert_operationid_status, \ get_coinbase_address, DEFAULT_FEE +from test_framework.zip317 import conventional_fee import time from decimal import Decimal @@ -20,6 +21,7 @@ def setup_chain(self): # Start nodes with -regtestshieldcoinbase to set fCoinbaseMustBeShielded to true. def setup_network(self, split=False): self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-regtestshieldcoinbase', '-debug=zrpc', '-allowdeprecated=z_getnewaddress', @@ -79,7 +81,7 @@ def run_test (self): # the z_sendmany implementation because there are only two inputs per joinsplit. recipients = [] recipients.append({"address": self.nodes[2].z_getnewaddress(), "amount": Decimal('18.0')}) - recipients.append({"address": self.nodes[2].z_getnewaddress(), "amount": Decimal('12.0') - 4*DEFAULT_FEE}) + recipients.append({"address": self.nodes[2].z_getnewaddress(), "amount": Decimal('12.0') - 4 * conventional_fee(3)}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1) # Wait for Tx 2 to begin executing... diff --git a/depend/zcash/qa/rpc-tests/wallet_unified_change.py b/depend/zcash/qa/rpc-tests/wallet_unified_change.py index 1384d642d..5c618c3e3 100755 --- a/depend/zcash/qa/rpc-tests/wallet_unified_change.py +++ b/depend/zcash/qa/rpc-tests/wallet_unified_change.py @@ -22,7 +22,8 @@ def __init__(self): self.num_nodes = 4 def setup_nodes(self): - return start_nodes(self.num_nodes, self.options.tmpdir, [[ + return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 201), ]] * self.num_nodes) diff --git a/depend/zcash/qa/rpc-tests/wallet_z_sendmany.py b/depend/zcash/qa/rpc-tests/wallet_z_sendmany.py index f60f2239e..42efdb79d 100755 --- a/depend/zcash/qa/rpc-tests/wallet_z_sendmany.py +++ b/depend/zcash/qa/rpc-tests/wallet_z_sendmany.py @@ -22,8 +22,13 @@ # Test wallet address behaviour across network upgrades class WalletZSendmanyTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.cache_behavior = 'sprout' + def setup_network(self, split=False): - self.nodes = start_nodes(3, self.options.tmpdir, [[ + self.nodes = start_nodes(3, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', nuparams(NU5_BRANCH_ID, 238), '-allowdeprecated=getnewaddress', '-allowdeprecated=z_getnewaddress', @@ -102,7 +107,7 @@ def run_test(self): # send node 2 taddr to zaddr recipients = [] recipients.append({"address":myzaddr, "amount":7}) - opid = self.nodes[2].z_sendmany(mytaddr, recipients, 1, DEFAULT_FEE, 'AllowRevealedSenders') + opid = self.nodes[2].z_sendmany(mytaddr, recipients, 1, DEFAULT_FEE, 'AllowFullyTransparent') mytxid = wait_and_assert_operationid_status(self.nodes[2], opid) self.sync_all() @@ -110,12 +115,13 @@ def run_test(self): # check balances zsendmanynotevalue = Decimal('7.0') zsendmanyfee = DEFAULT_FEE - node2utxobalance = Decimal('260.00000000') - zsendmanynotevalue - zsendmanyfee + node2sproutbalance = Decimal('50.00000000') + node2utxobalance = Decimal('210.00000000') - zsendmanynotevalue - zsendmanyfee # check shielded balance status with getwalletinfo wallet_info = self.nodes[2].getwalletinfo() assert_equal(Decimal(wallet_info["shielded_unconfirmed_balance"]), zsendmanynotevalue) - assert_equal(Decimal(wallet_info["shielded_balance"]), Decimal('0.0')) + assert_equal(Decimal(wallet_info["shielded_balance"]), node2sproutbalance) self.nodes[2].generate(10) self.sync_all() @@ -130,13 +136,13 @@ def run_test(self): # check via z_gettotalbalance resp = self.nodes[2].z_gettotalbalance() assert_equal(Decimal(resp["transparent"]), node2utxobalance) - assert_equal(Decimal(resp["private"]), zbalance) - assert_equal(Decimal(resp["total"]), node2utxobalance + zbalance) + assert_equal(Decimal(resp["private"]), node2sproutbalance + zbalance) + assert_equal(Decimal(resp["total"]), node2utxobalance + node2sproutbalance + zbalance) # check confirmed shielded balance with getwalletinfo wallet_info = self.nodes[2].getwalletinfo() assert_equal(Decimal(wallet_info["shielded_unconfirmed_balance"]), Decimal('0.0')) - assert_equal(Decimal(wallet_info["shielded_balance"]), zsendmanynotevalue) + assert_equal(Decimal(wallet_info["shielded_balance"]), node2sproutbalance + zsendmanynotevalue) # there should be at least one Sapling output mytxdetails = self.nodes[2].getrawtransaction(mytxid, 1) @@ -152,6 +158,11 @@ def run_test(self): # The following assertion might fail nondeterministically # assert_equal(node2balance, Decimal('16.99799000')) + # try sending with a memo to a taddr, which should fail + recipients = [{"address":self.nodes[0].getnewaddress(), "amount":1, "memo":"DEADBEEF"}] + opid = self.nodes[2].z_sendmany(myzaddr, recipients, 1, DEFAULT_FEE, 'AllowRevealedRecipients') + wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', 'Failed to build transaction: Memos cannot be sent to transparent addresses.') + recipients = [] recipients.append({"address":self.nodes[0].getnewaddress(), "amount":1}) recipients.append({"address":self.nodes[2].getnewaddress(), "amount":1.0}) @@ -183,9 +194,10 @@ def run_test(self): # If we attempt to spend with the default privacy policy, z_sendmany # fails because it needs to spend transparent coins in a transaction # involving a Unified Address. - revealed_senders_msg = 'This transaction requires selecting transparent coins, which is not enabled by default because it will publicly reveal transaction senders and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedSenders` or weaker if you wish to allow this transaction to proceed anyway.' + unified_address_msg = 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.' + revealed_senders_msg = 'Insufficient funds: have 0.00, need 10.00; note that coinbase outputs will not be selected if you specify ANY_TADDR, any transparent recipients are included, or if the `privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker.' opid = self.nodes[2].z_sendmany(source, recipients, 1, 0) - wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', revealed_senders_msg) + wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', unified_address_msg) # We can't create a transaction with an unknown privacy policy. assert_raises_message( @@ -196,13 +208,13 @@ def run_test(self): # If we set any policy that does not include AllowRevealedSenders, # z_sendmany also fails. - for policy in [ - 'FullPrivacy', - 'AllowRevealedAmounts', - 'AllowRevealedRecipients', + for (policy, msg) in [ + ('FullPrivacy', unified_address_msg), + ('AllowRevealedAmounts', revealed_senders_msg), + ('AllowRevealedRecipients', revealed_senders_msg), ]: opid = self.nodes[2].z_sendmany(source, recipients, 1, 0, policy) - wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', revealed_senders_msg) + wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', msg) # By setting the correct policy, we can create the transaction. opid = self.nodes[2].z_sendmany(source, recipients, 1, 0, 'AllowRevealedSenders') @@ -321,20 +333,26 @@ def run_test(self): # If we try to send 3 ZEC from n1ua0, it will fail with too-few funds. recipients = [{"address":n0ua0, "amount":3}] - linked_addrs_msg = 'Insufficient funds: have 2.00, need 3.00. (This transaction may require selecting transparent coins that were sent to multiple Unified Addresses, which is not enabled by default because it would create a public link between the transparent receivers of these addresses. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowLinkingAccountAddresses` or weaker if you wish to allow this transaction to proceed anyway.)' + linked_addrs_with_coinbase_note_msg = 'Insufficient funds: have 2.00, need 3.00; note that coinbase outputs will not be selected if you specify ANY_TADDR, any transparent recipients are included, or if the `privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker. (This transaction may require selecting transparent coins that were sent to multiple Unified Addresses, which is not enabled by default because it would create a public link between the transparent receivers of these addresses. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowLinkingAccountAddresses` or weaker if you wish to allow this transaction to proceed anyway.)' + linked_addrs_without_coinbase_note_msg = 'Insufficient funds: have 2.00, need 3.00. (This transaction may require selecting transparent coins that were sent to multiple Unified Addresses, which is not enabled by default because it would create a public link between the transparent receivers of these addresses. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowLinkingAccountAddresses` or weaker if you wish to allow this transaction to proceed anyway.)' + revealed_amounts_msg = 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.' opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, 0) - wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', linked_addrs_msg) + wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', revealed_amounts_msg) # If we try it again with any policy that is too strong, it also fails. - for policy in [ - 'FullPrivacy', - 'AllowRevealedAmounts', - 'AllowRevealedRecipients', - 'AllowRevealedSenders', - 'AllowFullyTransparent', + for (policy, msg) in [ + ('FullPrivacy', revealed_amounts_msg), + ('AllowRevealedAmounts', linked_addrs_with_coinbase_note_msg), + ('AllowRevealedRecipients', linked_addrs_with_coinbase_note_msg), + ('AllowRevealedSenders', linked_addrs_without_coinbase_note_msg), + ('AllowFullyTransparent', linked_addrs_without_coinbase_note_msg), ]: opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, 0, policy) - wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', linked_addrs_msg) + wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', msg) + + # If we try to send just a bit less than we have, it will fail, complaining about dust + opid = self.nodes[1].z_sendmany(n1ua0, [{"address":n0ua0, "amount":3.9999999}], 1, 0, 'AllowLinkingAccountAddresses') + wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', 'Insufficient funds: have 4.00, need 0.00000044 more to avoid creating invalid change output 0.0000001 (dust threshold is 0.00000054).') # Once we provide a sufficiently-weak policy, the transaction succeeds. opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, 0, 'AllowLinkingAccountAddresses') @@ -356,6 +374,20 @@ def run_test(self): assert_equal(self.nodes[1].z_getbalance(n1ua0), 1) assert_equal(self.nodes[1].z_getbalance(n1ua1), 1) + # + # Test Orchard-only UA before NU5 + # + + n0orchard_only = self.nodes[0].z_getaddressforaccount(n0account0, ["orchard"])['address'] + recipients = [{"address":n0orchard_only, "amount":1}] + for (policy, msg) in [ + ('FullPrivacy', 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.'), + ('AllowRevealedAmounts', 'This transaction would send to a transparent receiver of a unified address, which is not enabled by default because it will publicly reveal transaction recipients and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedRecipients` or weaker if you wish to allow this transaction to proceed anyway.'), + ('AllowRevealedRecipients', 'Could not send to an Orchard-only receiver despite a lax privacy policy, because NU5 has not been activated yet.'), + ]: + opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, 0, policy) + wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', msg) + # # Test NoPrivacy policy # @@ -363,8 +395,8 @@ def run_test(self): # Send some legacy transparent funds to n1ua0, creating Sapling outputs. source = get_coinbase_address(self.nodes[2]) recipients = [{"address":n1ua0, "amount":10}] - # This requires the AllowRevealedSenders policy... - opid = self.nodes[2].z_sendmany(source, recipients, 1, 0) + # This requires the AllowRevealedSenders policy, but we specify only AllowRevealedAmounts... + opid = self.nodes[2].z_sendmany(source, recipients, 1, 0, 'AllowRevealedAmounts') wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', revealed_senders_msg) # ... which we can always override with the NoPrivacy policy. opid = self.nodes[2].z_sendmany(source, recipients, 1, 0, 'NoPrivacy') @@ -389,6 +421,20 @@ def run_test(self): self.nodes[1].generate(10) self.sync_all() + # + # Test sending Sprout funds to Orchard-only UA + # + + sproutAddr = self.nodes[2].listaddresses()[0]['sprout']['addresses'][0] + recipients = [{"address":n0orchard_only, "amount":100}] + for (policy, msg) in [ + ('FullPrivacy', 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.'), + ('AllowRevealedAmounts', 'This transaction would send to a transparent receiver of a unified address, which is not enabled by default because it will publicly reveal transaction recipients and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedRecipients` or weaker if you wish to allow this transaction to proceed anyway.'), + ('AllowRevealedRecipients', 'Could not send to an Orchard-only receiver despite a lax privacy policy, because you are sending from the Sprout pool and there is no transaction version that supports both Sprout and Orchard.'), + ]: + opid = self.nodes[2].z_sendmany(sproutAddr, recipients, 1, 0, policy) + wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', msg) + # # Test AllowRevealedAmounts policy # @@ -402,7 +448,7 @@ def run_test(self): recipients = [{"address":n0ua1, "amount": 6}] # Should fail under default and 'FullPrivacy' policies ... - revealed_amounts_msg = 'Sending from the Sapling shielded pool to the Orchard shielded pool is not enabled by default because it will publicly reveal the transaction amount. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.' + revealed_amounts_msg = 'Could not send to a shielded receiver of a unified address without spending funds from a different pool, which would reveal transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` or weaker if you wish to allow this transaction to proceed anyway.' opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, 0) wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', revealed_amounts_msg) @@ -449,5 +495,24 @@ def run_test(self): {'pools': {'orchard': {'valueZat': 200000000}}, 'minimum_confirmations': 1}, self.nodes[0].z_getbalanceforaccount(n0account0)) + + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # + # Test transparent change + # + + recipients = [{"address":n0ua1, "amount": 4}] + # Should fail because this generates transparent change, but we don’t have + # `AllowRevealedRecipients` + opid = self.nodes[2].z_sendmany(mytaddr, recipients, 1, 0, 'AllowRevealedSenders') + wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', "This transaction would have transparent change, which is not enabled by default because it will publicly reveal the change address and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedRecipients` or weaker if you wish to allow this transaction to proceed anyway.") + + # Should succeed once we include `AllowRevealedRecipients` + opid = self.nodes[2].z_sendmany(mytaddr, recipients, 1, 0, 'AllowFullyTransparent') + wait_and_assert_operationid_status(self.nodes[2], opid) + if __name__ == '__main__': WalletZSendmanyTest().main() diff --git a/depend/zcash/qa/rpc-tests/zkey_import_export.py b/depend/zcash/qa/rpc-tests/zkey_import_export.py index 3525c336d..b29fbac90 100755 --- a/depend/zcash/qa/rpc-tests/zkey_import_export.py +++ b/depend/zcash/qa/rpc-tests/zkey_import_export.py @@ -24,6 +24,7 @@ def setup_chain(self): def setup_network(self, split=False): self.nodes = start_nodes(5, self.options.tmpdir, extra_args=[[ + '-minrelaytxfee=0', '-allowdeprecated=getnewaddress', '-allowdeprecated=z_getnewaddress', '-allowdeprecated=z_getbalance', diff --git a/depend/zcash/qa/supply-chain/audits.toml b/depend/zcash/qa/supply-chain/audits.toml index 2df790aa5..d0dda94f2 100644 --- a/depend/zcash/qa/supply-chain/audits.toml +++ b/depend/zcash/qa/supply-chain/audits.toml @@ -19,6 +19,11 @@ criteria = "safe-to-deploy" delta = "0.4.3 -> 0.5.1" notes = "Adds an AeadCore::generate_nonce function to generate random nonces, given a CryptoRng." +[[audits.aead]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.5.1 -> 0.5.2" + [[audits.anyhow]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -31,22 +36,92 @@ criteria = "safe-to-deploy" delta = "1.0.61 -> 1.0.65" notes = "Build script changes just alter what it is probing for; no difference in side effects." +[[audits.anyhow]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.68 -> 1.0.69" + +[[audits.anyhow]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "1.0.69 -> 1.0.70" + +[[audits.arrayref]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.3.6 -> 0.3.7" + +[[audits.bech32]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.1 -> 0.9.1" + [[audits.bellman]] who = "Jack Grigg " criteria = ["crypto-reviewed", "safe-to-deploy"] delta = "0.13.0 -> 0.13.1" notes = "Adds multi-threaded batch validation, which I checked against the existing single-threaded batch validation." +[[audits.bellman]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.13.1 -> 0.14.0" + +[[audits.bip0039]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.9.0 -> 0.10.1" + +[[audits.blake2b_simd]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.0 -> 1.0.1" +notes = "Switches to `constant_time_eq 0.2.4`, which bumps its MSRV to 1.59." + +[[audits.blake2s_simd]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.0 -> 1.0.1" +notes = "Switches to `constant_time_eq 0.2.4`, which bumps its MSRV to 1.59." + +[[audits.block-buffer]] +who = "Jack Grigg " +criteria = ["crypto-reviewed", "safe-to-deploy"] +delta = "0.10.3 -> 0.10.4" +notes = "Adds panics to prevent a block size of zero from causing unsoundness." + [[audits.bls12_381]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.7.0 -> 0.7.1" +[[audits.bls12_381]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.7.1 -> 0.8.0" +notes = "I previously reviewed the crypto-sensitive portions of these changes as well." + +[[audits.bumpalo]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "3.11.1 -> 3.12.0" +notes = "Changes to `unsafe` code are to replace `mem::forget` uses with `ManuallyDrop`." + [[audits.byte-slice-cast]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.2.1 -> 1.2.2" +[[audits.bytes]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.3.0 -> 1.4.0" +notes = """ +Adds a `mem::forget` as part of avoiding `Vec::into_boxed_slice` when it would reallocate. +I checked that the required semantics of `mem::forget` are maintained, but it seems like +`ManuallyDrop` should also work here and be compatible with their MSRV. +""" + [[audits.chacha20]] who = "Jack Grigg " criteria = ["crypto-reviewed", "safe-to-deploy"] @@ -76,6 +151,12 @@ criteria = "safe-to-deploy" delta = "0.3.0 -> 0.4.3" notes = "Significant rework of (mainly RustCrypto-internal) APIs." +[[audits.cipher]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.4.3 -> 0.4.4" +notes = "Adds panics to prevent a block size of zero from causing unsoundness." + [[audits.clearscreen]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -92,12 +173,55 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.0.11 -> 2.0.0" +[[audits.clearscreen]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.0.0 -> 2.0.1" + +[[audits.constant_time_eq]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.2.4 -> 0.2.5" +notes = "No code changes." + [[audits.cpufeatures]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.2.2 -> 0.2.5" notes = "Unsafe changes just introduce `#[inline(never)]` wrappers." +[[audits.cpufeatures]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.2.5 -> 0.2.6" + +[[audits.crossbeam-channel]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.5.6 -> 0.5.7" +notes = "Fixes wrapping overflows for large timeouts." + +[[audits.crossbeam-deque]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.2 -> 0.8.3" +notes = "No new code." + +[[audits.crossbeam-epoch]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.9.13 -> 0.9.14" +notes = "Bumps memoffset to 0.8, and marks some BPF and Sony Vita targets as not having atomics." + +[[audits.crossbeam-utils]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.14 -> 0.8.15" +notes = """ +- Fixes a wrapping overflow for large timeouts. +- Marks some BPF and Sony Vita targets as not having atomics. +""" + [[audits.crypto-common]] who = "Jack Grigg " criteria = ["crypto-reviewed", "safe-to-deploy"] @@ -135,6 +259,25 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.0.79 -> 1.0.83" +[[audits.cxx]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.83 -> 1.0.91" +notes = """ +- Buildscript change is only to bump MSRV. +- Only change to C++ side is to fix a memory leak. +""" + +[[audits.cxx]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.91 -> 1.0.92" + +[[audits.cxx]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.92 -> 1.0.94" + [[audits.cxxbridge-flags]] who = "Daira Hopwood " criteria = "safe-to-deploy" @@ -161,6 +304,21 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.0.79 -> 1.0.83" +[[audits.cxxbridge-flags]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.83 -> 1.0.91" + +[[audits.cxxbridge-flags]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.91 -> 1.0.92" + +[[audits.cxxbridge-flags]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.92 -> 1.0.94" + [[audits.cxxbridge-macro]] who = "Daira Hopwood " criteria = "safe-to-deploy" @@ -192,11 +350,56 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.0.79 -> 1.0.83" +[[audits.cxxbridge-macro]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.83 -> 1.0.91" + +[[audits.cxxbridge-macro]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.91 -> 1.0.92" + +[[audits.cxxbridge-macro]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.92 -> 1.0.94" +notes = """ +Migration to `syn 2`. I didn't check the logic, but the changes look reasonable +and I didn't notice anything that seemed like it would adversely change the +generated code. +""" + +[[audits.directories]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "4.0.1 -> 5.0.0" + +[[audits.dirs]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.0.2 -> 4.0.0" +notes = "Some paths change across this upgrade (AFAICT they were bugfixes)." + +[[audits.dirs-sys]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.7 -> 0.4.0" +notes = """ +Changes to `unsafe` code are migrating from `winapi` to `windows-sys`. The APIs +are equivalent, with the `windows-sys` ones being slightly more type-safe. +""" + [[audits.ed25519-zebra]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "3.0.0 -> 3.1.0" +[[audits.either]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.8.0 -> 1.8.1" + [[audits.equihash]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -220,6 +423,105 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.12.0 -> 0.12.1" +[[audits.ff]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.12.1 -> 0.13.0" + +[[audits.fpe]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.6.0 -> 0.6.1" +notes = """ +I am the author of this crate. This release fixes a regression bug in 0.6.0, and +was reviewed by an ECC engineer. +""" + +[[audits.futures-channel]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.25 -> 0.3.26" +notes = "Atomics usage in `Stream::size_hint` impls looks fine." + +[[audits.futures-channel]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.26 -> 0.3.27" + +[[audits.futures-channel]] +who = "Daira Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.27 -> 0.3.28" +notes = "Dependency updates, and an MSRV update to Rust 1.56." + +[[audits.futures-core]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.25 -> 0.3.26" +notes = "Adds optional dependency on `portable-atomic 1` that can be enabled to replace `core::sync::atomic`." + +[[audits.futures-core]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.26 -> 0.3.27" + +[[audits.futures-core]] +who = "Daira Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.27 -> 0.3.28" +notes = """ +Adds an optimization in unsafe code (https://github.com/rust-lang/futures-rs/pull/2723). +The new code in AtomicWaker calls self.waker.get() twice assuming the same resulting pointer, but this appears to be correct because the AtomicWaker is in the required locked state. +""" + +[[audits.futures-task]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.25 -> 0.3.26" + +[[audits.futures-task]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.26 -> 0.3.27" + +[[audits.futures-task]] +who = "Daira Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.26 -> 0.3.28" +notes = "Dependency updates, and an MSRV update to Rust 1.56." + +[[audits.futures-task]] +who = "Daira Emma Hopwood " +criteria = "safe-to-deploy" +delta = "0.3.27 -> 0.3.28" +notes = "Dependency updates, and an MSRV update to Rust 1.56." + +[[audits.futures-util]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.25 -> 0.3.26" +notes = """ +Changes to `unsafe` usage are to split `Either::project` into `Either::as_pin_ref` and +`Either::as_pin_mut`. The new code follows the old code's pattern, and also now has SAFETY +documentation. +""" + +[[audits.futures-util]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.27 -> 0.3.28" +notes = """ +- MSRV bumped to 1.56. +- Changes to `unsafe` code are to move a function call outside an `unsafe fn`, + and to call the `unsafe fn` earlier. The safety requirement of being in the + `POLLING` state appears to be preserved. +""" + +[[audits.generic-array]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.14.6 -> 0.14.7" + [[audits.getrandom]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -229,11 +531,64 @@ Checked that getrandom::wasi::getrandom_inner matches wasi::random_get. Checked that getrandom::util_libc::Weak lock ordering matches std::sys::unix::weak::DlsymWeak. """ +[[audits.getrandom]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.8 -> 0.2.9" +notes = """ +The new `getrandom_uninit` method is introduced by retrofitting every system +implementation to take `&mut [MaybeUninit]` instead of `&mut [u8]`. + +Most implementations are only altered to update their signature, and to +internally cast the slice back to `*mut u8` when writing to it. All of these +backends appear to write bytes to the full length of the slice, so it should be +fully initialized afterwards, upholding the invariants of the new `unsafe` code +in the public APIs. + +- I did not check the behaviour of each implementation's system method to ensure + they never write uninitialized bytes; the code prior to this change already + needed to uphold that invariant as it was writing into `&mut [u8]`. + +The following system implementations have additional `unsafe` code modifications: + +- `custom`: The slice is zero-filled to ensure the `MaybeUninit` doesn't + escape into a system implementation that might not write initialized bytes + into the entire slice. The internal API between registration and usage is also + switched from C ABI to Rust ABI, to guard against potential panics. + +- `emscripten`: New backend, implementation looks reasonable. + +- `hermit`: New backend, writes incrementally to the slice, but ensures that the + entire slice has been written to before returning `Ok(())`. I note that it is + possible for the implementation to loop indefinitely if `sys_read_entropy` + were to always return 0 for some reason. + +- `js`: Adds chunking to limit each write to less than 2^31 (but that seems like + a bugfix). The safety requirements for `Uint8Array::view_mut_raw` appear to be + satisfied. + +- `rdrand`: Code changes to better handle CPU families with broken RDRAND. + +- `solaris_illumos`: Now uses `GRND_RANDOM`. + +- `windows`: Added `RtlGenRandom` fallback for non-UWP Windows. +""" + +[[audits.gimli]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.27.0 -> 0.27.2" + [[audits.group]] who = "Kris Nuttycombe " criteria = "safe-to-deploy" delta = "0.12.0 -> 0.12.1" +[[audits.group]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.12.1 -> 0.13.0" + [[audits.halo2_gadgets]] who = "Jack Grigg " criteria = ["crypto-reviewed", "safe-to-deploy"] @@ -246,6 +601,18 @@ criteria = ["crypto-reviewed", "safe-to-deploy"] delta = "0.1.0 -> 0.2.0" notes = "The ECC core team maintains this crate, and we have reviewed every line." +[[audits.halo2_gadgets]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.2.0 -> 0.3.0" +notes = "The ECC core team maintains this crate, and we have reviewed every line." + +[[audits.halo2_legacy_pdqsort]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +version = "0.1.0" +notes = "The ECC core team maintains this crate, and we have reviewed every line." + [[audits.halo2_proofs]] who = "Jack Grigg " criteria = ["crypto-reviewed", "safe-to-deploy"] @@ -258,12 +625,51 @@ criteria = ["crypto-reviewed", "safe-to-deploy"] delta = "0.1.0 -> 0.2.0" notes = "The ECC core team maintains this crate, and we have reviewed every line." +[[audits.halo2_proofs]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.2.0 -> 0.3.0" +notes = """ +The ECC core team maintains this crate, and we have reviewed every line. +The crate has `deny(unsafe_code)`. +""" + +[[audits.http]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.8 -> 0.2.9" + +[[audits.hyper]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.14.23 -> 0.14.24" +notes = """ +Fixes a bug where memory was reserved based on an adversary-controllable size, before the +corresponding data was received. +""" + +[[audits.hyper]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.14.25 -> 0.14.26" + +[[audits.incrementalmerkletree]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.0 -> 0.3.1" +notes = "Fixes bug in calculating altitudes from tree positions on 32-bit platforms." + [[audits.indexmap]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.8.1 -> 1.9.1" notes = "I'm satisfied that the assertion guarding the new unsafe block is correct." +[[audits.indexmap]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "1.9.2 -> 1.9.3" + [[audits.inout]] who = "Daira Hopwood " criteria = "safe-to-deploy" @@ -275,12 +681,38 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "2.5.0 -> 2.7.1" +[[audits.ipnet]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "2.7.1 -> 2.7.2" + [[audits.itoa]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.0.1 -> 1.0.3" notes = "Update makes no changes to code." +[[audits.itoa]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.5 -> 1.0.6" + +[[audits.js-sys]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.3.60 -> 0.3.61" +notes = """ +- Adds `i64` variants of existing `Atomics` methods, which I checked them against. +- Adds `Array.length` setter and `Intl.RelativeTimeFormat`; I checked these against their + MDN documentation. +""" + +[[audits.jubjub]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.9.0 -> 0.10.0" +notes = "I previously reviewed the crypto-sensitive portions of these changes as well." + [[audits.libm]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -313,6 +745,11 @@ criteria = "safe-to-deploy" delta = "0.4.16 -> 0.4.17" notes = "I confirmed that the unsafe transmutes are fine; NonZeroU128 and NonZeroI128 are `#[repr(transparent)]` wrappers around u128 and i128 respectively." +[[audits.maybe-rayon]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +version = "0.1.1" + [[audits.memuse]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -365,6 +802,22 @@ criteria = "safe-to-deploy" delta = "0.8.4 -> 0.8.5" notes = "The only unsafe changes are in epoll_create1 failure cases. Usage of epoll_create and fcntl looks fine; it is vulnerable to a race condition in multithreaded programs that fork child processes, but epoll_create1 is how you avoid this problem. See the discussion of the O_CLOEXEC flag in the open(2) man page for details." +[[audits.mio]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.5 -> 0.8.6" +notes = """ +New `unsafe` usages: +- `NonZeroU8::new_unchecked`: I verified the constant is non-zero. +- Additional `syscall!(close(socket))` calls before returning errors. +""" + +[[audits.nix]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.26.1 -> 0.26.2" +notes = "Fixes `SockaddrIn6` endianness bug." + [[audits.num-integer]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -381,6 +834,20 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.30.1 -> 0.30.2" +[[audits.object]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.30.2 -> 0.30.3" + +[[audits.once_cell]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.17.0 -> 1.17.1" +notes = """ +Small refactor that reduces the overall amount of `unsafe` code. The new strict provenance +approach looks reasonable. +""" + [[audits.orchard]] who = "Jack Grigg " criteria = ["crypto-reviewed", "safe-to-deploy"] @@ -398,6 +865,32 @@ who = "Kris Nuttycombe " criteria = "safe-to-deploy" delta = "0.2.0 -> 0.3.0" +[[audits.orchard]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.3.0 -> 0.4.0" +notes = "The ECC core team maintains this crate, and we have reviewed every line." + +[[audits.pairing]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.22.0 -> 0.23.0" + +[[audits.parity-scale-codec]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "3.2.1 -> 3.4.0" +notes = "No new code, just refactoring to remove the `full` feature flag." + +[[audits.parity-scale-codec-derive]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "3.1.3 -> 3.1.4" +notes = """ +- Bumps `syn` minimum version. +- Fixes `max_encoded_len()` to pay attention to `#[codec(skip)]` attribute. +""" + [[audits.parking_lot]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -414,32 +907,171 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.9.5 -> 0.9.6" +[[audits.parking_lot_core]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.9.6 -> 0.9.7" + [[audits.pasta_curves]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.4.0 -> 0.4.1" +[[audits.pasta_curves]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.4.1 -> 0.5.1" + +[[audits.pbkdf2]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.9.0 -> 0.10.1" + +[[audits.phf]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.0 -> 0.11.1" +notes = """ +Mostly modernisation, migrating to `PhfBorrow`, and making more things `&'static`. +No unsafe code in the new `OrderedMap` and `OrderedSet` types. +""" + +[[audits.phf_codegen]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.0 -> 0.11.1" +notes = "New codegen and changes to existing codegen look fine." + +[[audits.phf_generator]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.0 -> 0.11.1" +notes = "Just dependency and edition bumps and code formatting." + +[[audits.phf_shared]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.8.0 -> 0.11.1" +notes = """ +Adds `uncased` dependency, and newly generates unsafe code to transmute `&'static str` +into `&'static UncasedStr`. I verified that `UncasedStr` is a `#[repr(transparent)]` +newtype around `str`. +""" + [[audits.poly1305]] who = "Daira Hopwood " criteria = "safe-to-deploy" delta = "0.7.2 -> 0.8.0" notes = "Changes to unsafe (avx2) code look reasonable." +[[audits.proc-macro-crate]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.2.1 -> 1.3.0" +notes = "Migrates from `toml` to `toml_edit`." + +[[audits.proc-macro-crate]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.3.0 -> 1.3.1" +notes = "Bumps MSRV to 1.60." + [[audits.proc-macro2]] who = "Daira Hopwood " criteria = "safe-to-deploy" delta = "1.0.37 -> 1.0.41" +[[audits.proc-macro2]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.49 -> 1.0.51" + +[[audits.proc-macro2]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.51 -> 1.0.52" + +[[audits.proc-macro2]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.54 -> 1.0.56" + [[audits.quanta]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.9.3 -> 0.10.1" +[[audits.quote]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.23 -> 1.0.26" + +[[audits.raw-cpuid]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "10.6.0 -> 10.6.1" + +[[audits.raw-cpuid]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "10.6.1 -> 10.7.0" +notes = """ +Appears to be a move-only change in display code to expose an internal API. +I did not verify that the change was move-only, but there is no unsafe code affected. +""" + +[[audits.reddsa]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.3.0 -> 0.5.0" + +[[audits.redjubjub]] +who = "Daira Emma Hopwood " +criteria = ["safe-to-deploy", "crypto-reviewed"] +version = "0.7.0" +notes = """ +This crate is a thin wrapper around the `reddsa` crate, which I did not review. I also +did not review tests or verify test vectors. + +The comment on `batch::Verifier::verify` has an error in the batch verification equation, +filed as https://github.com/ZcashFoundation/redjubjub/issues/163 . It does not affect the +implementation which just delegates to `reddsa`. `reddsa` has the same comment bug filed as +https://github.com/ZcashFoundation/reddsa/issues/52 , but its batch verification implementation +is correct. (I checked the latter against https://zips.z.cash/protocol/protocol.pdf#reddsabatchvalidate +which has had previous cryptographic review by NCC group; see finding NCC-Zcash2018-009 in +https://research.nccgroup.com/wp-content/uploads/2020/07/NCC_Group_Zcash2018_Public_Report_2019-01-30_v1.3.pdf ). +""" + [[audits.regex]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.7.0 -> 1.7.1" +[[audits.regex]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "1.7.1 -> 1.7.3" + +[[audits.regex-syntax]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.6.28 -> 0.6.29" + +[[audits.rustc-demangle]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.1.21 -> 0.1.22" + +[[audits.rustc-demangle]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.1.22 -> 0.1.23" + +[[audits.ryu]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.12 -> 1.0.13" + [[audits.serde]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -451,6 +1083,16 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.0.143 -> 1.0.145" +[[audits.serde]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.155 -> 1.0.156" + +[[audits.serde]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.159 -> 1.0.160" + [[audits.serde_derive]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -462,22 +1104,69 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.0.143 -> 1.0.145" +[[audits.serde_derive]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.155 -> 1.0.156" + +[[audits.serde_derive]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.159 -> 1.0.160" + +[[audits.serde_json]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.95 -> 1.0.96" + [[audits.sketches-ddsketch]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.1.3 -> 0.2.0" notes = "I did not review the refactor, but there are no unsafe blocks and I didn't see any obvious changes that could result in panics." +[[audits.sketches-ddsketch]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.0 -> 0.2.1" + [[audits.syn]] who = "Daira Hopwood " criteria = "safe-to-deploy" delta = "1.0.91 -> 1.0.98" +[[audits.syn]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "1.0.102 -> 1.0.104" + [[audits.syn]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.0.102 -> 1.0.107" +[[audits.syn]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.107 -> 1.0.109" +notes = "Fixes string literal parsing to only skip specified whitespace characters." + +[[audits.syn]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.0.11 -> 2.0.13" + +[[audits.syn]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "2.0.13 -> 2.0.15" + +[[audits.terminfo]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.7.3 -> 0.7.5" +notes = "Just dependency and edition updates." + [[audits.thiserror]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -502,11 +1191,54 @@ criteria = "safe-to-deploy" delta = "1.0.32 -> 1.0.37" notes = "Proc macro changes migrating to the Provider API look fine." +[[audits.thread_local]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.1.4 -> 1.1.7" +notes = """ +New `unsafe` usage: +- An extra `deallocate_bucket`, to replace a `Mutex::lock` with a `compare_exchange`. +- Setting and getting a `#[thread_local] static mut Option` on nightly. +""" + [[audits.time-macros]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.2.4 -> 0.2.6" +[[audits.time-macros]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.7 -> 0.2.8" +notes = """ +- Only new `unsafe` code takes a `NonZeroU16` at proc-macro evaluation time and hard-codes + its contents into a `NonZeroU16::new_unchecked` constructor, which is safe. +- Bumps MSRV to 1.63. +""" + +[[audits.tinyvec_macros]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.1.0 -> 0.1.1" +notes = "Adds `#![forbid(unsafe_code)]` and license files." + +[[audits.toml_datetime]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +version = "0.5.1" +notes = "Crate has `#![forbid(unsafe_code)]`, no `unwrap / expect / panic`, no ambient capabilities." + +[[audits.toml_datetime]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.5.1 -> 0.6.1" +notes = "Fixes a bug in parsing negative minutes in datetime string offsets." + +[[audits.toml_edit]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "0.19.7 -> 0.19.8" + [[audits.try-lock]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -523,12 +1255,34 @@ who = "Daira Hopwood " criteria = "safe-to-deploy" version = "1.0.2" +[[audits.unicode-ident]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.6 -> 1.0.8" + [[audits.universal-hash]] who = "Daira Hopwood " criteria = "safe-to-deploy" delta = "0.4.1 -> 0.5.0" notes = "I checked correctness of to_blocks which uses unsafe code in a safe function." +[[audits.wasm-bindgen-shared]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.83 -> 0.2.84" +notes = "Bumps the schema version to add `linked_modules`." + +[[audits.which]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "4.3.0 -> 4.4.0" +notes = "New APIs are remixes of existing code." + +[[audits.windows-targets]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.42.1 -> 0.42.2" + [[audits.windows_aarch64_gnullvm]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -538,6 +1292,12 @@ This is a Windows API bindings library maintained by Microsoft themselves. Changes are to a bundled binary library; it looks like these were accidentally left out of 0.42.0. """ +[[audits.windows_aarch64_gnullvm]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.42.1 -> 0.42.2" +notes = "This is an opaque Windows API bindings library maintained by Microsoft." + [[audits.windows_aarch64_msvc]] who = "Jack Grigg " criteria = "safe-to-run" @@ -558,6 +1318,12 @@ This is a Windows API bindings library maintained by Microsoft themselves. Changes are to a bundled binary library; it looks like these were accidentally left out of 0.42.0. """ +[[audits.windows_aarch64_msvc]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.42.1 -> 0.42.2" +notes = "This is an opaque Windows API bindings library maintained by Microsoft." + [[audits.windows_i686_gnu]] who = "Jack Grigg " criteria = "safe-to-run" @@ -578,6 +1344,12 @@ This is a Windows API bindings library maintained by Microsoft themselves. Changes are to a bundled binary library; it looks like these were accidentally left out of 0.42.0. """ +[[audits.windows_i686_gnu]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.42.1 -> 0.42.2" +notes = "This is an opaque Windows API bindings library maintained by Microsoft." + [[audits.windows_i686_msvc]] who = "Jack Grigg " criteria = "safe-to-run" @@ -598,6 +1370,12 @@ This is a Windows API bindings library maintained by Microsoft themselves. Changes are to a bundled binary library; it looks like these were accidentally left out of 0.42.0. """ +[[audits.windows_i686_msvc]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.42.1 -> 0.42.2" +notes = "This is an opaque Windows API bindings library maintained by Microsoft." + [[audits.windows_x86_64_gnu]] who = "Jack Grigg " criteria = "safe-to-run" @@ -618,6 +1396,12 @@ This is a Windows API bindings library maintained by Microsoft themselves. Changes are to a bundled binary library; it looks like these were accidentally left out of 0.42.0. """ +[[audits.windows_x86_64_gnu]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.42.1 -> 0.42.2" +notes = "This is an opaque Windows API bindings library maintained by Microsoft." + [[audits.windows_x86_64_gnullvm]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -627,6 +1411,12 @@ This is a Windows API bindings library maintained by Microsoft themselves. Changes are to a bundled binary library; it looks like these were accidentally left out of 0.42.0. """ +[[audits.windows_x86_64_gnullvm]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.42.1 -> 0.42.2" +notes = "This is an opaque Windows API bindings library maintained by Microsoft." + [[audits.windows_x86_64_msvc]] who = "Jack Grigg " criteria = "safe-to-run" @@ -647,6 +1437,12 @@ This is a Windows API bindings library maintained by Microsoft themselves. Changes are to a bundled binary library; it looks like these were accidentally left out of 0.42.0. """ +[[audits.windows_x86_64_msvc]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.42.1 -> 0.42.2" +notes = "This is an opaque Windows API bindings library maintained by Microsoft." + [[audits.wyz]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -664,6 +1460,12 @@ who = "Kris Nuttycombe " criteria = "safe-to-deploy" delta = "0.1.0 -> 0.2.0" +[[audits.zcash_address]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.2.0 -> 0.2.1" +notes = "The ECC core team maintains this crate, and we have reviewed every line." + [[audits.zcash_encoding]] who = "Jack Grigg " criteria = "safe-to-deploy" @@ -692,6 +1494,12 @@ who = "Kris Nuttycombe " criteria = "safe-to-deploy" delta = "0.1.0 -> 0.2.0" +[[audits.zcash_note_encryption]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.2.0 -> 0.3.0" +notes = "The ECC core team maintains this crate, and we have reviewed every line." + [[audits.zcash_primitives]] who = "Jack Grigg " criteria = ["crypto-reviewed", "safe-to-deploy"] @@ -715,6 +1523,36 @@ criteria = "safe-to-deploy" delta = "0.8.1 -> 0.9.1" notes = "The ECC core team maintains this crate, and we have reviewed every line." +[[audits.zcash_primitives]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.9.1 -> 0.10.0" +notes = "The ECC core team maintains this crate, and we have reviewed every line." + +[[audits.zcash_primitives]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.10.0 -> 0.10.1" +notes = """ +The ECC core team maintains this crate, and we have reviewed every line. +This point release temporarily re-exposes some constructors. +""" + +[[audits.zcash_primitives]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.10.1 -> 0.10.2" +notes = """ +The ECC core team maintains this crate, and we have reviewed every line. +This point release temporarily re-exposes a constructor. +""" + +[[audits.zcash_primitives]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.10.2 -> 0.11.0" +notes = "The ECC core team maintains this crate, and we have reviewed every line." + [[audits.zcash_proofs]] who = "Jack Grigg " criteria = ["crypto-reviewed", "safe-to-deploy"] @@ -743,14 +1581,41 @@ who = "Jack Grigg " criteria = "safe-to-deploy" delta = "0.8.0 -> 0.9.0" +[[audits.zcash_proofs]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.9.0 -> 0.10.0" +notes = "The ECC core team maintains this crate, and we have reviewed every line." + +[[audits.zcash_proofs]] +who = "Jack Grigg " +criteria = ["safe-to-deploy", "crypto-reviewed"] +delta = "0.10.0 -> 0.11.0" +notes = "The ECC core team maintains this crate, and we have reviewed every line." + [[audits.zeroize]] who = "Daira Hopwood " criteria = "safe-to-deploy" delta = "1.4.3 -> 1.5.7" notes = "The zeroize_c_string unit test has UB, but that's very unlikely to cause a problem in practice." +[[audits.zeroize]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "1.5.7 -> 1.6.0" + [[audits.zeroize_derive]] who = "Jack Grigg " criteria = "safe-to-deploy" delta = "1.3.2 -> 1.3.3" notes = "Removes `T: Drop` bound from `impl Drop for SomeType`. I agree it was unnecessary." + +[[audits.zeroize_derive]] +who = "Sean Bowe " +criteria = "safe-to-deploy" +delta = "1.3.3 -> 1.4.1" + +[[audits.zeroize_derive]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.4.1 -> 1.4.2" diff --git a/depend/zcash/qa/supply-chain/config.toml b/depend/zcash/qa/supply-chain/config.toml index 0a15b187a..19a81f95b 100644 --- a/depend/zcash/qa/supply-chain/config.toml +++ b/depend/zcash/qa/supply-chain/config.toml @@ -1,14 +1,23 @@ # cargo-vet config file +[cargo-vet] +version = "0.6" + [imports.bytecode-alliance] url = "https://raw.githubusercontent.com/bytecodealliance/wasmtime/main/supply-chain/audits.toml" [imports.embark-studios] url = "https://raw.githubusercontent.com/EmbarkStudios/rust-ecosystem/main/audits.toml" -[imports.firefox] -url = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[imports.google] +url = "https://raw.githubusercontent.com/google/supply-chain/main/audits.toml" + +[imports.isrg] +url = "https://raw.githubusercontent.com/divviup/libprio-rs/main/supply-chain/audits.toml" + +[imports.mozilla] +url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml" [[exemptions.addr2line]] version = "0.17.0" @@ -23,7 +32,7 @@ version = "0.4.3" criteria = "safe-to-deploy" [[exemptions.aes]] -version = "0.7.5" +version = "0.8.2" criteria = "safe-to-deploy" [[exemptions.ahash]] @@ -34,10 +43,6 @@ criteria = "safe-to-deploy" version = "0.7.18" criteria = "safe-to-deploy" -[[exemptions.arrayref]] -version = "0.3.6" -criteria = "safe-to-deploy" - [[exemptions.backtrace]] version = "0.3.67" criteria = "safe-to-deploy" @@ -74,18 +79,6 @@ criteria = "safe-to-deploy" version = "1.0.0" criteria = "safe-to-deploy" -[[exemptions.block-buffer]] -version = "0.9.0" -criteria = "safe-to-deploy" - -[[exemptions.block-modes]] -version = "0.8.1" -criteria = "safe-to-deploy" - -[[exemptions.block-padding]] -version = "0.2.1" -criteria = "safe-to-deploy" - [[exemptions.bls12_381]] version = "0.7.0" criteria = "safe-to-deploy" @@ -106,16 +99,16 @@ criteria = "safe-to-deploy" version = "1.2.1" criteria = "safe-to-deploy" -[[exemptions.cc]] -version = "1.0.78" +[[exemptions.cbc]] +version = "0.1.2" criteria = "safe-to-deploy" -[[exemptions.cfg-if]] -version = "0.1.10" +[[exemptions.cc]] +version = "1.0.79" criteria = "safe-to-deploy" [[exemptions.chacha20]] -version = "0.8.1" +version = "0.9.1" criteria = "safe-to-deploy" [[exemptions.chacha20poly1305]] @@ -130,10 +123,6 @@ criteria = "safe-to-deploy" version = "1.0.9" criteria = "safe-to-deploy" -[[exemptions.constant_time_eq]] -version = "0.1.5" -criteria = "safe-to-deploy" - [[exemptions.cpufeatures]] version = "0.2.2" criteria = "safe-to-deploy" @@ -154,14 +143,6 @@ criteria = "safe-to-deploy" version = "0.8.8" criteria = "safe-to-deploy" -[[exemptions.crunchy]] -version = "0.2.2" -criteria = "safe-to-deploy" - -[[exemptions.crypto-mac]] -version = "0.11.1" -criteria = "safe-to-deploy" - [[exemptions.curve25519-dalek]] version = "3.2.0" criteria = "safe-to-deploy" @@ -170,10 +151,6 @@ criteria = "safe-to-deploy" version = "1.0.68" criteria = "safe-to-deploy" -[[exemptions.cxxbridge-flags]] -version = "1.0.68" -criteria = "safe-to-deploy" - [[exemptions.cxxbridge-macro]] version = "1.0.68" criteria = "safe-to-deploy" @@ -198,10 +175,6 @@ criteria = "safe-to-deploy" version = "3.0.0" criteria = "safe-to-deploy" -[[exemptions.either]] -version = "1.8.0" -criteria = "safe-to-deploy" - [[exemptions.ff]] version = "0.12.0" criteria = "safe-to-deploy" @@ -211,37 +184,25 @@ version = "0.7.0" criteria = "safe-to-deploy" [[exemptions.fpe]] -version = "0.5.1" +version = "0.6.0" criteria = "safe-to-deploy" [[exemptions.funty]] version = "2.0.0" criteria = "safe-to-deploy" -[[exemptions.futures-channel]] -version = "0.3.21" -criteria = "safe-to-deploy" - -[[exemptions.futures-core]] -version = "0.3.21" -criteria = "safe-to-deploy" - [[exemptions.futures-task]] version = "0.3.21" criteria = "safe-to-deploy" [[exemptions.futures-util]] -version = "0.3.21" +version = "0.3.27" criteria = "safe-to-deploy" [[exemptions.generic-array]] version = "0.14.6" criteria = "safe-to-deploy" -[[exemptions.getrandom]] -version = "0.1.16" -criteria = "safe-to-deploy" - [[exemptions.getrandom]] version = "0.2.6" criteria = "safe-to-deploy" @@ -271,7 +232,7 @@ version = "0.2.6" criteria = "safe-to-deploy" [[exemptions.hmac]] -version = "0.11.0" +version = "0.12.1" criteria = "safe-to-deploy" [[exemptions.http]] @@ -286,12 +247,8 @@ criteria = "safe-to-deploy" version = "1.8.0" criteria = "safe-to-deploy" -[[exemptions.httpdate]] -version = "1.0.2" -criteria = "safe-to-deploy" - [[exemptions.hyper]] -version = "0.14.23" +version = "0.14.25" criteria = "safe-to-deploy" [[exemptions.impl-codec]] @@ -326,12 +283,8 @@ criteria = "safe-to-deploy" version = "0.9.0" criteria = "safe-to-deploy" -[[exemptions.lazy_static]] -version = "1.4.0" -criteria = "safe-to-deploy" - [[exemptions.libc]] -version = "0.2.126" +version = "0.2.141" criteria = "safe-to-deploy" [[exemptions.libm]] @@ -382,6 +335,10 @@ criteria = "safe-to-deploy" version = "0.13.0" criteria = "safe-to-deploy" +[[exemptions.minimal-lexical]] +version = "0.2.1" +criteria = "safe-to-deploy" + [[exemptions.miniz_oxide]] version = "0.5.3" criteria = "safe-to-deploy" @@ -395,7 +352,7 @@ version = "0.26.1" criteria = "safe-to-deploy" [[exemptions.nom]] -version = "5.1.2" +version = "7.1.1" criteria = "safe-to-deploy" [[exemptions.nonempty]] @@ -418,10 +375,6 @@ criteria = "safe-to-deploy" version = "1.17.0" criteria = "safe-to-deploy" -[[exemptions.opaque-debug]] -version = "0.3.0" -criteria = "safe-to-deploy" - [[exemptions.overload]] version = "0.1.1" criteria = "safe-to-deploy" @@ -478,10 +431,6 @@ criteria = "safe-to-deploy" version = "0.2.9" criteria = "safe-to-deploy" -[[exemptions.pin-utils]] -version = "0.1.0" -criteria = "safe-to-deploy" - [[exemptions.poly1305]] version = "0.7.2" criteria = "safe-to-deploy" @@ -510,18 +459,10 @@ criteria = "safe-to-deploy" version = "0.7.0" criteria = "safe-to-deploy" -[[exemptions.rand]] -version = "0.7.3" -criteria = "safe-to-deploy" - [[exemptions.rand]] version = "0.8.5" criteria = "safe-to-deploy" -[[exemptions.rand_chacha]] -version = "0.2.2" -criteria = "safe-to-deploy" - [[exemptions.rand_chacha]] version = "0.3.1" criteria = "safe-to-deploy" @@ -534,14 +475,6 @@ criteria = "safe-to-deploy" version = "0.6.4" criteria = "safe-to-deploy" -[[exemptions.rand_hc]] -version = "0.2.0" -criteria = "safe-to-deploy" - -[[exemptions.rand_pcg]] -version = "0.2.1" -criteria = "safe-to-deploy" - [[exemptions.raw-cpuid]] version = "10.6.0" criteria = "safe-to-deploy" @@ -550,10 +483,6 @@ criteria = "safe-to-deploy" version = "0.3.0" criteria = "safe-to-deploy" -[[exemptions.redjubjub]] -version = "0.5.0" -criteria = "safe-to-deploy" - [[exemptions.redox_syscall]] version = "0.2.16" criteria = "safe-to-deploy" @@ -639,7 +568,7 @@ version = "1.10.0" criteria = "safe-to-deploy" [[exemptions.socket2]] -version = "0.4.7" +version = "0.4.9" criteria = "safe-to-deploy" [[exemptions.spin]] @@ -663,7 +592,7 @@ version = "1.0.1" criteria = "safe-to-deploy" [[exemptions.terminfo]] -version = "0.7.3" +version = "0.8.0" criteria = "safe-to-deploy" [[exemptions.thiserror]] @@ -679,7 +608,7 @@ version = "1.1.4" criteria = "safe-to-deploy" [[exemptions.time]] -version = "0.3.17" +version = "0.3.20" criteria = "safe-to-deploy" [[exemptions.time-core]] @@ -687,15 +616,15 @@ version = "0.1.0" criteria = "safe-to-deploy" [[exemptions.time-macros]] -version = "0.2.4" +version = "0.2.7" criteria = "safe-to-deploy" [[exemptions.tokio]] -version = "1.24.1" +version = "1.27.0" criteria = "safe-to-deploy" -[[exemptions.toml]] -version = "0.5.9" +[[exemptions.toml_edit]] +version = "0.19.7" criteria = "safe-to-deploy" [[exemptions.tower-service]] @@ -722,10 +651,6 @@ criteria = "safe-to-deploy" version = "0.3.16" criteria = "safe-to-deploy" -[[exemptions.try-lock]] -version = "0.2.3" -criteria = "safe-to-deploy" - [[exemptions.typenum]] version = "1.15.0" criteria = "safe-to-deploy" @@ -734,38 +659,10 @@ criteria = "safe-to-deploy" version = "0.9.4" criteria = "safe-to-deploy" -[[exemptions.unicode-ident]] -version = "1.0.0" -criteria = "safe-to-deploy" - -[[exemptions.unicode-xid]] -version = "0.2.4" -criteria = "safe-to-deploy" - -[[exemptions.universal-hash]] -version = "0.4.1" -criteria = "safe-to-deploy" - -[[exemptions.untrusted]] -version = "0.7.1" -criteria = "safe-to-deploy" - [[exemptions.valuable]] version = "0.1.0" criteria = "safe-to-deploy" -[[exemptions.version_check]] -version = "0.9.4" -criteria = "safe-to-deploy" - -[[exemptions.want]] -version = "0.3.0" -criteria = "safe-to-deploy" - -[[exemptions.wasi]] -version = "0.9.0+wasi-snapshot-preview1" -criteria = "safe-to-deploy" - [[exemptions.wasi]] version = "0.10.2+wasi-snapshot-preview1" criteria = "safe-to-deploy" @@ -775,27 +672,23 @@ version = "0.11.0+wasi-snapshot-preview1" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen]] -version = "0.2.83" +version = "0.2.84" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-backend]] -version = "0.2.83" +version = "0.2.84" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-macro]] -version = "0.2.83" +version = "0.2.84" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-macro-support]] -version = "0.2.83" -criteria = "safe-to-deploy" - -[[exemptions.wasm-bindgen-shared]] -version = "0.2.83" +version = "0.2.84" criteria = "safe-to-deploy" [[exemptions.web-sys]] -version = "0.3.60" +version = "0.3.61" criteria = "safe-to-deploy" [[exemptions.which]] @@ -814,6 +707,10 @@ criteria = "safe-to-deploy" version = "0.4.0" criteria = "safe-to-deploy" +[[exemptions.winnow]] +version = "0.4.1" +criteria = "safe-to-deploy" + [[exemptions.wyz]] version = "0.5.0" criteria = "safe-to-deploy" diff --git a/depend/zcash/qa/supply-chain/imports.lock b/depend/zcash/qa/supply-chain/imports.lock index bd10963eb..149015b4c 100644 --- a/depend/zcash/qa/supply-chain/imports.lock +++ b/depend/zcash/qa/supply-chain/imports.lock @@ -1,15 +1,13 @@ # cargo-vet imports lock -[[audits.bytecode-alliance.audits.anyhow]] -who = "Alex Crichton " +[[audits.bytecode-alliance.audits.arrayref]] +who = "Nick Fitzgerald " criteria = "safe-to-deploy" -delta = "1.0.62 -> 1.0.66" +version = "0.3.6" notes = """ -This update looks to be related to minor fixes and mostly integrating with a -nightly feature in the standard library for backtrace integration. No undue -`unsafe` is added and nothing unsurprising for the `anyhow` crate is happening -here. +Unsafe code, but its logic looks good to me. Necessary given what it is +doing. Well tested, has quickchecks. """ [[audits.bytecode-alliance.audits.arrayvec]] @@ -21,41 +19,29 @@ Well documented invariants, good assertions for those invariants in unsafe code, and tested with MIRI to boot. LGTM. """ -[[audits.bytecode-alliance.audits.backtrace]] -who = "Alex Crichton " -criteria = "safe-to-deploy" -version = "0.3.66" -notes = "I am the author of this crate." - [[audits.bytecode-alliance.audits.block-buffer]] who = "Benjamin Bouvier " criteria = "safe-to-deploy" delta = "0.9.0 -> 0.10.2" -[[audits.bytecode-alliance.audits.bumpalo]] -who = "Nick Fitzgerald " -criteria = "safe-to-deploy" -version = "3.9.1" -notes = "I am the author of this crate." - [[audits.bytecode-alliance.audits.bumpalo]] who = "Nick Fitzgerald " criteria = "safe-to-deploy" version = "3.11.1" notes = "I am the author of this crate." -[[audits.bytecode-alliance.audits.cc]] -who = "Alex Crichton " -criteria = "safe-to-deploy" -version = "1.0.73" -notes = "I am the author of this crate." - [[audits.bytecode-alliance.audits.cfg-if]] who = "Alex Crichton " criteria = "safe-to-deploy" version = "1.0.0" notes = "I am the author of this crate." +[[audits.bytecode-alliance.audits.constant_time_eq]] +who = "Nick Fitzgerald " +criteria = "safe-to-deploy" +version = "0.2.4" +notes = "A few tiny blocks of `unsafe` but each of them is very obviously correct." + [[audits.bytecode-alliance.audits.crypto-common]] who = "Benjamin Bouvier " criteria = "safe-to-deploy" @@ -66,15 +52,34 @@ who = "Benjamin Bouvier " criteria = "safe-to-deploy" delta = "0.9.0 -> 0.10.3" -[[audits.bytecode-alliance.audits.libm]] +[[audits.bytecode-alliance.audits.futures-channel]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" +notes = "build.rs is just detecting the target and setting cfg. unsafety is for implementing a concurrency primitives using atomics and unsafecell, and is not obviously incorrect (this is the sort of thing I wouldn't certify as correct without formal methods)" + +[[audits.bytecode-alliance.audits.futures-core]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.27" +notes = "Unsafe used to implement a concurrency primitive AtomicWaker. Well-commented and not obviously incorrect. Like my other audits of these concurrency primitives inside the futures family, I couldn't certify that it is correct without formal methods, but that is out of scope for this vetting." + +[[audits.bytecode-alliance.audits.httpdate]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "1.0.2" +notes = "No unsafety, no io" + +[[audits.bytecode-alliance.audits.memoffset]] who = "Alex Crichton " criteria = "safe-to-deploy" -delta = "0.2.2 -> 0.2.4" -notes = """ -This diff primarily fixes a few issues with the `fma`-related functions, -but also contains some other minor fixes as well. Everything looks A-OK and -as expected. -""" +delta = "0.7.1 -> 0.8.0" +notes = "This was a small update to the crate which has to do with Rust language features and compiler versions, no substantial changes." + +[[audits.bytecode-alliance.audits.pin-utils]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.1.0" [[audits.bytecode-alliance.audits.rustc-demangle]] who = "Alex Crichton " @@ -88,18 +93,6 @@ criteria = "safe-to-deploy" delta = "0.9.9 -> 0.10.2" notes = "This upgrade is mostly a code refactor, as far as I can tell. No new uses of unsafe nor any new ambient capabilities usage." -[[audits.bytecode-alliance.audits.spin]] -who = "Alex Crichton " -criteria = "safe-to-run" -version = "0.9.4" -notes = """ -I've verified the contents of this crate and that while they contain `unsafe` -it's exclusively around implementing atomic primitive where some `unsafe` is to -be expected. Otherwise this crate does not unduly access ambient capabilities -and does what it says on the tin, providing spin-based synchronization -primitives. -""" - [[audits.bytecode-alliance.audits.tinyvec]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -120,6 +113,12 @@ intended to multiplex across the internal representation of a tinyvec, presumably. This trivially doesn't contain anything bad. """ +[[audits.bytecode-alliance.audits.try-lock]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.2.4" +notes = "Implements a concurrency primitive with atomics, and is not obviously incorrect" + [[audits.bytecode-alliance.audits.unicode-normalization]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -132,12 +131,29 @@ throughout the ecosystem and skimming the crate shows no usage of `std::*` APIs and nothing suspicious. """ +[[audits.bytecode-alliance.audits.want]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.3.0" + [[audits.bytecode-alliance.audits.windows-sys]] who = "Dan Gohman " criteria = "safe-to-deploy" version = "0.42.0" notes = "This is a Windows API bindings library maintained by Microsoft themselves." +[[audits.bytecode-alliance.audits.windows-sys]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +delta = "0.42.0 -> 0.45.0" +notes = "This is a Windows API bindings library maintained by Microsoft themselves." + +[[audits.bytecode-alliance.audits.windows-targets]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "0.42.1" +notes = "This is a Windows API bindings library maintained by Microsoft themselves. Additionally, this particular crate is empty and just collects a bunch of dependencies, which are not exported, so I don't understand why it exists at all." + [[audits.bytecode-alliance.audits.windows_aarch64_gnullvm]] who = "Dan Gohman " criteria = "safe-to-deploy" @@ -185,348 +201,368 @@ who = "Johan Andersson " criteria = "safe-to-deploy" version = "1.0.58" -[[audits.embark-studios.audits.anyhow]] -who = "Johan Andersson " +[[audits.google.audits.cxxbridge-flags]] +who = "George Burgess IV " criteria = "safe-to-deploy" -delta = "1.0.58 -> 1.0.66" -notes = "New unsafe usage, looks sane. Expert maintainer" +version = "1.0.92" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.embark-studios.audits.tinyvec_macros]] -who = "Johan Andersson " +[[audits.google.audits.version_check]] +who = "George Burgess IV " criteria = "safe-to-deploy" -version = "0.1.0" -notes = "Inspected it and is a tiny crate with single safe macro" +version = "0.9.4" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.firefox.audits.aho-corasick]] -who = "Mike Hommey " +[[audits.isrg.audits.block-buffer]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "0.7.18 -> 0.7.20" +version = "0.9.0" -[[audits.firefox.audits.anyhow]] -who = "Mike Hommey " +[[audits.isrg.audits.crunchy]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "1.0.57 -> 1.0.61" +version = "0.2.2" -[[audits.firefox.audits.anyhow]] -who = "Bobby Holley " +[[audits.isrg.audits.either]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "1.0.58 -> 1.0.57" -notes = "No functional differences, just CI config and docs." +version = "1.6.1" -[[audits.firefox.audits.anyhow]] -who = "Mike Hommey " +[[audits.isrg.audits.opaque-debug]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "1.0.61 -> 1.0.62" +version = "0.3.0" -[[audits.firefox.audits.anyhow]] -who = "Mike Hommey " +[[audits.isrg.audits.proc-macro2]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "1.0.62 -> 1.0.68" +delta = "1.0.52 -> 1.0.54" -[[audits.firefox.audits.autocfg]] -who = "Josh Stone " +[[audits.isrg.audits.rayon]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -version = "1.1.0" -notes = "All code written or reviewed by Josh Stone." +delta = "1.6.1 -> 1.7.0" -[[audits.firefox.audits.block-buffer]] -who = "Mike Hommey " +[[audits.isrg.audits.rayon-core]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "0.10.2 -> 0.10.3" +delta = "1.10.2 -> 1.11.0" -[[audits.firefox.audits.bumpalo]] -who = "Bobby Holley " -criteria = "safe-to-run" -delta = "3.9.1 -> 3.10.0" -notes = """ -Some nontrivial functional changes but certainly meets the no-malware bar of -safe-to-run. If we needed safe-to-deploy for this in m-c I'd ask Nick to re- -certify this version, but we don't, so this is fine for now. -""" +[[audits.isrg.audits.serde]] +who = "David Cook " +criteria = "safe-to-deploy" +delta = "1.0.152 -> 1.0.153" -[[audits.firefox.audits.bytes]] -who = "Mike Hommey " +[[audits.isrg.audits.serde]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "1.1.0 -> 1.2.1" +delta = "1.0.153 -> 1.0.154" -[[audits.firefox.audits.bytes]] -who = "Mike Hommey " +[[audits.isrg.audits.serde]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "1.2.1 -> 1.3.0" +delta = "1.0.154 -> 1.0.155" -[[audits.firefox.audits.cpufeatures]] -who = "Mike Hommey " +[[audits.isrg.audits.serde]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "0.2.2 -> 0.2.4" +delta = "1.0.156 -> 1.0.159" -[[audits.firefox.audits.cpufeatures]] -who = "Mike Hommey " +[[audits.isrg.audits.serde_derive]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "0.2.4 -> 0.2.5" +delta = "1.0.152 -> 1.0.153" -[[audits.firefox.audits.crossbeam-channel]] -who = "Mike Hommey " +[[audits.isrg.audits.serde_derive]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "0.5.4 -> 0.5.6" +delta = "1.0.153 -> 1.0.154" -[[audits.firefox.audits.crossbeam-deque]] -who = "Mike Hommey " +[[audits.isrg.audits.serde_derive]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "0.8.1 -> 0.8.2" +delta = "1.0.154 -> 1.0.155" -[[audits.firefox.audits.crossbeam-epoch]] -who = "Mike Hommey " +[[audits.isrg.audits.serde_derive]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "0.9.8 -> 0.9.10" +delta = "1.0.156 -> 1.0.159" -[[audits.firefox.audits.crossbeam-epoch]] -who = "Mike Hommey " +[[audits.isrg.audits.serde_json]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "0.9.10 -> 0.9.13" +delta = "1.0.93 -> 1.0.94" -[[audits.firefox.audits.crossbeam-utils]] -who = "Mike Hommey " +[[audits.isrg.audits.serde_json]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "0.8.8 -> 0.8.11" +delta = "1.0.94 -> 1.0.95" -[[audits.firefox.audits.crossbeam-utils]] -who = "Mike Hommey " +[[audits.isrg.audits.syn]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "0.8.11 -> 0.8.14" +delta = "1.0.104 -> 2.0.11" -[[audits.firefox.audits.crypto-common]] -who = "Mike Hommey " +[[audits.isrg.audits.thiserror]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "0.1.3 -> 0.1.6" +delta = "1.0.38 -> 1.0.39" -[[audits.firefox.audits.digest]] -who = "Mike Hommey " +[[audits.isrg.audits.thiserror]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "0.10.3 -> 0.10.6" +delta = "1.0.39 -> 1.0.40" -[[audits.firefox.audits.either]] -who = "Mike Hommey " +[[audits.isrg.audits.thiserror-impl]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "1.6.1 -> 1.7.0" +delta = "1.0.38 -> 1.0.39" -[[audits.firefox.audits.either]] -who = "Mike Hommey " +[[audits.isrg.audits.thiserror-impl]] +who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "1.7.0 -> 1.8.0" +delta = "1.0.39 -> 1.0.40" -[[audits.firefox.audits.fnv]] -who = "Bobby Holley " +[[audits.isrg.audits.unicode-ident]] +who = "David Cook " criteria = "safe-to-deploy" -version = "1.0.7" -notes = "Simple hasher implementation with no unsafe code." +delta = "1.0.2 -> 1.0.3" -[[audits.firefox.audits.futures-channel]] -who = "Mike Hommey " +[[audits.isrg.audits.universal-hash]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "0.3.21 -> 0.3.23" +version = "0.4.1" -[[audits.firefox.audits.futures-channel]] -who = "Mike Hommey " +[[audits.isrg.audits.untrusted]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "0.3.23 -> 0.3.25" +version = "0.7.1" -[[audits.firefox.audits.futures-core]] -who = "Mike Hommey " +[[audits.isrg.audits.wasm-bindgen-shared]] +who = "David Cook " criteria = "safe-to-deploy" -delta = "0.3.21 -> 0.3.23" +version = "0.2.83" -[[audits.firefox.audits.futures-core]] +[[audits.mozilla.audits.aho-corasick]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.3.23 -> 0.3.25" +delta = "0.7.18 -> 0.7.20" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.futures-task]] +[[audits.mozilla.audits.anyhow]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.3.21 -> 0.3.23" +delta = "1.0.57 -> 1.0.61" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.futures-task]] -who = "Mike Hommey " +[[audits.mozilla.audits.anyhow]] +who = "Bobby Holley " criteria = "safe-to-deploy" -delta = "0.3.23 -> 0.3.25" +delta = "1.0.58 -> 1.0.57" +notes = "No functional differences, just CI config and docs." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.futures-util]] +[[audits.mozilla.audits.anyhow]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.3.21 -> 0.3.23" +delta = "1.0.61 -> 1.0.62" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.futures-util]] +[[audits.mozilla.audits.anyhow]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.3.23 -> 0.3.25" +delta = "1.0.62 -> 1.0.68" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.generic-array]] -who = "Mike Hommey " +[[audits.mozilla.audits.autocfg]] +who = "Josh Stone " criteria = "safe-to-deploy" -delta = "0.14.5 -> 0.14.6" +version = "1.1.0" +notes = "All code written or reviewed by Josh Stone." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.getrandom]] +[[audits.mozilla.audits.block-buffer]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.2.6 -> 0.2.7" +delta = "0.10.2 -> 0.10.3" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.getrandom]] +[[audits.mozilla.audits.bytes]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.2.7 -> 0.2.8" +delta = "1.2.1 -> 1.3.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.hashbrown]] -who = "Mike Hommey " +[[audits.mozilla.audits.crossbeam-channel]] +who = "Jan-Erik Rediger " criteria = "safe-to-deploy" -version = "0.12.3" -notes = "This version is used in rust's libstd, so effectively we're already trusting it" +delta = "0.5.7 -> 0.5.8" +notes = "Reviewed the fix, previous versions indeed had were able to trigger a race condition" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" -[[audits.firefox.audits.hex]] -who = "Simon Friedberger " +[[audits.mozilla.audits.crossbeam-epoch]] +who = "Mike Hommey " criteria = "safe-to-deploy" -version = "0.4.3" +delta = "0.9.8 -> 0.9.10" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.httparse]] +[[audits.mozilla.audits.crossbeam-epoch]] who = "Mike Hommey " -criteria = "safe-to-run" -delta = "1.7.1 -> 1.8.0" +criteria = "safe-to-deploy" +delta = "0.9.10 -> 0.9.13" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.hyper]] +[[audits.mozilla.audits.crossbeam-utils]] who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.14.19 -> 0.14.20" +criteria = "safe-to-deploy" +delta = "0.8.8 -> 0.8.11" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.hyper]] +[[audits.mozilla.audits.crossbeam-utils]] who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.14.20 -> 0.14.22" +criteria = "safe-to-deploy" +delta = "0.8.11 -> 0.8.14" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.hyper]] +[[audits.mozilla.audits.digest]] who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.14.22 -> 0.14.23" +criteria = "safe-to-deploy" +delta = "0.10.3 -> 0.10.6" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.indexmap]] +[[audits.mozilla.audits.either]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "1.8.2 -> 1.9.1" +delta = "1.6.1 -> 1.7.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.indexmap]] +[[audits.mozilla.audits.either]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "1.9.1 -> 1.9.2" +delta = "1.7.0 -> 1.8.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.itoa]] +[[audits.mozilla.audits.fnv]] +who = "Bobby Holley " +criteria = "safe-to-deploy" +version = "1.0.7" +notes = "Simple hasher implementation with no unsafe code." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.futures-task]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "1.0.2 -> 1.0.3" +delta = "0.3.21 -> 0.3.23" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.itoa]] +[[audits.mozilla.audits.futures-task]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "1.0.3 -> 1.0.5" +delta = "0.3.23 -> 0.3.25" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.libc]] +[[audits.mozilla.audits.getrandom]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.2.126 -> 0.2.132" +delta = "0.2.7 -> 0.2.8" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.libc]] +[[audits.mozilla.audits.hashbrown]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.2.132 -> 0.2.138" +version = "0.12.3" +notes = "This version is used in rust's libstd, so effectively we're already trusting it" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.hex]] +who = "Simon Friedberger " +criteria = "safe-to-deploy" +version = "0.4.3" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.libc]] +[[audits.mozilla.audits.indexmap]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.2.138 -> 0.2.139" +delta = "1.9.1 -> 1.9.2" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.lock_api]] +[[audits.mozilla.audits.itoa]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.4.7 -> 0.4.9" +delta = "1.0.2 -> 1.0.3" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.log]] +[[audits.mozilla.audits.itoa]] who = "Mike Hommey " criteria = "safe-to-deploy" -version = "0.4.17" +delta = "1.0.3 -> 1.0.5" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.memoffset]] -who = "Gabriele Svelto " +[[audits.mozilla.audits.lazy_static]] +who = "Nika Layzell " criteria = "safe-to-deploy" -delta = "0.6.5 -> 0.7.1" +version = "1.4.0" +notes = "I have read over the macros, and audited the unsafe code." +aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml" -[[audits.firefox.audits.miniz_oxide]] +[[audits.mozilla.audits.log]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.5.3 -> 0.6.2" +version = "0.4.17" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.nix]] +[[audits.mozilla.audits.memoffset]] who = "Gabriele Svelto " criteria = "safe-to-deploy" -delta = "0.15.0 -> 0.25.0" -notes = "Plenty of new bindings but also several important bug fixes (including buffer overflows). New unsafe sections are restricted to wrappers and are no more dangerous than calling the C functions." +delta = "0.6.5 -> 0.7.1" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.nix]] +[[audits.mozilla.audits.miniz_oxide]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.25.0 -> 0.25.1" +delta = "0.5.3 -> 0.6.2" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.num-bigint]] -who = "Josh Stone " +[[audits.mozilla.audits.nom]] +who = "Mike Hommey " criteria = "safe-to-deploy" -version = "0.2.6" -notes = "All code written or reviewed by Josh Stone." +delta = "7.1.1 -> 7.1.3" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.num-bigint]] +[[audits.mozilla.audits.num-bigint]] who = "Josh Stone " criteria = "safe-to-deploy" version = "0.4.3" notes = "All code written or reviewed by Josh Stone." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.num-integer]] +[[audits.mozilla.audits.num-integer]] who = "Josh Stone " criteria = "safe-to-deploy" version = "0.1.45" notes = "All code written or reviewed by Josh Stone." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.num-traits]] +[[audits.mozilla.audits.num-traits]] who = "Josh Stone " criteria = "safe-to-deploy" version = "0.2.15" notes = "All code written or reviewed by Josh Stone." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.num_cpus]] +[[audits.mozilla.audits.num_cpus]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.13.1 -> 1.14.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.object]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "0.28.4 -> 0.30.0" - -[[audits.firefox.audits.once_cell]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.12.0 -> 1.13.1" - -[[audits.firefox.audits.once_cell]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.13.1 -> 1.16.0" - -[[audits.firefox.audits.parking_lot_core]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "0.8.5 -> 0.8.6" - -[[audits.firefox.audits.ppv-lite86]] +[[audits.mozilla.audits.ppv-lite86]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "0.2.16 -> 0.2.17" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.proc-macro2]] +[[audits.mozilla.audits.proc-macro2]] who = "Nika Layzell " criteria = "safe-to-deploy" version = "1.0.39" @@ -554,18 +590,21 @@ allow bypassing checks in the fallback implementation when constructing and is likely completely unused. Even when used, this API shouldn't be able to cause unsoundness. """ +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.proc-macro2]] +[[audits.mozilla.audits.proc-macro2]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.39 -> 1.0.43" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.proc-macro2]] +[[audits.mozilla.audits.proc-macro2]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.43 -> 1.0.49" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.quote]] +[[audits.mozilla.audits.quote]] who = "Nika Layzell " criteria = "safe-to-deploy" version = "1.0.18" @@ -579,299 +618,175 @@ This crate contains no unsafe code, and the internal logic, while difficult to read, is generally straightforward. I have audited the the quote macros, ident formatter, and runtime logic. """ +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.quote]] +[[audits.mozilla.audits.quote]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.18 -> 1.0.21" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.quote]] +[[audits.mozilla.audits.quote]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.21 -> 1.0.23" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.radium]] -who = "Nika Layzell " -criteria = "safe-to-deploy" -version = "0.5.3" -notes = """ -I am no longer the primary maintainer of `radium`, however I have audited the -code to ensure it is still correct. The implementation contains no `unsafe` -logic, and will not abstract away `Sync` trait bounds. - -The core logic is very simple, and acts as an abstraction trait for `Cell` -and `AtomicT`. -""" - -[[audits.firefox.audits.rand_core]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "0.6.3 -> 0.6.4" - -[[audits.firefox.audits.rayon]] +[[audits.mozilla.audits.rayon]] who = "Josh Stone " criteria = "safe-to-deploy" version = "1.5.3" notes = "All code written or reviewed by Josh Stone or Niko Matsakis." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.rayon]] +[[audits.mozilla.audits.rayon]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.5.3 -> 1.6.1" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.rayon-core]] +[[audits.mozilla.audits.rayon-core]] who = "Josh Stone " criteria = "safe-to-deploy" version = "1.9.3" notes = "All code written or reviewed by Josh Stone or Niko Matsakis." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.rayon-core]] +[[audits.mozilla.audits.rayon-core]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.9.3 -> 1.10.1" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.redox_syscall]] +[[audits.mozilla.audits.rayon-core]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.2.13 -> 0.2.16" +delta = "1.10.1 -> 1.10.2" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.regex]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.5.6 -> 1.6.0" - -[[audits.firefox.audits.regex]] +[[audits.mozilla.audits.regex]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.6.0 -> 1.7.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.regex-syntax]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "0.6.26 -> 0.6.27" - -[[audits.firefox.audits.regex-syntax]] +[[audits.mozilla.audits.regex-syntax]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "0.6.27 -> 0.6.28" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.ryu]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.0.10 -> 1.0.11" - -[[audits.firefox.audits.ryu]] +[[audits.mozilla.audits.ryu]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.11 -> 1.0.12" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.serde]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.0.137 -> 1.0.143" - -[[audits.firefox.audits.serde]] +[[audits.mozilla.audits.serde]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.143 -> 1.0.144" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.serde]] +[[audits.mozilla.audits.serde]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.144 -> 1.0.151" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.serde]] +[[audits.mozilla.audits.serde]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.151 -> 1.0.152" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.serde_derive]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.0.137 -> 1.0.143" - -[[audits.firefox.audits.serde_derive]] +[[audits.mozilla.audits.serde_derive]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.143 -> 1.0.144" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.serde_derive]] +[[audits.mozilla.audits.serde_derive]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.144 -> 1.0.151" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.serde_derive]] +[[audits.mozilla.audits.serde_derive]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.151 -> 1.0.152" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.serde_json]] +[[audits.mozilla.audits.serde_json]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.81 -> 1.0.83" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.serde_json]] +[[audits.mozilla.audits.serde_json]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.83 -> 1.0.85" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.serde_json]] +[[audits.mozilla.audits.serde_json]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.85 -> 1.0.91" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.sha2]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "0.10.2 -> 0.10.6" - -[[audits.firefox.audits.smallvec]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.8.0 -> 1.9.0" - -[[audits.firefox.audits.smallvec]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.9.0 -> 1.10.0" - -[[audits.firefox.audits.socket2]] +[[audits.mozilla.audits.serde_json]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.4.4 -> 0.4.7" +delta = "1.0.91 -> 1.0.93" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.syn]] +[[audits.mozilla.audits.sha2]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "1.0.96 -> 1.0.99" - -[[audits.firefox.audits.syn]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.0.99 -> 1.0.107" - -[[audits.firefox.audits.synstructure]] -who = "Nika Layzell " -criteria = "safe-to-deploy" -version = "0.12.6" -notes = """ -I am the primary author of the `synstructure` crate, and its current -maintainer. The one use of `unsafe` is unnecessary, but documented and -harmless. It will be removed in the next version. -""" - -[[audits.firefox.audits.thiserror]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.0.31 -> 1.0.32" +delta = "0.10.2 -> 0.10.6" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.thiserror]] +[[audits.mozilla.audits.thiserror]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.32 -> 1.0.38" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.thiserror-impl]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.0.31 -> 1.0.32" - -[[audits.firefox.audits.thiserror-impl]] +[[audits.mozilla.audits.thiserror-impl]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.32 -> 1.0.38" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.time]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "0.1.44 -> 0.1.45" - -[[audits.firefox.audits.time]] -who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.3.9 -> 0.3.17" - -[[audits.firefox.audits.time-macros]] -who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.2.4 -> 0.2.6" - -[[audits.firefox.audits.toml]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "0.5.9 -> 0.5.10" - -[[audits.firefox.audits.tower-service]] -who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.3.1 -> 0.3.2" - -[[audits.firefox.audits.tracing]] -who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.1.35 -> 0.1.36" - -[[audits.firefox.audits.tracing]] -who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.1.36 -> 0.1.37" - -[[audits.firefox.audits.tracing-attributes]] -who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.1.21 -> 0.1.22" - -[[audits.firefox.audits.tracing-attributes]] -who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.1.22 -> 0.1.23" - -[[audits.firefox.audits.tracing-core]] -who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.1.27 -> 0.1.29" - -[[audits.firefox.audits.tracing-core]] -who = "Mike Hommey " -criteria = "safe-to-run" -delta = "0.1.29 -> 0.1.30" - -[[audits.firefox.audits.typenum]] +[[audits.mozilla.audits.typenum]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.15.0 -> 1.16.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.unicode-ident]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.0.0 -> 1.0.1" - -[[audits.firefox.audits.unicode-ident]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.0.1 -> 1.0.3" - -[[audits.firefox.audits.unicode-ident]] +[[audits.mozilla.audits.unicode-ident]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "1.0.3 -> 1.0.6" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.unicode-normalization]] +[[audits.mozilla.audits.unicode-normalization]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "0.1.19 -> 0.1.20" notes = "I am the author of most of these changes upstream, and prepared the release myself, at which point I looked at the other changes since 0.1.19." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.unicode-normalization]] +[[audits.mozilla.audits.unicode-normalization]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "0.1.20 -> 0.1.21" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.firefox.audits.unicode-normalization]] +[[audits.mozilla.audits.unicode-normalization]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "0.1.21 -> 0.1.22" - -[[audits.firefox.audits.unicode-xid]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "0.2.3 -> 0.2.4" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" diff --git a/depend/zcash/qa/zcash/postponed-updates.txt b/depend/zcash/qa/zcash/postponed-updates.txt index c83172961..ddd1614e9 100644 --- a/depend/zcash/qa/zcash/postponed-updates.txt +++ b/depend/zcash/qa/zcash/postponed-updates.txt @@ -4,48 +4,28 @@ # bdb 18.1.40 2020-09-01 # -# cxx 1.0.84-1.0.87 didn't update third-party/Cargo.lock -native_cxxbridge 1.0.84 2024-02-01 -native_cxxbridge 1.0.85 2024-02-01 -native_cxxbridge 1.0.86 2024-02-01 -native_cxxbridge 1.0.87 2024-02-01 -native_cxxbridge 1.0.88 2024-02-01 -native_cxxbridge 1.0.89 2024-02-01 -rustcxx 1.0.84 2024-02-01 -rustcxx 1.0.85 2024-02-01 -rustcxx 1.0.86 2024-02-01 -rustcxx 1.0.87 2024-02-01 -rustcxx 1.0.88 2024-02-01 -rustcxx 1.0.89 2024-02-01 +# CCache 4.7 appears to drop support for Ubuntu 18.04. +# We will drop Ubuntu 18.04 no later than May 2023. +native_ccache 4.7 2023-05-01 +native_ccache 4.7.1 2023-05-01 +native_ccache 4.7.2 2023-05-01 +native_ccache 4.7.3 2023-05-01 +native_ccache 4.7.4 2023-05-01 +native_ccache 4.7.5 2023-05-01 +native_ccache 4.8 2023-05-01 -# CCache 4.7 appears to drop support for Ubuntu 18.04 -native_ccache 4.7 2023-03-01 -native_ccache 4.7.1 2023-03-01 -native_ccache 4.7.2 2023-03-01 -native_ccache 4.7.3 2023-03-01 -native_ccache 4.7.4 2023-03-01 +# Clang and Rust are currently pinned to LLVM 15 +libcxx 15.0.7 2023-04-30 +libcxx 16.0.0 2023-04-30 +libcxx 16.0.1 2023-04-30 +libcxx 16.0.2 2023-04-30 +native_clang 15.0.7 2023-04-30 +native_clang 16.0.0 2023-04-30 +native_clang 16.0.1 2023-04-30 +native_clang 16.0.2 2023-04-30 -# Clang and Rust are currently pinned to LLVM 14 -libcxx 15.0.0 2023-03-01 -libcxx 15.0.1 2023-03-01 -libcxx 15.0.2 2023-03-01 -libcxx 15.0.3 2023-03-01 -libcxx 15.0.4 2023-03-01 -libcxx 15.0.5 2023-03-01 -libcxx 15.0.6 2023-03-01 -libcxx 15.0.7 2023-03-01 -native_clang 15.0.0 2023-03-01 -native_clang 15.0.1 2023-03-01 -native_clang 15.0.2 2023-03-01 -native_clang 15.0.3 2023-03-01 -native_clang 15.0.4 2023-03-01 -native_clang 15.0.5 2023-03-01 -native_clang 15.0.6 2023-03-01 -native_clang 15.0.7 2023-03-01 -native_rust 1.65.0 2023-03-01 -native_rust 1.66.0 2023-03-01 -native_rust 1.66.1 2023-03-01 -native_rust 1.67.0 2023-03-01 +# We follow upstream Bitcoin Core's LevelDB updates +leveldb 1.23 2023-06-01 # We're never updating to this version -bdb 18.1.40 2024-02-01 +bdb 18.1.40 2024-03-01 diff --git a/depend/zcash/qa/zcash/smoke_tests.py b/depend/zcash/qa/zcash/smoke_tests.py index b5c895417..cb421bd0a 100755 --- a/depend/zcash/qa/zcash/smoke_tests.py +++ b/depend/zcash/qa/zcash/smoke_tests.py @@ -22,7 +22,7 @@ from slickrpc import Proxy from slickrpc.exc import RpcException -DEFAULT_FEE = Decimal('0.00001') +LEGACY_DEFAULT_FEE = Decimal('0.00001') URL_FAUCET_DONATION = 'https://faucet.testnet.z.cash/donations' URL_FAUCET_TAP = 'https://faucet.testnet.z.cash/' @@ -120,11 +120,7 @@ def run_cmd(results, case, zcash, name, args=[]): print('----- %s -----' % (datetime.datetime.now() - TIME_STARTED)) - print('%s $ zcash-cli %s %s' % ( - case.ljust(3), - name, - ' '.join([str(arg) for arg in args], - ))) + print('%s: %s %r' % (case.ljust(3), name, args)) try: res = zcash.__getattr__(name)(*args) print(res) @@ -218,47 +214,49 @@ def async_txid_cmd(results, case, zcash, name, args=[]): results[case] = False return txid -def z_sendmany(results, case, zcash, from_addr, recipients): +def z_sendmany(results, case, zcash, from_addr, recipients, privacy_policy): return async_txid_cmd(results, case, zcash, 'z_sendmany', [ from_addr, [{ 'address': to_addr, 'amount': amount, } for (to_addr, amount) in recipients], - 1 # minconf + 1, # minconf + LEGACY_DEFAULT_FEE, # fee + privacy_policy ]) -def check_z_sendmany(results, case, zcash, from_addr, recipients): - txid = z_sendmany(results, case, zcash, from_addr, recipients) +def check_z_sendmany(results, case, zcash, from_addr, recipients, privacy_policy): + txid = z_sendmany(results, case, zcash, from_addr, recipients, privacy_policy) if txid is None: return [Decimal('0')] return [wait_and_check_balance(results, case, zcash, to_addr, amount) for (to_addr, amount) in recipients] def check_z_sendmany_parallel(results, zcash, runs): # First attempt to create all the transactions - txids = [(run, z_sendmany(results, run[0], zcash, run[1], run[2])) for run in runs] + txids = [(run, z_sendmany(results, run[0], zcash, run[1], run[2], run[3])) for run in runs] # Then wait for balance updates caused by successful transactions return [ wait_and_check_balance(results, run[0], zcash, to_addr, amount) if txid is not None else Decimal('0') for (run, txid) in txids for (to_addr, amount) in run[2]] -def z_mergetoaddress(results, case, zcash, from_addrs, to_addr): +def z_mergetoaddress(results, case, zcash, from_addrs, to_addr, privacy_policy): for addr in from_addrs: balance = Decimal(zcash.z_getbalance(addr, 0)).quantize(Decimal('1.00000000')) if balance > 0: wait_for_balance(zcash, addr, balance, minconf=4) - return async_txid_cmd(results, case, zcash, 'z_mergetoaddress', [from_addrs, to_addr]) + return async_txid_cmd(results, case, zcash, 'z_mergetoaddress', [from_addrs, to_addr, LEGACY_DEFAULT_FEE, None, None, None, privacy_policy]) -def check_z_mergetoaddress(results, case, zcash, from_addrs, to_addr, amount): - txid = z_mergetoaddress(results, case, zcash, from_addrs, to_addr) +def check_z_mergetoaddress(results, case, zcash, from_addrs, to_addr, amount, privacy_policy): + txid = z_mergetoaddress(results, case, zcash, from_addrs, to_addr, privacy_policy) if txid is None: return Decimal('0') return wait_and_check_balance(results, case, zcash, to_addr, amount) def check_z_mergetoaddress_parallel(results, zcash, runs): # First attempt to create all the transactions - txids = [(run, z_mergetoaddress(results, run[0], zcash, run[1], run[2])) for run in runs] + txids = [(run, z_mergetoaddress(results, run[0], zcash, run[1], run[2], run[4])) for run in runs] # Then wait for balance updates caused by successful transactions return [ wait_and_check_balance(results, run[0], zcash, run[2], run[3]) if txid is not None else Decimal('0') @@ -341,7 +339,7 @@ def transaction_chain(zcash): # Validate the addresses ret = run_cmd(results, '5c', zcash, 'z_validateaddress', [sapling_zaddr_1]) - if not ret['isvalid'] or ret['type'] != 'sapling': + if not ret['isvalid'] or ret['address_type'] != 'sapling': results['5c'] = False # Set up beginning and end of the chain @@ -382,13 +380,13 @@ def transaction_chain(zcash): # taddr -> Sapling # Send it all here because z_sendmany pick a new t-addr for change sapling_balance = check_z_sendmany( - results, '4f', zcash, taddr_1, [(sapling_zaddr_1, taddr_balance - DEFAULT_FEE)])[0] + results, '4f', zcash, taddr_1, [(sapling_zaddr_1, taddr_balance - LEGACY_DEFAULT_FEE)], "AllowRevealedSenders")[0] taddr_balance = Decimal('0') # Sapling -> taddr taddr_balance = check_z_sendmany( - results, '4i', zcash, sapling_zaddr_1, [(taddr_1, (starting_balance / Decimal('10')) * Decimal('3'))])[0] - sapling_balance -= taddr_balance + DEFAULT_FEE + results, '4i', zcash, sapling_zaddr_1, [(taddr_1, (starting_balance / Decimal('10')) * Decimal('3'))], "AllowRevealedRecipients")[0] + sapling_balance -= taddr_balance + LEGACY_DEFAULT_FEE # # Intra-pool tests @@ -396,13 +394,13 @@ def transaction_chain(zcash): # Sapling -> same Sapling sapling_balance = check_z_sendmany( - results, '4g',zcash, sapling_zaddr_1, [(sapling_zaddr_1, sapling_balance - DEFAULT_FEE)])[0] + results, '4g',zcash, sapling_zaddr_1, [(sapling_zaddr_1, sapling_balance - LEGACY_DEFAULT_FEE)], "FullPrivacy")[0] # taddr -> different taddr # Sapling -> different Sapling (taddr_balance, sapling_balance) = check_z_sendmany_parallel(results, zcash, [ - ('4e', taddr_1, [(taddr_2, taddr_balance - DEFAULT_FEE)]), - ('4h', sapling_zaddr_1, [(sapling_zaddr_2, sapling_balance - DEFAULT_FEE)]), + ('4e', taddr_1, [(taddr_2, taddr_balance - LEGACY_DEFAULT_FEE)], "AllowFullyTransparent"), + ('4h', sapling_zaddr_1, [(sapling_zaddr_2, sapling_balance - LEGACY_DEFAULT_FEE)], "FullPrivacy"), ]) # taddr -> multiple taddr @@ -410,25 +408,25 @@ def transaction_chain(zcash): check_z_sendmany_parallel(results, zcash, [ ('4p', taddr_2, [ (taddr_1, starting_balance / Decimal('10')), - (taddr_3, taddr_balance - (starting_balance / Decimal('10')) - DEFAULT_FEE), - ]), + (taddr_3, taddr_balance - (starting_balance / Decimal('10')) - LEGACY_DEFAULT_FEE), + ], "AllowFullyTransparent"), ('4t', sapling_zaddr_2, [ (sapling_zaddr_1, starting_balance / Decimal('10')), (sapling_zaddr_3, starting_balance / Decimal('10')), - ]), + ], "FullPrivacy"), ]) - taddr_balance -= DEFAULT_FEE - sapling_balance -= DEFAULT_FEE + taddr_balance -= LEGACY_DEFAULT_FEE + sapling_balance -= LEGACY_DEFAULT_FEE # multiple Sapling -> Sapling # multiple taddr -> taddr check_z_mergetoaddress_parallel(results, zcash, [ - ('4gg', [sapling_zaddr_1, sapling_zaddr_3], sapling_zaddr_2, sapling_balance - DEFAULT_FEE), - ('', [taddr_1, taddr_3], taddr_2, taddr_balance - DEFAULT_FEE), + ('4gg', [sapling_zaddr_1, sapling_zaddr_3], sapling_zaddr_2, sapling_balance - LEGACY_DEFAULT_FEE, "FullPrivacy"), + ('', [taddr_1, taddr_3], taddr_2, taddr_balance - LEGACY_DEFAULT_FEE, "AllowFullyTransparent"), ]) - sapling_balance -= DEFAULT_FEE - taddr_balance -= DEFAULT_FEE + sapling_balance -= LEGACY_DEFAULT_FEE + taddr_balance -= LEGACY_DEFAULT_FEE taddr_2_balance = taddr_balance # @@ -439,53 +437,53 @@ def transaction_chain(zcash): check_z_sendmany( results, '4w', zcash, sapling_zaddr_2,[ (taddr_3, starting_balance / Decimal('10')), - (sapling_zaddr_1, starting_balance / Decimal('10'))])[0] + (sapling_zaddr_1, starting_balance / Decimal('10'))], "AllowRevealedRecipients")[0] - sapling_balance -= (starting_balance / Decimal('10')) + DEFAULT_FEE + sapling_balance -= (starting_balance / Decimal('10')) + LEGACY_DEFAULT_FEE taddr_balance += starting_balance / Decimal('10') # taddr and Sapling -> Sapling - check_z_mergetoaddress(results, '4ee', zcash, [taddr_3, sapling_zaddr_1], sapling_zaddr_2, sapling_balance + (starting_balance / Decimal('10')) - DEFAULT_FEE) + check_z_mergetoaddress(results, '4ee', zcash, [taddr_3, sapling_zaddr_1], sapling_zaddr_2, sapling_balance + (starting_balance / Decimal('10')) - LEGACY_DEFAULT_FEE, "AllowRevealedSenders") - sapling_balance += (starting_balance / Decimal('10')) - DEFAULT_FEE + sapling_balance += (starting_balance / Decimal('10')) - LEGACY_DEFAULT_FEE taddr_balance -= starting_balance / Decimal('10') # Sapling -> multiple taddr check_z_sendmany(results, '4v', zcash, sapling_zaddr_2, [ (taddr_4, (starting_balance / Decimal('10'))), - (taddr_5, (starting_balance / Decimal('10')))])[0] + (taddr_5, (starting_balance / Decimal('10')))], "AllowRevealedRecipients")[0] - sapling_balance -= ((starting_balance / Decimal('10')) * Decimal('2')) + DEFAULT_FEE + sapling_balance -= ((starting_balance / Decimal('10')) * Decimal('2')) + LEGACY_DEFAULT_FEE taddr_balance += (starting_balance / Decimal('10')) * Decimal('2') # multiple taddr -> Sapling - check_z_mergetoaddress(results, '4bb',zcash, [taddr_4, taddr_5], sapling_zaddr_2, sapling_balance + ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE) - sapling_balance += ((starting_balance / Decimal('10')) * Decimal('2')) - DEFAULT_FEE + check_z_mergetoaddress(results, '4bb',zcash, [taddr_4, taddr_5], sapling_zaddr_2, sapling_balance + ((starting_balance / Decimal('10')) * Decimal('2')) - LEGACY_DEFAULT_FEE, "AllowRevealedSenders") + sapling_balance += ((starting_balance / Decimal('10')) * Decimal('2')) - LEGACY_DEFAULT_FEE taddr_balance -= (starting_balance / Decimal('10')) * Decimal('2') # multiple Sapling -> taddr - check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2, sapling_zaddr_3], taddr_2, taddr_2_balance + sapling_balance - DEFAULT_FEE) - taddr_balance += sapling_balance - DEFAULT_FEE + check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2, sapling_zaddr_3], taddr_2, taddr_2_balance + sapling_balance - LEGACY_DEFAULT_FEE, "AllowRevealedRecipients") + taddr_balance += sapling_balance - LEGACY_DEFAULT_FEE sapling_balance = Decimal('0') # taddr -> multiple Sapling taddr_2_balance = Decimal(zcash.z_getbalance(taddr_2)).quantize(Decimal('1.00000000')) check_z_sendmany(results, '4r', zcash, taddr_2, [ (sapling_zaddr_1, (starting_balance / Decimal('10'))), - (sapling_zaddr_2, taddr_2_balance - (starting_balance / Decimal('10')) - DEFAULT_FEE)])[0] + (sapling_zaddr_2, taddr_2_balance - (starting_balance / Decimal('10')) - LEGACY_DEFAULT_FEE)], "AllowFullyTransparent")[0] - sapling_balance = taddr_2_balance - DEFAULT_FEE + sapling_balance = taddr_2_balance - LEGACY_DEFAULT_FEE taddr_balance -= taddr_2_balance # multiple Sapling -> taddr - check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2], taddr_2, sapling_balance - DEFAULT_FEE) - taddr_balance += sapling_balance - DEFAULT_FEE + check_z_mergetoaddress(None, '', zcash, [sapling_zaddr_1, sapling_zaddr_2], taddr_2, sapling_balance - LEGACY_DEFAULT_FEE, "AllowRevealedRecipients") + taddr_balance += sapling_balance - LEGACY_DEFAULT_FEE sapling_balance = Decimal('0') # z_mergetoaddress taddr -> Sapling taddr_2_balance = Decimal(zcash.z_getbalance(taddr_2)).quantize(Decimal('1.00000000')) - check_z_mergetoaddress(results, '4cc', zcash, [taddr_2], sapling_zaddr_1, taddr_2_balance - DEFAULT_FEE) - sapling_balance = taddr_2_balance - DEFAULT_FEE + check_z_mergetoaddress(results, '4cc', zcash, [taddr_2], sapling_zaddr_1, taddr_2_balance - LEGACY_DEFAULT_FEE, "AllowFullyTransparent") + sapling_balance = taddr_2_balance - LEGACY_DEFAULT_FEE taddr_balance -= taddr_2_balance except Exception as e: print('Error: %s' % e) @@ -515,7 +513,7 @@ def transaction_chain(zcash): for addr in all_addrs: balance = Decimal(zcash.z_getbalance(addr)).quantize(Decimal('1.00000000')) if balance > 0: - z_sendmany(None, '', zcash, addr, [(chain_end, balance - DEFAULT_FEE)]) + z_sendmany(None, '', zcash, addr, [(chain_end, balance - LEGACY_DEFAULT_FEE)], "NoPrivacy") return results @@ -592,7 +590,10 @@ def start(self, extra_args=None, timewait=None): '-showmetrics=0', '-experimentalfeatures', '-zmergetoaddress', - '-walletrequirebackup=false' + '-walletrequirebackup=false', + '-allowdeprecated=getnewaddress', + '-allowdeprecated=z_getnewaddress', + '-allowdeprecated=z_listaddresses' ] if self.__testnet: args.append('-testnet=1') diff --git a/depend/zcash/qa/zcash/test-depends-sources-mirror.py b/depend/zcash/qa/zcash/test-depends-sources-mirror.py index d83cca1f1..de93161fe 100755 --- a/depend/zcash/qa/zcash/test-depends-sources-mirror.py +++ b/depend/zcash/qa/zcash/test-depends-sources-mirror.py @@ -13,10 +13,15 @@ import requests MIRROR_URL_DIR="https://download.z.cash/depends-sources/" -DEPENDS_SOURCES_DIR=os.path.realpath(os.path.join( + +try: + DEPENDS_SOURCES_DIR = os.environ['SOURCES_PATH'] +except KeyError: + DEPENDS_SOURCES_DIR=os.path.realpath(os.path.join( os.path.dirname(__file__), "..", "..", "depends", "sources" -)) + )) + def get_depends_sources_list(): return filter( diff --git a/depend/zcash/qa/zcash/updatecheck.py b/depend/zcash/qa/zcash/updatecheck.py index b0901ac42..350af45fa 100755 --- a/depend/zcash/qa/zcash/updatecheck.py +++ b/depend/zcash/qa/zcash/updatecheck.py @@ -105,8 +105,8 @@ def get_dependency_list(): { "v4.3.1": (4, 3, 1), "v4.2.0-rc1": None }), DependsVersionGetter("zeromq")), Dependency("leveldb", - GithubTagReleaseLister("google", "leveldb", "^v(\d+)\.(\d+)$", - { "v1.13": (1, 13) }), + GithubTagReleaseLister("google", "leveldb", "^v?(\d+)\.(\d+)$", + { "v1.13": (1, 13), "1.23": (1, 23) }), LevelDbVersionGetter()), Dependency("tl_expected", GithubTagReleaseLister("TartanLlama", "expected", "^v(\d+)\.(\d+)(?:\.(\d+))?$", @@ -156,6 +156,21 @@ def __str__(self): def __hash__(self): return hash(tuple(self)) + def __gt__(self, other): + if type(self) != type(other): + raise TypeError + + # If one of the versions is a commit hash and the other is not, treat the commit + # hash as being newer (as it indicates we are pinning a specific revision). + self_is_commit_hash = len(self) == 1 and len(self[0]) == 40 + other_is_commit_hash = len(other) == 1 and len(other[0]) == 40 + if other_is_commit_hash: + return False + if self_is_commit_hash: + return True + + return super().__gt__(other) + class Dependency: def __init__(self, name, release_lister, current_getter): self.name = name @@ -255,12 +270,12 @@ def current_version(self): mk_file = open(mk_file_path, 'r', encoding='utf8').read() regexp_whitelist = [ + "package\)_default_version=(\d+)\.(\d+)\.(\d+)$", "package\)_version=(\d+)\.(\d+)\.(\d+)$", "package\)_version=(\d+)\.(\d+)$", "package\)_version=(\d+)_(\d+)_(\d+)$", "package\)_version=(\d+)\.(\d+)\.(\d+)([a-z])$", - # Workaround for wasi 0.9.0 preview - "package\)_version=(\d+)\.(\d+)\.(\d+)\+wasi-snapshot-preview1$", + "package\)_version=([0-9a-f]{40})$", ] current_version = None @@ -271,7 +286,9 @@ def current_version(self): current_version = Version(match.groups()) if not current_version: - raise RuntimeError("Couldn't parse version number from depends .mk file.") + raise RuntimeError( + "Couldn't parse version number from depends %s.mk file." % (safe_depends(self.name),) + ) return current_version diff --git a/depend/zcash/rust-toolchain.toml b/depend/zcash/rust-toolchain.toml index 10add70bb..d04f2eb53 100644 --- a/depend/zcash/rust-toolchain.toml +++ b/depend/zcash/rust-toolchain.toml @@ -1,2 +1,3 @@ [toolchain] -channel = "1.64.0" +channel = "1.69.0" +components = ["clippy", "rustfmt"] diff --git a/depend/zcash/src/Makefile.am b/depend/zcash/src/Makefile.am index 8b14284ca..a66bed8a6 100644 --- a/depend/zcash/src/Makefile.am +++ b/depend/zcash/src/Makefile.am @@ -28,6 +28,7 @@ LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBBITCOIN_CRYPTO_SSE41=crypto/libbitcoin_crypto_sse41.a LIBBITCOIN_CRYPTO_AVX2=crypto/libbitcoin_crypto_avx2.a +LIBCXXBRIDGE=libcxxbridge.a LIBRUSTZCASH=$(top_builddir)/target/$(RUST_TARGET)/release/librustzcash.a LIBSECP256K1=secp256k1/libsecp256k1.la LIBUNIVALUE=univalue/libunivalue.la @@ -49,28 +50,24 @@ if ENABLE_WALLET LIBBITCOIN_WALLET=libbitcoin_wallet.a endif -# TODO: Figure out how to avoid an explicit file list. CXXBRIDGE_RS = \ rust/src/blake2b.rs \ - rust/src/bundlecache.rs \ + rust/src/ed25519.rs \ rust/src/equihash.rs \ - rust/src/orchard_bundle.rs \ - rust/src/sapling.rs \ - rust/src/wallet_scanner.rs + rust/src/streams.rs \ + rust/src/bridge.rs CXXBRIDGE_H = \ rust/gen/include/rust/blake2b.h \ - rust/gen/include/rust/bundlecache.h \ + rust/gen/include/rust/ed25519.h \ rust/gen/include/rust/equihash.h \ - rust/gen/include/rust/orchard_bundle.h \ - rust/gen/include/rust/sapling.h \ - rust/gen/include/rust/wallet_scanner.h + rust/gen/include/rust/streams.h \ + rust/gen/include/rust/bridge.h CXXBRIDGE_CPP = \ rust/gen/src/blake2b.cpp \ - rust/gen/src/bundlecache.cpp \ + rust/gen/src/ed25519.cpp \ rust/gen/src/equihash.cpp \ - rust/gen/src/orchard_bundle.cpp \ - rust/gen/src/sapling.cpp \ - rust/gen/src/wallet_scanner.cpp + rust/gen/src/streams.cpp \ + rust/gen/src/bridge.cpp # We add a rust/cxx.h include to indicate that we provide this (via the rustcxx depends # package), so that cxxbridge doesn't include it within the generated headers and code. @@ -163,6 +160,7 @@ EXTRA_LIBRARIES += \ $(LIBBITCOIN_CLI) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) \ + $(LIBCXXBRIDGE) \ $(LIBZCASH) lib_LTLIBRARIES = $(LIBZCASH_SCRIPT) @@ -246,6 +244,7 @@ BITCOIN_CORE_H = \ httprpc.h \ httpserver.h \ init.h \ + int128.h \ key.h \ key_constants.h \ key_io.h \ @@ -289,6 +288,7 @@ BITCOIN_CORE_H = \ serialize.h \ spentindex.h \ streams.h \ + streams_rust.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ support/cleanse.h \ @@ -321,6 +321,7 @@ BITCOIN_CORE_H = \ wallet/asyncrpcoperation_saplingmigration.h \ wallet/asyncrpcoperation_sendmany.h \ wallet/asyncrpcoperation_shieldcoinbase.h \ + wallet/wallet_tx_builder.h \ wallet/crypter.h \ wallet/db.h \ wallet/memo.h \ @@ -332,6 +333,8 @@ BITCOIN_CORE_H = \ wallet/walletdb.h \ wallet/wallet_tx_builder.h \ warnings.h \ + weighted_map.h \ + zip317.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ zmq/zmqnotificationinterface.h \ @@ -391,6 +394,12 @@ libbitcoin_server_a_SOURCES = \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) +libcxxbridge_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) +libcxxbridge_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +libcxxbridge_a_SOURCES = \ + $(CXXBRIDGE_CPP) \ + $(CXXBRIDGE_H) + if ENABLE_ZMQ libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -411,6 +420,7 @@ libbitcoin_wallet_a_SOURCES = \ wallet/asyncrpcoperation_saplingmigration.cpp \ wallet/asyncrpcoperation_sendmany.cpp \ wallet/asyncrpcoperation_shieldcoinbase.cpp \ + wallet/wallet_tx_builder.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/orchard.cpp \ @@ -523,9 +533,11 @@ libbitcoin_common_a_SOURCES = \ script/script_error.cpp \ script/sign.cpp \ script/standard.cpp \ + streams_rust.cpp \ transaction_builder.cpp \ util/test.cpp \ warnings.cpp \ + zip317.cpp \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) @@ -584,6 +596,7 @@ zcashd_LDADD = \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_COMMON) \ + $(LIBCXXBRIDGE) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_ZMQ) \ @@ -617,6 +630,7 @@ zcash_cli_LDADD = \ $(LIBBITCOIN_CLI) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ + $(LIBCXXBRIDGE) \ $(BOOST_LIBS) \ $(EVENT_LIBS) \ $(LIBRUSTZCASH) \ @@ -641,6 +655,7 @@ zcash_tx_LDADD = \ $(LIBUNIVALUE) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ + $(LIBCXXBRIDGE) \ $(LIBSECP256K1) \ $(LIBRUSTZCASH) \ $(LIBZCASH) \ @@ -652,7 +667,6 @@ zcash_tx_LDADD += $(BOOST_LIBS) # zcash protocol primitives # libzcash_a_SOURCES = \ - $(CXXBRIDGE_CPP) \ zcash/IncrementalMerkleTree.cpp \ zcash/NoteEncryption.cpp \ zcash/Address.cpp \ @@ -672,7 +686,6 @@ libzcash_a_SOURCES = \ libzcash_a_CPPFLAGS = $(AM_CPPFLAGS) $(PIC_FLAGS) $(BITCOIN_INCLUDES) libzcash_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -libzcash_a_LDFLAGS = $(AM_LDFLAGS) # zcash_script library # if BUILD_BITCOIN_LIBS @@ -691,7 +704,8 @@ libzcash_script_la_SOURCES = \ script/interpreter.cpp \ script/script.cpp \ uint256.cpp \ - util/strencodings.cpp + util/strencodings.cpp \ + zip317.cpp if GLIBC_BACK_COMPAT libzcash_script_la_SOURCES += compat/glibc_compat.cpp @@ -724,7 +738,7 @@ clean-local: -$(MAKE) -C secp256k1 clean -$(MAKE) -C univalue clean rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno - rm -f rust/gen + rm -rf rust/gen rm -f fuzz.cpp rm -rf fuzzing/*/output -rm -f config.h @@ -734,10 +748,6 @@ clean-local: @test -f $(WINDRES) $(AM_V_GEN) $(WINDRES) -DWINDRES_PREPROC -i $< -o $@ -.mm.o: - $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CXXFLAGS) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $< - check-symbols: $(bin_PROGRAMS) $(bin_SCRIPTS) if GLIBC_BACK_COMPAT @echo "Checking glibc back compat of [$(bin_PROGRAMS) $(bin_SCRIPTS)]..." diff --git a/depend/zcash/src/Makefile.bench.include b/depend/zcash/src/Makefile.bench.include index c3d98c2cf..d93753f05 100644 --- a/depend/zcash/src/Makefile.bench.include +++ b/depend/zcash/src/Makefile.bench.include @@ -32,6 +32,7 @@ bench_bench_bitcoin_LDADD = \ $(LIBBITCOIN_UNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) \ + $(LIBCXXBRIDGE) \ $(LIBLEVELDB) \ $(LIBLEVELDB_SSE42) \ $(LIBMEMENV) \ diff --git a/depend/zcash/src/Makefile.gtest.include b/depend/zcash/src/Makefile.gtest.include index ec80559d7..c69f3a056 100644 --- a/depend/zcash/src/Makefile.gtest.include +++ b/depend/zcash/src/Makefile.gtest.include @@ -51,6 +51,7 @@ zcash_gtest_SOURCES = \ gtest/test_txid.cpp \ gtest/test_upgrades.cpp \ gtest/test_validation.cpp \ + gtest/test_weightedmap.cpp \ gtest/test_zip32.cpp \ gtest/test_coins.cpp if ENABLE_WALLET @@ -77,6 +78,7 @@ zcash_gtest_LDADD = \ $(LIBBITCOIN_ZMQ) \ $(LIBBITCOIN_PROTON) \ $(LIBBITCOIN_CRYPTO) \ + $(LIBCXXBRIDGE) \ $(LIBUNIVALUE) \ $(LIBLEVELDB) \ $(LIBLEVELDB_SSE42) \ diff --git a/depend/zcash/src/Makefile.test.include b/depend/zcash/src/Makefile.test.include index 1d7727d4c..6ef416753 100644 --- a/depend/zcash/src/Makefile.test.include +++ b/depend/zcash/src/Makefile.test.include @@ -138,7 +138,7 @@ if ENABLE_WALLET test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) endif -test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \ +test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBCXXBRIDGE) $(LIBUNIVALUE) \ $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) diff --git a/depend/zcash/src/amount.cpp b/depend/zcash/src/amount.cpp index 11468eca2..948cd3143 100644 --- a/depend/zcash/src/amount.cpp +++ b/depend/zcash/src/amount.cpp @@ -5,6 +5,7 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php . #include "amount.h" +#include "consensus/consensus.h" #include "policy/fees.h" #include "tinyformat.h" @@ -14,10 +15,11 @@ const std::string MINOR_CURRENCY_UNIT = "zatoshis"; CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nSize) { - if (nSize > 0) - nSatoshisPerK = nFeePaid*1000/nSize; - else + if (nSize > 0) { + nSatoshisPerK = std::min(nFeePaid*1000/nSize, (uint64_t)INT64_MAX / MAX_BLOCK_SIZE); + } else { nSatoshisPerK = 0; + } } CAmount CFeeRate::GetFeeForRelay(size_t nSize) const @@ -37,5 +39,5 @@ CAmount CFeeRate::GetFee(size_t nSize) const std::string CFeeRate::ToString() const { - return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT); + return strprintf("%d.%08d %s per 1000 bytes", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT); } diff --git a/depend/zcash/src/amount.h b/depend/zcash/src/amount.h index bd9316dbf..44991b26d 100644 --- a/depend/zcash/src/amount.h +++ b/depend/zcash/src/amount.h @@ -22,8 +22,8 @@ extern const std::string MINOR_CURRENCY_UNIT; /** No amount larger than this (in zatoshi) is valid. * - * Note that this constant is *not* the total money supply, which in Bitcoin - * currently happens to be less than 21,000,000 BTC for various reasons, but + * Note that this constant is *not* the total money supply, which in Zcash + * currently happens to be less than 21,000,000 ZEC for various reasons, but * rather a sanity check. As this sanity check is used by consensus-critical * validation code, the exact value of the MAX_MONEY constant is consensus * critical; in unusual circumstances like a(nother) overflow bug that allowed diff --git a/depend/zcash/src/bench/verification.cpp b/depend/zcash/src/bench/verification.cpp index 7441639be..83fb0c670 100644 --- a/depend/zcash/src/bench/verification.cpp +++ b/depend/zcash/src/bench/verification.cpp @@ -15,8 +15,8 @@ #include "version.h" #include "librustzcash.h" +#include #include -#include static void ECDSA(benchmark::State& state) { @@ -80,20 +80,20 @@ static void ECDSA(benchmark::State& state) static void JoinSplitSig(benchmark::State& state) { - Ed25519VerificationKey joinSplitPubKey; - Ed25519SigningKey joinSplitPrivKey; + ed25519::VerificationKey joinSplitPubKey; + ed25519::SigningKey joinSplitPrivKey; uint256 dataToBeSigned; - Ed25519Signature joinSplitSig; + ed25519::Signature joinSplitSig; - ed25519_generate_keypair(&joinSplitPrivKey, &joinSplitPubKey); - ed25519_sign(&joinSplitPrivKey, dataToBeSigned.begin(), 32, &joinSplitSig); + ed25519::generate_keypair(joinSplitPrivKey, joinSplitPubKey); + ed25519::sign(joinSplitPrivKey, {dataToBeSigned.begin(), 32}, joinSplitSig); while (state.KeepRunning()) { // Declared with warn_unused_result. - auto res = ed25519_verify( - &joinSplitPubKey, - &joinSplitSig, - dataToBeSigned.begin(), 32); + auto res = ed25519::verify( + joinSplitPubKey, + joinSplitSig, + {dataToBeSigned.begin(), 32}); } } diff --git a/depend/zcash/src/bitcoin-cli.cpp b/depend/zcash/src/bitcoin-cli.cpp index 4ef57104f..2657e199c 100644 --- a/depend/zcash/src/bitcoin-cli.cpp +++ b/depend/zcash/src/bitcoin-cli.cpp @@ -9,6 +9,7 @@ #include "fs.h" #include "rpc/client.h" #include "rpc/protocol.h" +#include "util/match.h" #include "util/system.h" #include "util/strencodings.h" @@ -197,7 +198,33 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx) } #endif -UniValue CallRPC(const std::string& strMethod, const UniValue& params) +tl::expected CallRPC(const std::string& strMethod, const UniValue& params); + +UniValue ConvertValues(const std::string &strMethod, const std::vector &strParams) +{ + return RPCConvertValues(strMethod, strParams) + .map_error([&](auto failure) { + throw std::runtime_error( + FormatConversionFailure(strMethod, failure) + + examine(failure, match { + [](const UnknownRPCMethod&) { return std::string{}; }, + [&](const auto&) { + auto helpMsg = CallRPC("help", ConvertValues("help", {strMethod})); + return "\n\n" + + (helpMsg.has_value() + ? "Usage: " + helpMsg.value().get_str() + : strprintf( + "An error occurred while attempting to retrieve the " + "help text for %s: %s", + strMethod, + helpMsg.error().get_str())); + } + })); + }) + .value(); +} + +tl::expected CallRPC(const std::string& strMethod, const UniValue& params) { std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT); int port = GetArg("-rpcport", BaseParams().RPCPort()); @@ -269,7 +296,11 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params) if (reply.empty()) throw std::runtime_error("expected reply to have result, error and id properties"); - return reply; + const UniValue error = find_value(reply, "error"); + + return error.isNull() + ? tl::expected(find_value(reply, "result")) + : tl::make_unexpected(error); } bool SetStdinEcho(bool enable = true) @@ -340,19 +371,17 @@ int CommandLineRPC(int argc, char *argv[]) if (args.size() < 1) throw std::runtime_error("too few parameters (need at least command)"); std::string strMethod = args[0]; - UniValue params = RPCConvertValues(strMethod, std::vector(args.begin()+1, args.end())); + auto params = + ConvertValues(strMethod, std::vector(args.begin()+1, args.end())); // Execute and handle connection failures with -rpcwait const bool fWait = GetBoolArg("-rpcwait", false); do { try { - const UniValue reply = CallRPC(strMethod, params); - - // Parse reply - const UniValue& result = find_value(reply, "result"); - const UniValue& error = find_value(reply, "error"); + auto reply = CallRPC(strMethod, params); - if (!error.isNull()) { + if (!reply.has_value()) { + auto error = reply.error(); // Error int code = error["code"].get_int(); if (fWait && code == RPC_IN_WARMUP) @@ -369,6 +398,7 @@ int CommandLineRPC(int argc, char *argv[]) strPrint += "error message:\n"+errMsg.get_str(); } } else { + auto result = reply.value(); // Result if (result.isNull()) strPrint = ""; diff --git a/depend/zcash/src/bitcoin-tx.cpp b/depend/zcash/src/bitcoin-tx.cpp index b44826a17..f6acf9d05 100644 --- a/depend/zcash/src/bitcoin-tx.cpp +++ b/depend/zcash/src/bitcoin-tx.cpp @@ -378,7 +378,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& strInput) // starts as a clone of the raw tx: CMutableTransaction mergedTx(txVariants[0]); bool fComplete = true; - CCoinsView viewDummy; + CCoinsViewDummy viewDummy; CCoinsViewCache view(&viewDummy); if (!registers.count("privatekeys")) diff --git a/depend/zcash/src/bitcoind.cpp b/depend/zcash/src/bitcoind.cpp index 2c9c0e14a..152044ac7 100644 --- a/depend/zcash/src/bitcoind.cpp +++ b/depend/zcash/src/bitcoind.cpp @@ -94,14 +94,6 @@ bool AppInit(int argc, char* argv[]) return true; } - // Handle setting of allowed-deprecated features as early as possible - // so that it's possible for other initialization steps to respect them. - auto deprecationError = SetAllowedDeprecatedFeaturesFromCLIArgs(); - if (deprecationError.has_value()) { - fprintf(stderr, "%s", deprecationError.value().c_str()); - return false; - } - try { if (!fs::is_directory(GetDataDir(false))) @@ -143,6 +135,14 @@ bool AppInit(int argc, char* argv[]) return false; } + // Handle setting of allowed-deprecated features as early as possible + // so that it's possible for other initialization steps to respect them. + auto deprecationError = LoadAllowedDeprecatedFeatures(); + if (deprecationError.has_value()) { + fprintf(stderr, "%s", deprecationError.value().c_str()); + return false; + } + // Command-line RPC bool fCommandLine = false; for (int i = 1; i < argc; i++) diff --git a/depend/zcash/src/bloom.cpp b/depend/zcash/src/bloom.cpp index c97bfd61b..86bb99d18 100644 --- a/depend/zcash/src/bloom.cpp +++ b/depend/zcash/src/bloom.cpp @@ -187,15 +187,7 @@ CRollingBloomFilter::CRollingBloomFilter(const unsigned int nElements, const dou * => nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - pow(fpRate, 1.0 / nHashFuncs)) * => nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs)) */ - uint32_t nFilterBits = (uint32_t)ceil(-1.0 * nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs))); - data.clear(); - /* For each data element we need to store 2 bits. If both bits are 0, the - * bit is treated as unset. If the bits are (01), (10), or (11), the bit is - * treated as set in generation 1, 2, or 3 respectively. - * These bits are stored in separate integers: position P corresponds to bit - * (P & 63) of the integers data[(P >> 6) * 2] and data[(P >> 6) * 2 + 1]. */ - data.resize(((nFilterBits + 63) / 64) << 1); - reset(); + nFilterBits = (uint32_t)ceil(-1.0 * nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs))); } /* Similar to CBloomFilter::Hash */ @@ -213,6 +205,9 @@ static inline uint32_t FastMod(uint32_t x, size_t n) { void CRollingBloomFilter::insert(const std::vector& vKey) { + if (data.empty()) { + initialize(); + } if (nEntriesThisGeneration == nEntriesPerGeneration) { nEntriesThisGeneration = 0; nGeneration++; @@ -250,6 +245,9 @@ void CRollingBloomFilter::insert(const uint256& hash) bool CRollingBloomFilter::contains(const std::vector& vKey) const { + if (data.empty()) { + return false; + } for (int n = 0; n < nHashFuncs; n++) { uint32_t h = RollingBloomHash(n, nTweak, vKey); int bit = h & 0x3F; @@ -268,8 +266,19 @@ bool CRollingBloomFilter::contains(const uint256& hash) const return contains(vData); } -void CRollingBloomFilter::reset() +void CRollingBloomFilter::reset() { + std::vector().swap(data); +} + +void CRollingBloomFilter::initialize() { + /* For each data element we need to store 2 bits. If both bits are 0, the + * bit is treated as unset. If the bits are (01), (10), or (11), the bit is + * treated as set in generation 1, 2, or 3 respectively. + * These bits are stored in separate integers: position P corresponds to bit + * (P & 63) of the integers data[(P >> 6) * 2] and data[(P >> 6) * 2 + 1]. */ + data.resize(((nFilterBits + 63) / 64) << 1); + nTweak = GetRand(std::numeric_limits::max()); nEntriesThisGeneration = 0; nGeneration = 1; diff --git a/depend/zcash/src/bloom.h b/depend/zcash/src/bloom.h index f1cecceb0..9c9687a8c 100644 --- a/depend/zcash/src/bloom.h +++ b/depend/zcash/src/bloom.h @@ -131,7 +131,13 @@ class CRollingBloomFilter void reset(); +protected: + bool is_data_empty() const { return data.empty(); } + private: + void initialize(); + + uint32_t nFilterBits; int nEntriesPerGeneration; int nEntriesThisGeneration; int nGeneration; diff --git a/depend/zcash/src/chainparams.cpp b/depend/zcash/src/chainparams.cpp index 693b57c70..3b3cce6b3 100644 --- a/depend/zcash/src/chainparams.cpp +++ b/depend/zcash/src/chainparams.cpp @@ -242,7 +242,7 @@ class CMainParams : public CChainParams { } // The best chain should have at least this much work. - consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000afb16bd5d58e5be"); + consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000c7da51ec335d66c"); /** * The message start string should be awesome! ⓩ❤ @@ -300,11 +300,12 @@ class CMainParams : public CChainParams { (1200000, uint256S("0x0000000000347d5011108fdcf667c93e622e8635c94e586556898e41db18d192")) (1400000, uint256S("0x0000000001155ecec0ad3924d47ad476c0a5ed7527b8776f53cbda1a780b9f76")) (1600000, uint256S("0x0000000000aae69fb228f90e77f34c24b7920667eaca726c3a3939536f03dcfc")) - (1860000, uint256S("0x000000000043a968c78af5fb8133e00e6fe340051c19dd969e53ab62bf3dc22a")), - 1667052073, // * UNIX timestamp of last checkpoint block - 11768038, // * total number of transactions between genesis and last checkpoint - 3644 // * estimated number of transactions per day after checkpoint - // total number of tx / (checkpoint block height / (24 * 24)) + (1860000, uint256S("0x000000000043a968c78af5fb8133e00e6fe340051c19dd969e53ab62bf3dc22a")) + (2000000, uint256S("0x00000000010accaf2f87934765dc2e0bf4823a2b1ae2c1395b334acfce52ad68")), + 1677602242, // * UNIX timestamp of last checkpoint block + 12380742, // * total number of transactions between genesis and last checkpoint + 7131 // * estimated number of transactions per day after checkpoint + // (total number of tx * 48 * 24) / checkpoint block height }; // Hardcoded fallback value for the Sprout shielded value pool balance @@ -776,14 +777,14 @@ class CRegTestParams : public CChainParams { }; static CRegTestParams regTestParams; -static CChainParams *pCurrentParams = 0; +static const CChainParams* pCurrentParams = nullptr; -const CChainParams &Params() { +const CChainParams& Params() { assert(pCurrentParams); return *pCurrentParams; } -CChainParams& Params(const std::string& chain) +const CChainParams& Params(const std::string& chain) { if (chain == CBaseChainParams::MAIN) return mainParams; diff --git a/depend/zcash/src/chainparams.h b/depend/zcash/src/chainparams.h index 74a1323fb..0286c2c3b 100644 --- a/depend/zcash/src/chainparams.h +++ b/depend/zcash/src/chainparams.h @@ -14,6 +14,8 @@ #include +#include + struct CDNSSeedData { std::string name, host; CDNSSeedData(const std::string &strName, const std::string &strHost) : name(strName), host(strHost) {} @@ -44,6 +46,16 @@ class CChainParams: public KeyConstants { public: const Consensus::Params& GetConsensus() const { return consensus; } + const rust::Box RustNetwork() const { + return consensus::network( + NetworkIDString(), + consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight, + consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight, + consensus.vUpgrades[Consensus::UPGRADE_BLOSSOM].nActivationHeight, + consensus.vUpgrades[Consensus::UPGRADE_HEARTWOOD].nActivationHeight, + consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, + consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight); + } const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } const std::vector& AlertKey() const { return vAlertPubKey; } int GetDefaultPort() const { return nDefaultPort; } @@ -128,7 +140,7 @@ const CChainParams &Params(); /** * @returns CChainParams for the given BIP70 chain name. */ -CChainParams& Params(const std::string& chain); +const CChainParams& Params(const std::string& chain); /** * Sets the params returned by Params() to those for the given BIP70 chain name. diff --git a/depend/zcash/src/clientversion.h b/depend/zcash/src/clientversion.h index 255dab614..cd4a976ee 100644 --- a/depend/zcash/src/clientversion.h +++ b/depend/zcash/src/clientversion.h @@ -16,7 +16,7 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 5 -#define CLIENT_VERSION_MINOR 4 +#define CLIENT_VERSION_MINOR 5 #define CLIENT_VERSION_REVISION 0 #define CLIENT_VERSION_BUILD 50 diff --git a/depend/zcash/src/coins.cpp b/depend/zcash/src/coins.cpp index 59548d74c..b6f4e0471 100644 --- a/depend/zcash/src/coins.cpp +++ b/depend/zcash/src/coins.cpp @@ -45,32 +45,6 @@ bool CCoins::Spend(uint32_t nPos) Cleanup(); return true; } -bool CCoinsView::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return false; } -bool CCoinsView::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { return false; } -bool CCoinsView::GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const { return false; } -bool CCoinsView::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return false; } -bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; } -bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; } -uint256 CCoinsView::GetBestBlock() const { return uint256(); } -uint256 CCoinsView::GetBestAnchor(ShieldedType type) const { return uint256(); }; -HistoryIndex CCoinsView::GetHistoryLength(uint32_t epochId) const { return 0; } -HistoryNode CCoinsView::GetHistoryAt(uint32_t epochId, HistoryIndex index) const { return HistoryNode(); } -uint256 CCoinsView::GetHistoryRoot(uint32_t epochId) const { return uint256(); } - -bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, - const uint256 &hashBlock, - const uint256 &hashSproutAnchor, - const uint256 &hashSaplingAnchor, - const uint256 &hashOrchardAnchor, - CAnchorsSproutMap &mapSproutAnchors, - CAnchorsSaplingMap &mapSaplingAnchors, - CAnchorsOrchardMap &mapOrchardAnchors, - CNullifiersMap &mapSproutNullifiers, - CNullifiersMap &mapSaplingNullifiers, - CNullifiersMap &mapOrchardNullifiers, - CHistoryCacheMap &historyCacheMap) { return false; } -bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; } - CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { } @@ -1109,39 +1083,6 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const return true; } -double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const -{ - if (tx.IsCoinBase()) - return 0.0; - - // Shielded transfers do not reveal any information about the value or age of a note, so we - // cannot apply the priority algorithm used for transparent utxos. Instead, we just - // use the maximum priority for all (partially or fully) shielded transactions. - // (Note that coinbase transactions cannot contain JoinSplits, or Sapling shielded Spends or Outputs.) - - if (tx.vJoinSplit.size() > 0 || - tx.vShieldedSpend.size() > 0 || - tx.vShieldedOutput.size() > 0 || - tx.GetOrchardBundle().IsPresent()) - { - return MAX_PRIORITY; - } - - // FIXME: this logic is partially duplicated between here and CreateNewBlock in miner.cpp. - double dResult = 0.0; - for (const CTxIn& txin : tx.vin) - { - const CCoins* coins = AccessCoins(txin.prevout.hash); - assert(coins); - if (!coins->IsAvailable(txin.prevout.n)) continue; - if (coins->nHeight < nHeight) { - dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight); - } - } - - return tx.ComputePriority(dResult); -} - CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage) : cache(cache_), it(it_), cachedCoinUsage(usage) { assert(!cache.hasModifier); cache.hasModifier = true; diff --git a/depend/zcash/src/coins.h b/depend/zcash/src/coins.h index 43c235b0f..96cf8f815 100644 --- a/depend/zcash/src/coins.h +++ b/depend/zcash/src/coins.h @@ -49,7 +49,7 @@ * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 - * * 8358: compact amount representation for 60000000000 (600 BTC) + * * 8358: compact amount representation for 60000000000 (600 ZEC) * * 00: special txout type pay-to-pubkey-hash * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 * - height = 203998 @@ -65,11 +65,11 @@ * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee - * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) + * * 86ef97d579: compact amount representation for 234925952 (2.35 ZEC) * * 00: special txout type pay-to-pubkey-hash * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 - * * bbd123: compact amount representation for 110397 (0.001 BTC) + * * bbd123: compact amount representation for 110397 (0.001 ZEC) * * 00: special txout type pay-to-pubkey-hash * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 * - height = 120891 @@ -364,38 +364,38 @@ class CCoinsView { public: //! Retrieve the tree (Sprout) at a particular anchored root in the chain - virtual bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; + virtual bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const = 0; //! Retrieve the tree (Sapling) at a particular anchored root in the chain - virtual bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; + virtual bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const = 0; //! Retrieve the tree (Orchard) at a particular anchored root in the chain - virtual bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const; + virtual bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const = 0; //! Determine whether a nullifier is spent or not - virtual bool GetNullifier(const uint256 &nullifier, ShieldedType type) const; + virtual bool GetNullifier(const uint256 &nullifier, ShieldedType type) const = 0; //! Retrieve the CCoins (unspent transaction outputs) for a given txid - virtual bool GetCoins(const uint256 &txid, CCoins &coins) const; + virtual bool GetCoins(const uint256 &txid, CCoins &coins) const = 0; //! Just check whether we have data for a given txid. //! This may (but cannot always) return true for fully spent transactions - virtual bool HaveCoins(const uint256 &txid) const; + virtual bool HaveCoins(const uint256 &txid) const = 0; //! Retrieve the block hash whose state this CCoinsView currently represents - virtual uint256 GetBestBlock() const; + virtual uint256 GetBestBlock() const = 0; //! Get the current "tip" or the latest anchored tree root in the chain - virtual uint256 GetBestAnchor(ShieldedType type) const; + virtual uint256 GetBestAnchor(ShieldedType type) const = 0; //! Get the current chain history length (which should be roughly chain height x2) - virtual HistoryIndex GetHistoryLength(uint32_t epochId) const; + virtual HistoryIndex GetHistoryLength(uint32_t epochId) const = 0; //! Get history node at specified index - virtual HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const; + virtual HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const = 0; //! Get current history root - virtual uint256 GetHistoryRoot(uint32_t epochId) const; + virtual uint256 GetHistoryRoot(uint32_t epochId) const = 0; //! Do a bulk modification (multiple CCoins changes + BestBlock change). //! The passed mapCoins can be modified. @@ -410,15 +410,47 @@ class CCoinsView CNullifiersMap &mapSproutNullifiers, CNullifiersMap &mapSaplingNullifiers, CNullifiersMap &mapOrchardNullifiers, - CHistoryCacheMap &historyCacheMap); + CHistoryCacheMap &historyCacheMap) = 0; //! Calculate statistics about the unspent transaction output set - virtual bool GetStats(CCoinsStats &stats) const; + virtual bool GetStats(CCoinsStats &stats) const = 0; //! As we use CCoinsViews polymorphically, have a virtual destructor virtual ~CCoinsView() {} }; +class CCoinsViewDummy : public CCoinsView +{ +public: + ~CCoinsViewDummy() {} + + bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return false; } + bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { return false; } + bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const { return false; } + bool GetNullifier(const uint256 &nullifier, ShieldedType type) const { return false; } + bool GetCoins(const uint256 &txid, CCoins &coins) const { return false; } + bool HaveCoins(const uint256 &txid) const { return false; } + uint256 GetBestBlock() const { return uint256(); } + uint256 GetBestAnchor(ShieldedType type) const { return uint256(); }; + HistoryIndex GetHistoryLength(uint32_t epochId) const { return 0; } + HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const { return HistoryNode(); } + uint256 GetHistoryRoot(uint32_t epochId) const { return uint256(); } + + bool BatchWrite(CCoinsMap &mapCoins, + const uint256 &hashBlock, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + const uint256 &hashOrchardAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, + CAnchorsOrchardMap &mapOrchardAnchors, + CNullifiersMap &mapSproutNullifiers, + CNullifiersMap &mapSaplingNullifiers, + CNullifiersMap &mapOrchardNullifiers, + CHistoryCacheMap &historyCacheMap) { return false; } + + bool GetStats(CCoinsStats &stats) const { return false; } +}; /** CCoinsView backed by another CCoinsView */ class CCoinsViewBacked : public CCoinsView @@ -428,6 +460,8 @@ class CCoinsViewBacked : public CCoinsView public: CCoinsViewBacked(CCoinsView *viewIn); + ~CCoinsViewBacked() {} + bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const; @@ -620,12 +654,9 @@ class CCoinsViewCache : public CCoinsViewBacked //! Check whether all prevouts of the transaction are present in the UTXO set represented by this view bool HaveInputs(const CTransaction& tx) const; - //! Check whether all joinsplit and sapling spend requirements (anchors/nullifiers) are satisfied + //! Check whether all shielded spend requirements (anchors/nullifiers) are satisfied tl::expected CheckShieldedRequirements(const CTransaction& tx) const; - //! Return priority of tx at height nHeight - double GetPriority(const CTransaction &tx, int nHeight) const; - const CTxOut &GetOutputFor(const CTxIn& input) const; friend class CCoinsModifier; diff --git a/depend/zcash/src/compat.h b/depend/zcash/src/compat.h index b80021089..32aa2a932 100644 --- a/depend/zcash/src/compat.h +++ b/depend/zcash/src/compat.h @@ -26,6 +26,11 @@ #include #include #include + +#ifdef __MINGW32__ +// Needed for isatty. +#include +#endif #else #include #include diff --git a/depend/zcash/src/consensus/params.cpp b/depend/zcash/src/consensus/params.cpp index 34e94f48e..51dfb183c 100644 --- a/depend/zcash/src/consensus/params.cpp +++ b/depend/zcash/src/consensus/params.cpp @@ -188,7 +188,7 @@ namespace Consensus { throw std::runtime_error("Funding stream address was not a valid " PACKAGE_NAME " address."); } - std::visit(match { + examine(addr.value(), match { [&](const CKeyID& keyId) { addresses.push_back(GetScriptForDestination(keyId)); }, @@ -201,7 +201,7 @@ namespace Consensus { [&](const auto& zaddr) { throw std::runtime_error("Funding stream address was not a valid transparent P2SH or Sapling address."); } - }, addr.value()); + }); } auto validationResult = FundingStream::ValidateFundingStream(params, startHeight, endHeight, addresses); diff --git a/depend/zcash/src/crypto/equihash.cpp b/depend/zcash/src/crypto/equihash.cpp index 5f3c324ac..fbfb5d902 100644 --- a/depend/zcash/src/crypto/equihash.cpp +++ b/depend/zcash/src/crypto/equihash.cpp @@ -127,7 +127,7 @@ void EhIndexToArray(const eh_index i, unsigned char* array) memcpy(array, &bei, sizeof(eh_index)); } -std::vector GetMinimalFromIndices(std::vector indices, +std::vector GetMinimalFromIndices(const std::vector& indices, size_t cBitLen) { assert(((cBitLen+1)+7)/8 <= sizeof(eh_index)); diff --git a/depend/zcash/src/crypto/equihash.h b/depend/zcash/src/crypto/equihash.h index a57bc0f83..586068d0a 100644 --- a/depend/zcash/src/crypto/equihash.h +++ b/depend/zcash/src/crypto/equihash.h @@ -16,7 +16,7 @@ inline constexpr size_t equihash_solution_size(unsigned int N, unsigned int K) { typedef uint32_t eh_index; typedef uint8_t eh_trunc; -std::vector GetMinimalFromIndices(std::vector indices, +std::vector GetMinimalFromIndices(const std::vector& indices, size_t cBitLen); void CompressArray(const unsigned char* in, size_t in_len, unsigned char* out, size_t out_len, diff --git a/depend/zcash/src/deprecation.cpp b/depend/zcash/src/deprecation.cpp index 47569752c..28df90062 100644 --- a/depend/zcash/src/deprecation.cpp +++ b/depend/zcash/src/deprecation.cpp @@ -9,10 +9,11 @@ #include "init.h" #include "ui_interface.h" #include "util/system.h" -#include "chainparams.h" // Flags that enable deprecated functionality. bool fEnableGbtOldHashes = true; +bool fEnableDeprecationInfoDeprecationHeight = true; +bool fEnableAddrTypeField = true; #ifdef ENABLE_WALLET bool fEnableGetNewAddress = true; bool fEnableGetRawChangeAddress = true; @@ -21,16 +22,20 @@ bool fEnableZGetBalance = true; bool fEnableZGetTotalBalance = true; bool fEnableZListAddresses = true; bool fEnableLegacyPrivacyStrategy = true; -bool fEnableAddrTypeField = true; bool fEnableWalletTxVJoinSplit = true; #endif static const std::string CLIENT_VERSION_STR = FormatVersion(CLIENT_VERSION); -void EnforceNodeDeprecation(int nHeight, bool forceLogging, bool fThread) { +int64_t EstimatedNodeDeprecationTime(const CClock& clock, int nHeight) { + auto blocksToDeprecation = DEPRECATION_HEIGHT - nHeight; + + return clock.GetTime() + (blocksToDeprecation * Consensus::POST_BLOSSOM_POW_TARGET_SPACING); +} +void EnforceNodeDeprecation(const CChainParams& params, int nHeight, bool forceLogging, bool fThread) { // Do not enforce deprecation in regtest or on testnet - std::string networkID = Params().NetworkIDString(); + std::string networkID = params.NetworkIDString(); if (networkID != "main") return; int blocksToDeprecation = DEPRECATION_HEIGHT - nHeight; @@ -61,7 +66,7 @@ void EnforceNodeDeprecation(int nHeight, bool forceLogging, bool fThread) { } } -std::optional SetAllowedDeprecatedFeaturesFromCLIArgs() { +std::optional LoadAllowedDeprecatedFeatures() { auto args = GetMultiArg("-allowdeprecated"); std::set allowdeprecated(args.begin(), args.end()); @@ -93,6 +98,8 @@ std::optional SetAllowedDeprecatedFeaturesFromCLIArgs() { } fEnableGbtOldHashes = allowdeprecated.count("gbt_oldhashes") > 0; + fEnableDeprecationInfoDeprecationHeight = allowdeprecated.count("deprecationinfo_deprecationheight") > 0; + fEnableAddrTypeField = allowdeprecated.count("addrtype") > 0; #ifdef ENABLE_WALLET fEnableLegacyPrivacyStrategy = allowdeprecated.count("legacy_privacy") > 0; fEnableGetNewAddress = allowdeprecated.count("getnewaddress") > 0; @@ -101,7 +108,6 @@ std::optional SetAllowedDeprecatedFeaturesFromCLIArgs() { fEnableZGetBalance = allowdeprecated.count("z_getbalance") > 0; fEnableZGetTotalBalance = allowdeprecated.count("z_gettotalbalance") > 0; fEnableZListAddresses = allowdeprecated.count("z_listaddresses") > 0; - fEnableAddrTypeField = allowdeprecated.count("addrtype") > 0; fEnableWalletTxVJoinSplit = allowdeprecated.count("wallettxvjoinsplit") > 0; #endif diff --git a/depend/zcash/src/deprecation.h b/depend/zcash/src/deprecation.h index 565cca66c..b03fe072a 100644 --- a/depend/zcash/src/deprecation.h +++ b/depend/zcash/src/deprecation.h @@ -5,12 +5,15 @@ #ifndef ZCASH_DEPRECATION_H #define ZCASH_DEPRECATION_H +#include "chainparams.h" #include "consensus/params.h" +#include "util/time.h" + // Deprecation policy: // Per https://zips.z.cash/zip-0200 // Shut down nodes running this version of code, 16 weeks' worth of blocks after the estimated // release block height. A warning is shown during the 14 days' worth of blocks prior to shut down. -static const int APPROX_RELEASE_HEIGHT = 1977500; +static const int APPROX_RELEASE_HEIGHT = 2067000; static const int RELEASE_TO_DEPRECATION_WEEKS = 16; static const int EXPECTED_BLOCKS_PER_HOUR = 3600 / Consensus::POST_BLOSSOM_POW_TARGET_SPACING; static_assert(EXPECTED_BLOCKS_PER_HOUR == 48, "The value of Consensus::POST_BLOSSOM_POW_TARGET_SPACING was chosen such that this assertion holds."); @@ -24,6 +27,7 @@ static const int DEPRECATION_WARN_LIMIT = 14 * 24 * EXPECTED_BLOCKS_PER_HOUR; static const std::set DEFAULT_ALLOW_DEPRECATED{{ // Node-level features "gbt_oldhashes", + "deprecationinfo_deprecationheight", // Wallet-level features #ifdef ENABLE_WALLET @@ -48,6 +52,8 @@ static const std::set DEFAULT_DENY_DEPRECATED{{ // Flags that enable deprecated functionality. extern bool fEnableGbtOldHashes; +extern bool fEnableDeprecationInfoDeprecationHeight; +extern bool fEnableAddrTypeField; #ifdef ENABLE_WALLET extern bool fEnableGetNewAddress; extern bool fEnableGetRawChangeAddress; @@ -56,10 +62,15 @@ extern bool fEnableZGetBalance; extern bool fEnableZGetTotalBalance; extern bool fEnableZListAddresses; extern bool fEnableLegacyPrivacyStrategy; -extern bool fEnableAddrTypeField; extern bool fEnableWalletTxVJoinSplit; #endif +/** + * Returns the estimated time, in seconds since the epoch, at which deprecation + * enforcement will take effect for this node. + */ +int64_t EstimatedNodeDeprecationTime(const CClock& clock, int nHeight); + /** * Checks whether the node is deprecated based on the current block height, and * shuts down the node with an error if so (and deprecation is not disabled for @@ -68,16 +79,16 @@ extern bool fEnableWalletTxVJoinSplit; * * fThread means run -alertnotify in a free-running thread. */ -void EnforceNodeDeprecation(int nHeight, bool forceLogging=false, bool fThread=true); +void EnforceNodeDeprecation(const CChainParams& params, int nHeight, bool forceLogging=false, bool fThread=true); /** - * Checks command-line arguments for enabling and/or disabling of deprecated + * Checks config options for enabling and/or disabling of deprecated * features and sets flags that enable deprecated features accordingly. * * @return std::nullopt if successful, or an error message indicating what * values are permitted for `-allowdeprecated`. */ -std::optional SetAllowedDeprecatedFeaturesFromCLIArgs(); +std::optional LoadAllowedDeprecatedFeatures(); /** * Returns a comma-separated list of the valid arguments to the -allowdeprecated diff --git a/depend/zcash/src/gtest/main.cpp b/depend/zcash/src/gtest/main.cpp index a35a99c8f..83c4ed654 100644 --- a/depend/zcash/src/gtest/main.cpp +++ b/depend/zcash/src/gtest/main.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include diff --git a/depend/zcash/src/gtest/test_checktransaction.cpp b/depend/zcash/src/gtest/test_checktransaction.cpp index 928080fa6..8a3c3d58a 100644 --- a/depend/zcash/src/gtest/test_checktransaction.cpp +++ b/depend/zcash/src/gtest/test_checktransaction.cpp @@ -6,12 +6,13 @@ #include "consensus/validation.h" #include "transaction_builder.h" #include "gtest/utils.h" +#include "test/test_util.h" #include "util/test.h" #include "zcash/JoinSplit.hpp" #include +#include #include -#include #include // Subclass of CTransaction which doesn't call UpdateHash when constructing @@ -106,8 +107,8 @@ CMutableTransaction GetValidTransaction(uint32_t consensusBranchId=SPROUT_BRANCH void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranchId) { // Generate an ephemeral keypair. - Ed25519SigningKey joinSplitPrivKey; - ed25519_generate_keypair(&joinSplitPrivKey, &mtx.joinSplitPubKey); + ed25519::SigningKey joinSplitPrivKey; + ed25519::generate_keypair(joinSplitPrivKey, mtx.joinSplitPubKey); // Compute the correct hSig. // TODO: #966. @@ -125,10 +126,10 @@ void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranch } // Add the signature - assert(ed25519_sign( - &joinSplitPrivKey, - dataToBeSigned.begin(), 32, - &mtx.joinSplitSig)); + ed25519::sign( + joinSplitPrivKey, + {dataToBeSigned.begin(), 32}, + mtx.joinSplitSig); } TEST(ChecktransactionTests, ValidTransaction) { @@ -536,7 +537,7 @@ TEST(ContextualCheckShieldedInputsTest, BadTxnsInvalidJoinsplitSignature) { SelectParams(CBaseChainParams::REGTEST); auto consensus = Params().GetConsensus(); std::optional> saplingAuth = std::nullopt; - auto orchardAuth = orchard::AuthValidator::Disabled(); + std::optional> orchardAuth = std::nullopt; CMutableTransaction mtx = GetValidTransaction(); mtx.joinSplitSig.bytes[0] += 1; @@ -568,7 +569,7 @@ TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) { SelectParams(CBaseChainParams::REGTEST); auto consensus = Params().GetConsensus(); std::optional> saplingAuth = std::nullopt; - auto orchardAuth = orchard::AuthValidator::Disabled(); + std::optional> orchardAuth = std::nullopt; auto saplingBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId; auto blossomBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_BLOSSOM].nBranchId; @@ -618,7 +619,7 @@ TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) { SelectParams(CBaseChainParams::REGTEST); auto consensus = Params().GetConsensus(); std::optional> saplingAuth = std::nullopt; - auto orchardAuth = orchard::AuthValidator::Disabled(); + std::optional> orchardAuth = std::nullopt; AssumeShieldedInputsExistAndAreSpendable baseView; CCoinsViewCache view(&baseView); @@ -855,7 +856,7 @@ TEST(ChecktransactionTests, SaplingSproutInputSumsTooLarge) { { // create JSDescription uint256 rt; - Ed25519VerificationKey joinSplitPubKey; + ed25519::VerificationKey joinSplitPubKey; std::array inputs = { libzcash::JSInput(), libzcash::JSInput() @@ -878,7 +879,7 @@ TEST(ChecktransactionTests, SaplingSproutInputSumsTooLarge) { mtx.vJoinSplit.push_back(jsdesc); } - mtx.vShieldedSpend.push_back(SpendDescription()); + mtx.vShieldedSpend.push_back(RandomInvalidSpendDescription()); mtx.vJoinSplit[0].vpub_new = (MAX_MONEY / 2) + 10; @@ -1150,7 +1151,7 @@ TEST(ChecktransactionTests, InvalidSaplingShieldedCoinbase) { // Make it an invalid shielded coinbase (no ciphertexts or commitments). mtx.vin.resize(1); mtx.vin[0].prevout.SetNull(); - mtx.vShieldedOutput.resize(1); + mtx.vShieldedOutput.push_back(RandomInvalidOutputDescription()); mtx.vJoinSplit.resize(0); CTransaction tx(mtx); @@ -1324,7 +1325,7 @@ TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) { EXPECT_TRUE(ContextualCheckTransaction(tx, state, chainparams, 10, 57)); std::optional> saplingAuth = sapling::init_batch_validator(false); - auto orchardAuth = orchard::AuthValidator::Disabled(); + std::optional> orchardAuth = std::nullopt; auto heartwoodBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_HEARTWOOD].nBranchId; // Coinbase transaction does not pass shielded input checks, as bindingSig diff --git a/depend/zcash/src/gtest/test_coins.cpp b/depend/zcash/src/gtest/test_coins.cpp index ae3b00d6c..239ea82a9 100644 --- a/depend/zcash/src/gtest/test_coins.cpp +++ b/depend/zcash/src/gtest/test_coins.cpp @@ -161,6 +161,14 @@ class CCoinsViewTest : public CCoinsView uint256 GetBestBlock() const { return hashBestBlock_; } + HistoryIndex GetHistoryLength(uint32_t epochId) const { return 0; } + HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const { + throw std::runtime_error("`GetHistoryAt` unimplemented for mock CCoinsViewTest"); + } + uint256 GetHistoryRoot(uint32_t epochId) const { + throw std::runtime_error("`GetHistoryRoot` unimplemented for mock CCoinsViewTest"); + } + void BatchWriteNullifiers(CNullifiersMap& mapNullifiers, std::map& cacheNullifiers) { for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end(); ) { @@ -282,7 +290,7 @@ class TxWithNullifiers JSDescription jsd; jsd.nullifiers[0] = sproutNullifier; mutableTx.vJoinSplit.emplace_back(jsd); - + saplingNullifier = GetRandHash(); SpendDescription sd; sd.nullifier = saplingNullifier; @@ -294,7 +302,7 @@ class TxWithNullifiers uint256 dataToBeSigned; auto builder = orchard::Builder(true, true, orchardAnchor); mutableTx.orchardBundle = builder.Build().value().ProveAndSign({}, dataToBeSigned).value(); - orchardNullifier = mutableTx.orchardBundle.GetNullifiers()[0]; + orchardNullifier = mutableTx.orchardBundle.GetNullifiers().at(0); tx = CTransaction(mutableTx); } @@ -338,19 +346,19 @@ template<> bool GetAnchorAt(const CCoinsViewCacheTest &cache, const uint256 &rt, void checkNullifierCache(const CCoinsViewCacheTest &cache, const TxWithNullifiers &txWithNullifiers, bool shouldBeInCache) { // Make sure the nullifiers have not gotten mixed up - EXPECT_TRUE(!cache.GetNullifier(txWithNullifiers.sproutNullifier, SAPLING)); - EXPECT_TRUE(!cache.GetNullifier(txWithNullifiers.sproutNullifier, ORCHARD)); - EXPECT_TRUE(!cache.GetNullifier(txWithNullifiers.saplingNullifier, SPROUT)); - EXPECT_TRUE(!cache.GetNullifier(txWithNullifiers.saplingNullifier, ORCHARD)); - EXPECT_TRUE(!cache.GetNullifier(txWithNullifiers.orchardNullifier, SPROUT)); - EXPECT_TRUE(!cache.GetNullifier(txWithNullifiers.orchardNullifier, SAPLING)); + EXPECT_FALSE(cache.GetNullifier(txWithNullifiers.sproutNullifier, SAPLING)); + EXPECT_FALSE(cache.GetNullifier(txWithNullifiers.sproutNullifier, ORCHARD)); + EXPECT_FALSE(cache.GetNullifier(txWithNullifiers.saplingNullifier, SPROUT)); + EXPECT_FALSE(cache.GetNullifier(txWithNullifiers.saplingNullifier, ORCHARD)); + EXPECT_FALSE(cache.GetNullifier(txWithNullifiers.orchardNullifier, SPROUT)); + EXPECT_FALSE(cache.GetNullifier(txWithNullifiers.orchardNullifier, SAPLING)); // Check if the nullifiers either are or are not in the cache bool containsSproutNullifier = cache.GetNullifier(txWithNullifiers.sproutNullifier, SPROUT); bool containsSaplingNullifier = cache.GetNullifier(txWithNullifiers.saplingNullifier, SAPLING); bool containsOrchardNullifier = cache.GetNullifier(txWithNullifiers.orchardNullifier, ORCHARD); - EXPECT_TRUE(containsSproutNullifier == shouldBeInCache); - EXPECT_TRUE(containsSaplingNullifier == shouldBeInCache); - EXPECT_TRUE(containsOrchardNullifier == shouldBeInCache); + EXPECT_EQ(containsSproutNullifier, shouldBeInCache); + EXPECT_EQ(containsSaplingNullifier, shouldBeInCache); + EXPECT_EQ(containsOrchardNullifier, shouldBeInCache); } @@ -648,25 +656,41 @@ TEST(CoinsTests, AnchorRegression) TEST(CoinsTests, NullifiersTest) { + LoadProofParameters(); + CCoinsViewTest base; CCoinsViewCacheTest cache(&base); TxWithNullifiers txWithNullifiers; - checkNullifierCache(cache, txWithNullifiers, false); + { + SCOPED_TRACE("cache with unspent nullifiers"); + checkNullifierCache(cache, txWithNullifiers, false); + } cache.SetNullifiers(txWithNullifiers.tx, true); - checkNullifierCache(cache, txWithNullifiers, true); + { + SCOPED_TRACE("cache with spent nullifiers"); + checkNullifierCache(cache, txWithNullifiers, true); + } cache.Flush(); CCoinsViewCacheTest cache2(&base); - checkNullifierCache(cache2, txWithNullifiers, true); + { + SCOPED_TRACE("cache2 with spent nullifiers"); + checkNullifierCache(cache2, txWithNullifiers, true); + } cache2.SetNullifiers(txWithNullifiers.tx, false); - checkNullifierCache(cache2, txWithNullifiers, false); + { + SCOPED_TRACE("cache2 with unspent nullifiers"); + checkNullifierCache(cache2, txWithNullifiers, false); + } cache2.Flush(); CCoinsViewCacheTest cache3(&base); - - checkNullifierCache(cache3, txWithNullifiers, false); + { + SCOPED_TRACE("cache3 with unspent nullifiers"); + checkNullifierCache(cache3, txWithNullifiers, false); + } } @@ -685,7 +709,7 @@ template void anchorsFlushImpl(ShieldedType type) cache.PushAnchor(tree); cache.Flush(); } - + { CCoinsViewCacheTest cache(&base); Tree tree; @@ -775,7 +799,7 @@ template void anchorsTestImpl(ShieldedType type) { Tree test_tree2; GetAnchorAt(cache, newrt, test_tree2); - + EXPECT_TRUE(test_tree2.root() == newrt); } diff --git a/depend/zcash/src/gtest/test_consensus.cpp b/depend/zcash/src/gtest/test_consensus.cpp index 4d45b990c..1048e44d9 100644 --- a/depend/zcash/src/gtest/test_consensus.cpp +++ b/depend/zcash/src/gtest/test_consensus.cpp @@ -14,11 +14,11 @@ void TestLibsodiumEd25519SignatureVerification( { SCOPED_TRACE(scope); - Ed25519VerificationKey vk; - std::copy(pubkey.begin(), pubkey.end(), vk.bytes); + ed25519::VerificationKey vk; + std::copy(pubkey.begin(), pubkey.end(), vk.bytes.begin()); - Ed25519Signature signature; - std::copy(sig.begin(), sig.end(), signature.bytes); + ed25519::Signature signature; + std::copy(sig.begin(), sig.end(), signature.bytes.begin()); EXPECT_EQ( crypto_sign_verify_detached( @@ -28,7 +28,7 @@ void TestLibsodiumEd25519SignatureVerification( 0); EXPECT_EQ( - ed25519_verify(&vk, &signature, (const unsigned char*)msg.data(), msg.size()), + ed25519::verify(vk, signature, {(const unsigned char*)msg.data(), msg.size()}), true); } @@ -42,11 +42,11 @@ void ZIP215Check( std::vector pubkey_hex = ParseHex(pubkey); std::vector sig_hex = ParseHex(sig); - Ed25519VerificationKey vk; - std::copy(pubkey_hex.begin(), pubkey_hex.end(), vk.bytes); + ed25519::VerificationKey vk; + std::copy(pubkey_hex.begin(), pubkey_hex.end(), vk.bytes.begin()); - Ed25519Signature signature; - std::copy(sig_hex.begin(), sig_hex.end(), signature.bytes); + ed25519::Signature signature; + std::copy(sig_hex.begin(), sig_hex.end(), signature.bytes.begin()); std::string msg("Zcash"); @@ -64,7 +64,7 @@ void ZIP215Check( expected_legacy); EXPECT_EQ( - ed25519_verify(&vk, &signature, (const unsigned char*)msg.data(), msg.size()), + ed25519::verify(vk, signature, {(const unsigned char*)msg.data(), msg.size()}), valid_zip215); } diff --git a/depend/zcash/src/gtest/test_deprecation.cpp b/depend/zcash/src/gtest/test_deprecation.cpp index fe8e1e25f..4c1ccd3bf 100644 --- a/depend/zcash/src/gtest/test_deprecation.cpp +++ b/depend/zcash/src/gtest/test_deprecation.cpp @@ -39,7 +39,7 @@ class DeprecationTest : public ::testing::Test { uiInterface.ThreadSafeMessageBox.disconnect_all_slots(); uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, &mock_, _1, _2, _3)); SelectParams(CBaseChainParams::MAIN); - + } void TearDown() override { @@ -64,61 +64,61 @@ class DeprecationTest : public ::testing::Test { TEST_F(DeprecationTest, NonDeprecatedNodeKeepsRunning) { EXPECT_FALSE(ShutdownRequested()); - EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT - 1); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT - 1); EXPECT_FALSE(ShutdownRequested()); } TEST_F(DeprecationTest, NodeNearDeprecationIsWarned) { EXPECT_FALSE(ShutdownRequested()); EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING)); - EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT); EXPECT_FALSE(ShutdownRequested()); } TEST_F(DeprecationTest, NodeNearDeprecationWarningIsNotDuplicated) { EXPECT_FALSE(ShutdownRequested()); - EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1); EXPECT_FALSE(ShutdownRequested()); } TEST_F(DeprecationTest, NodeNearDeprecationWarningIsRepeatedOnStartup) { EXPECT_FALSE(ShutdownRequested()); EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING)); - EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1, true); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1, true); EXPECT_FALSE(ShutdownRequested()); } TEST_F(DeprecationTest, DeprecatedNodeShutsDown) { EXPECT_FALSE(ShutdownRequested()); EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR)); - EnforceNodeDeprecation(DEPRECATION_HEIGHT); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT); EXPECT_TRUE(ShutdownRequested()); } TEST_F(DeprecationTest, DeprecatedNodeErrorIsNotDuplicated) { EXPECT_FALSE(ShutdownRequested()); - EnforceNodeDeprecation(DEPRECATION_HEIGHT + 1); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT + 1); EXPECT_TRUE(ShutdownRequested()); } TEST_F(DeprecationTest, DeprecatedNodeErrorIsRepeatedOnStartup) { EXPECT_FALSE(ShutdownRequested()); EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR)); - EnforceNodeDeprecation(DEPRECATION_HEIGHT + 1, true); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT + 1, true); EXPECT_TRUE(ShutdownRequested()); } TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnRegtest) { SelectParams(CBaseChainParams::REGTEST); EXPECT_FALSE(ShutdownRequested()); - EnforceNodeDeprecation(DEPRECATION_HEIGHT+1); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT+1); EXPECT_FALSE(ShutdownRequested()); } TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnTestnet) { SelectParams(CBaseChainParams::TESTNET); EXPECT_FALSE(ShutdownRequested()); - EnforceNodeDeprecation(DEPRECATION_HEIGHT+1); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT+1); EXPECT_FALSE(ShutdownRequested()); } @@ -129,7 +129,7 @@ TEST_F(DeprecationTest, AlertNotify) { mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string(); EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING)); - EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT, false, false); + EnforceNodeDeprecation(Params(), DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT, false, false); std::vector r = read_lines(temp); EXPECT_EQ(r.size(), 1u); diff --git a/depend/zcash/src/gtest/test_history.cpp b/depend/zcash/src/gtest/test_history.cpp index 2f00cfa5d..fbc8dd06c 100644 --- a/depend/zcash/src/gtest/test_history.cpp +++ b/depend/zcash/src/gtest/test_history.cpp @@ -4,65 +4,6 @@ #include "util/test.h" #include "zcash/History.hpp" -// Fake an empty view -class FakeCoinsViewDB : public CCoinsView { -public: - FakeCoinsViewDB() {} - - bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { - return false; - } - - bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { - return false; - } - - bool GetNullifier(const uint256 &nf, ShieldedType type) const { - return false; - } - - bool GetCoins(const uint256 &txid, CCoins &coins) const { - return false; - } - - bool HaveCoins(const uint256 &txid) const { - return false; - } - - uint256 GetBestBlock() const { - uint256 a; - return a; - } - - uint256 GetBestAnchor(ShieldedType type) const { - uint256 a; - return a; - } - - bool BatchWrite(CCoinsMap &mapCoins, - const uint256 &hashBlock, - const uint256 &hashSproutAnchor, - const uint256 &hashSaplingAnchor, - CAnchorsSproutMap &mapSproutAnchors, - CAnchorsSaplingMap &mapSaplingAnchors, - CNullifiersMap &mapSproutNullifiers, - CNullifiersMap saplingNullifiersMap) { - return false; - } - - bool GetStats(CCoinsStats &stats) const { - return false; - } - - HistoryIndex GetHistoryLength(uint32_t branchId) const { - return 0; - } - - HistoryNode GetHistoryAt(uint32_t branchId, HistoryIndex index) const { - return HistoryNode(); - } -}; - HistoryNode getLeafN(uint64_t block_num) { HistoryNode node = libzcash::NewV1Leaf( uint256(), @@ -78,7 +19,7 @@ HistoryNode getLeafN(uint64_t block_num) { TEST(History, Smoky) { // Fake an empty view - FakeCoinsViewDB fakeDB; + CCoinsViewDummy fakeDB; CCoinsViewCache view(&fakeDB); uint32_t epochId = 0; @@ -116,7 +57,7 @@ TEST(History, Smoky) { TEST(History, EpochBoundaries) { // Fake an empty view - FakeCoinsViewDB fakeDB; + CCoinsViewDummy fakeDB; CCoinsViewCache view(&fakeDB); // Test with the Heartwood and Canopy epochs @@ -183,7 +124,7 @@ TEST(History, EpochBoundaries) { TEST(History, GarbageMemoryHash) { const auto consensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_HEARTWOOD].nBranchId; - FakeCoinsViewDB fakeDB; + CCoinsViewDummy fakeDB; CCoinsViewCache view(&fakeDB); // Hash two history nodes @@ -196,7 +137,7 @@ TEST(History, GarbageMemoryHash) { uint256 historyRoot = view.GetHistoryRoot(consensusBranchId); // Change garbage memory and re-hash nodes - FakeCoinsViewDB fakeDBGarbage; + CCoinsViewDummy fakeDBGarbage; CCoinsViewCache viewGarbage(&fakeDBGarbage); HistoryNode node0Garbage = getLeafN(1); diff --git a/depend/zcash/src/gtest/test_joinsplit.cpp b/depend/zcash/src/gtest/test_joinsplit.cpp index 2b7011d2f..3fb4ed113 100644 --- a/depend/zcash/src/gtest/test_joinsplit.cpp +++ b/depend/zcash/src/gtest/test_joinsplit.cpp @@ -20,7 +20,7 @@ #include -#include +#include using namespace libzcash; @@ -29,7 +29,7 @@ using namespace libzcash; JSDescription makeSproutProof( std::array& inputs, std::array& outputs, - const Ed25519VerificationKey& joinSplitPubKey, + const ed25519::VerificationKey& joinSplitPubKey, uint64_t vpub_old, uint64_t vpub_new, const uint256& rt @@ -39,7 +39,7 @@ JSDescription makeSproutProof( bool verifySproutProof( const JSDescription& jsdesc, - const Ed25519VerificationKey& joinSplitPubKey + const ed25519::VerificationKey& joinSplitPubKey ) { auto verifier = ProofVerifier::Strict(); @@ -62,8 +62,8 @@ void test_full_api() // Set up a JoinSplit description uint64_t vpub_old = 10; uint64_t vpub_new = 0; - Ed25519VerificationKey joinSplitPubKey; - GetRandBytes(joinSplitPubKey.bytes, ED25519_VERIFICATION_KEY_LEN); + ed25519::VerificationKey joinSplitPubKey; + GetRandBytes(joinSplitPubKey.bytes.data(), joinSplitPubKey.bytes.size()); uint256 rt = tree.root(); JSDescription jsdesc; @@ -122,8 +122,8 @@ void test_full_api() vpub_old = 0; vpub_new = 1; rt = tree.root(); - Ed25519VerificationKey joinSplitPubKey2; - GetRandBytes(joinSplitPubKey2.bytes, ED25519_VERIFICATION_KEY_LEN); + ed25519::VerificationKey joinSplitPubKey2; + GetRandBytes(joinSplitPubKey2.bytes.data(), joinSplitPubKey2.bytes.size()); { std::array inputs = { @@ -171,8 +171,8 @@ void invokeAPI( ) { uint256 ephemeralKey; uint256 randomSeed; - Ed25519VerificationKey joinSplitPubKey; - GetRandBytes(joinSplitPubKey.bytes, ED25519_VERIFICATION_KEY_LEN); + ed25519::VerificationKey joinSplitPubKey; + GetRandBytes(joinSplitPubKey.bytes.data(), joinSplitPubKey.bytes.size()); std::array macs; std::array nullifiers; std::array commitments; @@ -283,9 +283,9 @@ for test_input in TEST_VECTORS: }; for (std::vector& v : tests) { - Ed25519VerificationKey joinSplitPubKey; + ed25519::VerificationKey joinSplitPubKey; auto pubKeyBytes = uint256S(v[3]); - std::copy(pubKeyBytes.begin(), pubKeyBytes.end(), joinSplitPubKey.bytes); + std::copy(pubKeyBytes.begin(), pubKeyBytes.end(), joinSplitPubKey.bytes.begin()); auto expected = ZCJoinSplit::h_sig( uint256S(v[0]), {uint256S(v[1]), uint256S(v[2])}, @@ -617,7 +617,7 @@ TEST(Joinsplit, BasicJoinsplitVerification) auto witness = merkleTree.witness(); // create JSDescription - Ed25519VerificationKey joinSplitPubKey; + ed25519::VerificationKey joinSplitPubKey; std::array inputs = { libzcash::JSInput(witness, note, k), libzcash::JSInput() // dummy input of zero value diff --git a/depend/zcash/src/gtest/test_keystore.cpp b/depend/zcash/src/gtest/test_keystore.cpp index 4d9cfbee3..8af6b39d3 100644 --- a/depend/zcash/src/gtest/test_keystore.cpp +++ b/depend/zcash/src/gtest/test_keystore.cpp @@ -564,7 +564,7 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) { auto ufvkmetaUnadded = keyStore.GetUFVKMetadataForReceiver(saplingReceiver); EXPECT_TRUE(ufvkmetaUnadded.has_value()); EXPECT_EQ(ufvkmetaUnadded.value().GetUFVKId(), ufvkid); - EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex().value(), addrPair.second); + EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex(), addrPair.second); // Adding the Sapling addr -> ivk map entry causes us to find the same UFVK, // and since we trial-decrypt with both external and internal IVKs to @@ -575,7 +575,7 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) { auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver); EXPECT_TRUE(ufvkmeta.has_value()); EXPECT_EQ(ufvkmeta.value().GetUFVKId(), ufvkid); - EXPECT_TRUE(ufvkmeta.value().GetDiversifierIndex().has_value()); + EXPECT_EQ(ufvkmeta.value().GetDiversifierIndex(), addrPair.second); } TEST(KeystoreTests, StoreAndRetrieveUFVKByOrchard) { @@ -604,7 +604,7 @@ TEST(KeystoreTests, StoreAndRetrieveUFVKByOrchard) { auto ufvkmetaUnadded = keyStore.GetUFVKMetadataForReceiver(orchardReceiver); EXPECT_TRUE(ufvkmetaUnadded.has_value()); EXPECT_EQ(ufvkmetaUnadded.value().GetUFVKId(), ufvkid); - EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex().value(), addrPair.second); + EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex(), addrPair.second); } TEST(KeystoreTests, AddTransparentReceiverForUnifiedAddress) { diff --git a/depend/zcash/src/gtest/test_mempool.cpp b/depend/zcash/src/gtest/test_mempool.cpp index 97531dbb1..2fe80c7f7 100644 --- a/depend/zcash/src/gtest/test_mempool.cpp +++ b/depend/zcash/src/gtest/test_mempool.cpp @@ -18,6 +18,7 @@ extern CMutableTransaction GetValidTransaction(uint32_t consensusBranchId=SPROUT class FakeCoinsViewDB : public CCoinsView { public: FakeCoinsViewDB() {} + ~FakeCoinsViewDB() {} bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return false; @@ -27,6 +28,10 @@ class FakeCoinsViewDB : public CCoinsView { return false; } + bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const { + return false; + } + bool GetNullifier(const uint256 &nf, ShieldedType type) const { return false; } @@ -56,14 +61,30 @@ class FakeCoinsViewDB : public CCoinsView { return a; } + HistoryIndex GetHistoryLength(uint32_t branchId) const { + return 0; + } + + HistoryNode GetHistoryAt(uint32_t branchId, HistoryIndex index) const { + return HistoryNode(); + } + + uint256 GetHistoryRoot(uint32_t epochId) const { + return uint256(); + } + bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const uint256 &hashSproutAnchor, const uint256 &hashSaplingAnchor, + const uint256 &hashOrchardAnchor, CAnchorsSproutMap &mapSproutAnchors, CAnchorsSaplingMap &mapSaplingAnchors, + CAnchorsOrchardMap &mapOrchardAnchors, CNullifiersMap &mapSproutNullifiers, - CNullifiersMap &mapSaplingNullifiers) { + CNullifiersMap &mapSaplingNullifiers, + CNullifiersMap &mapOrchardNullifiers, + CHistoryCacheMap &historyCacheMap) { return false; } @@ -92,14 +113,11 @@ TEST(Mempool, PriorityStatsDoNotCrash) { CAmount nFees = 0; int64_t nTime = 0x58e5fed9; unsigned int nHeight = 92045; - double dPriority = view.GetPriority(tx, nHeight); - CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, false, 0, SPROUT_BRANCH_ID); + CTxMemPoolEntry entry(tx, nFees, nTime, nHeight, true, false, 0, SPROUT_BRANCH_ID); // Check it does not crash (ie. the death test fails) EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), ""); - - EXPECT_EQ(dPriority, MAX_PRIORITY); } // Valid overwinter v3 format tx gets rejected because overwinter hasn't activated yet. diff --git a/depend/zcash/src/gtest/test_mempoollimit.cpp b/depend/zcash/src/gtest/test_mempoollimit.cpp index f13047759..fb08bb115 100644 --- a/depend/zcash/src/gtest/test_mempoollimit.cpp +++ b/depend/zcash/src/gtest/test_mempoollimit.cpp @@ -11,6 +11,8 @@ #include "util/time.h" #include "util/test.h" #include "transaction_builder.h" +#include "zip317.h" +#include "core_memusage.h" const uint256 TX_ID1 = ArithToUint256(1); @@ -80,36 +82,31 @@ TEST(MempoolLimitTests, RecentlyEvictedDropOneAtATime) EXPECT_FALSE(recentlyEvicted.contains(TX_ID3)); } -TEST(MempoolLimitTests, WeightedTxTreeCheckSizeAfterDropping) +TEST(MempoolLimitTests, MempoolLimitTxSetCheckSizeAfterDropping) { std::set testedDropping; // Run the test until we have tested dropping each of the elements int trialNum = 0; while (testedDropping.size() < 3) { - WeightedTxTree tree(MIN_TX_COST * 2); - EXPECT_EQ(0, tree.getTotalWeight().cost); - EXPECT_EQ(0, tree.getTotalWeight().evictionWeight); - tree.add(WeightedTxInfo(TX_ID1, TxWeight(MIN_TX_COST, MIN_TX_COST))); - EXPECT_EQ(4000, tree.getTotalWeight().cost); - EXPECT_EQ(4000, tree.getTotalWeight().evictionWeight); - tree.add(WeightedTxInfo(TX_ID2, TxWeight(MIN_TX_COST, MIN_TX_COST))); - EXPECT_EQ(8000, tree.getTotalWeight().cost); - EXPECT_EQ(8000, tree.getTotalWeight().evictionWeight); - EXPECT_FALSE(tree.maybeDropRandom().has_value()); - tree.add(WeightedTxInfo(TX_ID3, TxWeight(MIN_TX_COST, MIN_TX_COST + LOW_FEE_PENALTY))); - EXPECT_EQ(12000, tree.getTotalWeight().cost); - EXPECT_EQ(12000 + LOW_FEE_PENALTY, tree.getTotalWeight().evictionWeight); - std::optional drop = tree.maybeDropRandom(); + MempoolLimitTxSet limitSet(MIN_TX_COST * 2); + EXPECT_EQ(0, limitSet.getTotalWeight()); + limitSet.add(TX_ID1, MIN_TX_COST, MIN_TX_COST); + EXPECT_EQ(10000, limitSet.getTotalWeight()); + limitSet.add(TX_ID2, MIN_TX_COST, MIN_TX_COST); + EXPECT_EQ(20000, limitSet.getTotalWeight()); + EXPECT_FALSE(limitSet.maybeDropRandom().has_value()); + limitSet.add(TX_ID3, MIN_TX_COST, MIN_TX_COST + LOW_FEE_PENALTY); + EXPECT_EQ(30000 + LOW_FEE_PENALTY, limitSet.getTotalWeight()); + std::optional drop = limitSet.maybeDropRandom(); ASSERT_TRUE(drop.has_value()); uint256 txid = drop.value(); testedDropping.insert(txid); // Do not continue to test if a particular trial fails - ASSERT_EQ(8000, tree.getTotalWeight().cost); - ASSERT_EQ(txid == TX_ID3 ? 8000 : 8000 + LOW_FEE_PENALTY, tree.getTotalWeight().evictionWeight); + ASSERT_EQ(txid == TX_ID3 ? 20000 : 20000 + LOW_FEE_PENALTY, limitSet.getTotalWeight()); } } -TEST(MempoolLimitTests, WeightedTxInfoFromTx) +TEST(MempoolLimitTests, MempoolCostAndEvictionWeight) { LoadProofParameters(); @@ -118,7 +115,8 @@ TEST(MempoolLimitTests, WeightedTxInfoFromTx) auto consensusParams = RegtestActivateSapling(); auto sk = libzcash::SaplingSpendingKey::random(); - auto testNote = GetTestSaplingNote(sk.default_address(), 50000); + CAmount funds = 66000; + auto testNote = GetTestSaplingNote(sk.default_address(), funds); // Default fee { @@ -126,9 +124,9 @@ TEST(MempoolLimitTests, WeightedTxInfoFromTx) builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 25000, {}); - WeightedTxInfo info = WeightedTxInfo::from(builder.Build().GetTxOrThrow(), DEFAULT_FEE); - EXPECT_EQ(MIN_TX_COST, info.txWeight.cost); - EXPECT_EQ(MIN_TX_COST, info.txWeight.evictionWeight); + auto [cost, evictionWeight] = MempoolCostAndEvictionWeight(builder.Build().GetTxOrThrow(), MINIMUM_FEE); + EXPECT_EQ(MIN_TX_COST, cost); + EXPECT_EQ(MIN_TX_COST, evictionWeight); } // Lower than standard fee @@ -136,30 +134,46 @@ TEST(MempoolLimitTests, WeightedTxInfoFromTx) auto builder = TransactionBuilder(consensusParams, 1, std::nullopt); builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 25000, {}); - static_assert(DEFAULT_FEE == 1000); - builder.SetFee(DEFAULT_FEE-1); + static_assert(MINIMUM_FEE == 10000); + builder.SetFee(MINIMUM_FEE-1); - WeightedTxInfo info = WeightedTxInfo::from(builder.Build().GetTxOrThrow(), DEFAULT_FEE-1); - EXPECT_EQ(MIN_TX_COST, info.txWeight.cost); - EXPECT_EQ(MIN_TX_COST + LOW_FEE_PENALTY, info.txWeight.evictionWeight); + auto [cost, evictionWeight] = MempoolCostAndEvictionWeight(builder.Build().GetTxOrThrow(), MINIMUM_FEE-1); + EXPECT_EQ(MIN_TX_COST, cost); + EXPECT_EQ(MIN_TX_COST + LOW_FEE_PENALTY, evictionWeight); } // Larger Tx { auto builder = TransactionBuilder(consensusParams, 1, std::nullopt); builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness()); - builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {}); - builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {}); - builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {}); - builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {}); + for (int i = 0; i < 10; i++) { + builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 1000, {}); + } auto result = builder.Build(); if (result.IsError()) { std::cerr << result.GetError() << std::endl; } - WeightedTxInfo info = WeightedTxInfo::from(result.GetTxOrThrow(), DEFAULT_FEE); - EXPECT_EQ(5168, info.txWeight.cost); - EXPECT_EQ(5168, info.txWeight.evictionWeight); + // max(1 input, 10 outputs + 1 change output) => 11 logical actions. + CAmount zip317_fee = CalculateConventionalFee(11); + ASSERT_GT(funds, 1000*10 + zip317_fee); + const CTransaction tx {result.GetTxOrThrow()}; + EXPECT_EQ(11, tx.GetLogicalActionCount()); + + // For the test to be valid, we want the memory usage of this transaction to be more than MIN_TX_COST. + // Avoid hard-coding the usage because it might be platform-dependent. + ASSERT_GT(GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), MIN_TX_COST); + size_t tx_usage = RecursiveDynamicUsage(tx); + EXPECT_GT(tx_usage, MIN_TX_COST); + + auto [cost, evictionWeight] = MempoolCostAndEvictionWeight(tx, zip317_fee); + EXPECT_EQ(tx_usage, cost); + EXPECT_EQ(tx_usage, evictionWeight); + + // If we pay less than the conventional fee for 11 actions, we should incur a low fee penalty. + auto [cost2, evictionWeight2] = MempoolCostAndEvictionWeight(tx, zip317_fee-1); + EXPECT_EQ(tx_usage, cost2); + EXPECT_EQ(tx_usage + LOW_FEE_PENALTY, evictionWeight2); } RegtestDeactivateSapling(); diff --git a/depend/zcash/src/gtest/test_noteencryption.cpp b/depend/zcash/src/gtest/test_noteencryption.cpp index dd8b37f7e..0824ab87a 100644 --- a/depend/zcash/src/gtest/test_noteencryption.cpp +++ b/depend/zcash/src/gtest/test_noteencryption.cpp @@ -65,51 +65,6 @@ TEST(NoteEncryption, NotePlaintext) auto encryptor = enc.second; auto epk = encryptor.get_epk(); - // Try to decrypt with incorrect commitment - ASSERT_FALSE(SaplingNotePlaintext::decrypt( - params, - 1, - ct, - ivk, - epk, - uint256() - )); - - // Try to decrypt with correct commitment - auto foo = SaplingNotePlaintext::decrypt( - params, - 1, - ct, - ivk, - epk, - cmu - ); - - if (!foo) { - FAIL(); - } - - auto bar = foo.value(); - - ASSERT_TRUE(bar.value() == pt.value()); - ASSERT_TRUE(bar.memo() == pt.memo()); - ASSERT_TRUE(bar.d == pt.d); - ASSERT_TRUE(bar.rcm() == pt.rcm()); - - auto foobar = bar.note(ivk); - - if (!foobar) { - FAIL(); - } - - auto new_note = foobar.value(); - - ASSERT_TRUE(note.value() == new_note.value()); - ASSERT_TRUE(note.d == new_note.d); - ASSERT_TRUE(note.pk_d == new_note.pk_d); - ASSERT_TRUE(note.rcm() == new_note.rcm()); - ASSERT_TRUE(note.cmu() == new_note.cmu()); - SaplingOutgoingPlaintext out_pt; out_pt.pk_d = note.pk_d; out_pt.esk = encryptor.get_esk(); @@ -156,7 +111,7 @@ TEST(NoteEncryption, NotePlaintext) ); // Test sender can decrypt the note ciphertext. - foo = SaplingNotePlaintext::decrypt( + auto foo = SaplingNotePlaintext::decrypt( params, 1, ct, @@ -170,7 +125,7 @@ TEST(NoteEncryption, NotePlaintext) FAIL(); } - bar = foo.value(); + auto bar = foo.value(); ASSERT_TRUE(bar.value() == pt.value()); ASSERT_TRUE(bar.memo() == pt.memo()); @@ -181,240 +136,6 @@ TEST(NoteEncryption, NotePlaintext) } } -TEST(NoteEncryption, RejectsInvalidNoteZip212Enabled) -{ - SelectParams(CBaseChainParams::REGTEST); - int overwinterActivationHeight = 5; - int saplingActivationHeight = 30; - int canopyActivationHeight = 70; - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, overwinterActivationHeight); - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, saplingActivationHeight); - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_CANOPY, canopyActivationHeight); - const Consensus::Params& params = Params().GetConsensus(); - - using namespace libzcash; - auto xsk = SaplingSpendingKey(uint256()).expanded_spending_key(); - auto fvk = xsk.full_viewing_key(); - auto ivk = fvk.in_viewing_key(); - SaplingPaymentAddress addr = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); - - std::array memo; - for (size_t i = 0; i < ZC_MEMO_SIZE; i++) { - // Fill the message with dummy data - memo[i] = (unsigned char) i; - } - - { - // non-0x01 received before Canopy activation height - SaplingNote note(addr, 39393, Zip212Enabled::AfterZip212); - auto cmu_opt = note.cmu(); - if (!cmu_opt) { - FAIL(); - } - uint256 cmu = cmu_opt.value(); - SaplingNotePlaintext pt(note, memo); - - auto res = pt.encrypt(addr.pk_d); - if (!res) { - FAIL(); - } - - auto enc = res.value(); - - auto ct = enc.first; - auto encryptor = enc.second; - auto epk = encryptor.get_epk(); - - ASSERT_FALSE(SaplingNotePlaintext::decrypt( - params, - canopyActivationHeight - 1, - ct, - ivk, - epk, - cmu - )); - } - - { - // non-0x02 received past (Canopy activation height + grace period) - SaplingNote note(addr, 39393, Zip212Enabled::BeforeZip212); - auto cmu_opt = note.cmu(); - if (!cmu_opt) { - FAIL(); - } - uint256 cmu = cmu_opt.value(); - SaplingNotePlaintext pt(note, memo); - - auto res = pt.encrypt(addr.pk_d); - if (!res) { - FAIL(); - } - - auto enc = res.value(); - - auto ct = enc.first; - auto encryptor = enc.second; - auto epk = encryptor.get_epk(); - - ASSERT_FALSE(SaplingNotePlaintext::decrypt( - params, - canopyActivationHeight + ZIP212_GRACE_PERIOD, - ct, - ivk, - epk, - cmu - )); - } - - // Revert to test default - RegtestDeactivateCanopy(); - RegtestDeactivateHeartwood(); - RegtestDeactivateSapling(); -} - -TEST(NoteEncryption, AcceptsValidNoteZip212Enabled) -{ - SelectParams(CBaseChainParams::REGTEST); - int overwinterActivationHeight = 5; - int saplingActivationHeight = 30; - int canopyActivationHeight = 70; - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, overwinterActivationHeight); - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, saplingActivationHeight); - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_CANOPY, canopyActivationHeight); - const Consensus::Params& params = Params().GetConsensus(); - - using namespace libzcash; - auto xsk = SaplingSpendingKey(uint256()).expanded_spending_key(); - auto fvk = xsk.full_viewing_key(); - auto ivk = fvk.in_viewing_key(); - SaplingPaymentAddress addr = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); - - std::array memo; - for (size_t i = 0; i < ZC_MEMO_SIZE; i++) { - // Fill the message with dummy data - memo[i] = (unsigned char) i; - } - - { - // 0x01 received before Canopy activation height - SaplingNote note(addr, 39393, Zip212Enabled::BeforeZip212); - auto cmu_opt = note.cmu(); - if (!cmu_opt) { - FAIL(); - } - uint256 cmu = cmu_opt.value(); - SaplingNotePlaintext pt(note, memo); - - auto res = pt.encrypt(addr.pk_d); - if (!res) { - FAIL(); - } - - auto enc = res.value(); - - auto ct = enc.first; - auto encryptor = enc.second; - auto epk = encryptor.get_epk(); - - auto plaintext = SaplingNotePlaintext::decrypt( - params, - canopyActivationHeight - 1, - ct, - ivk, - epk, - cmu - ); - - if (!plaintext) { - FAIL(); - } - } - - { - // {0x01,0x02} received after Canopy activation and before grace period has elapsed - std::vector zip_212_enabled = {libzcash::Zip212Enabled::BeforeZip212, libzcash::Zip212Enabled::AfterZip212}; - int height1 = canopyActivationHeight; - int height2 = canopyActivationHeight + (ZIP212_GRACE_PERIOD) - 1; - int heights[] = {height1, height2}; - - for (int i = 0; i < zip_212_enabled.size(); i++) { - for (int j = 0; j < sizeof(heights) / sizeof(int); j++) { - SaplingNote note(addr, 39393, zip_212_enabled[i]); - auto cmu_opt = note.cmu(); - if (!cmu_opt) { - FAIL(); - } - uint256 cmu = cmu_opt.value(); - SaplingNotePlaintext pt(note, memo); - - auto res = pt.encrypt(addr.pk_d); - if (!res) { - FAIL(); - } - - auto enc = res.value(); - - auto ct = enc.first; - auto encryptor = enc.second; - auto epk = encryptor.get_epk(); - - auto plaintext = SaplingNotePlaintext::decrypt( - params, - heights[j], - ct, - ivk, - epk, - cmu - ); - - if (!plaintext) { - FAIL(); - } - } - } - } - - { - // 0x02 received past (Canopy activation height + grace period) - SaplingNote note(addr, 39393, Zip212Enabled::AfterZip212); - auto cmu_opt = note.cmu(); - if (!cmu_opt) { - FAIL(); - } - uint256 cmu = cmu_opt.value(); - SaplingNotePlaintext pt(note, memo); - - auto res = pt.encrypt(addr.pk_d); - if (!res) { - FAIL(); - } - - auto enc = res.value(); - - auto ct = enc.first; - auto encryptor = enc.second; - auto epk = encryptor.get_epk(); - - auto plaintext = SaplingNotePlaintext::decrypt( - params, - canopyActivationHeight + ZIP212_GRACE_PERIOD, - ct, - ivk, - epk, - cmu - ); - - if (!plaintext) { - FAIL(); - } - } - - // Revert to test default - RegtestDeactivateCanopy(); - RegtestDeactivateHeartwood(); - RegtestDeactivateSapling(); -} - TEST(NoteEncryption, SaplingApi) { using namespace libzcash; diff --git a/depend/zcash/src/gtest/test_transaction.cpp b/depend/zcash/src/gtest/test_transaction.cpp index 66fe58ea5..8f987b89f 100644 --- a/depend/zcash/src/gtest/test_transaction.cpp +++ b/depend/zcash/src/gtest/test_transaction.cpp @@ -8,7 +8,7 @@ #include -#include +#include TEST(Transaction, JSDescriptionRandomized) { // construct a merkle tree @@ -31,7 +31,7 @@ TEST(Transaction, JSDescriptionRandomized) { auto witness = merkleTree.witness(); // create JSDescription - Ed25519VerificationKey joinSplitPubKey; + ed25519::VerificationKey joinSplitPubKey; std::array inputs = { libzcash::JSInput(witness, note, k), libzcash::JSInput() // dummy input of zero value diff --git a/depend/zcash/src/gtest/test_transaction_builder.cpp b/depend/zcash/src/gtest/test_transaction_builder.cpp index 975691550..15810f5ae 100644 --- a/depend/zcash/src/gtest/test_transaction_builder.cpp +++ b/depend/zcash/src/gtest/test_transaction_builder.cpp @@ -241,8 +241,7 @@ TEST(TransactionBuilder, TransparentToOrchard) libzcash::diversifier_index_t j(0); auto recipient = ivk.Address(j); - TransactionBuilderCoinsViewDB fakeDB; - auto orchardAnchor = fakeDB.GetBestAnchor(ShieldedType::ORCHARD); + auto orchardAnchor = uint256(); // Create a shielding transaction from transparent to Orchard // 0.00005 t-ZEC in, 0.00004 z-ZEC out, default fee @@ -318,18 +317,18 @@ TEST(TransactionBuilder, FailsWithNegativeChange) // 0.00005 z-ZEC out, default fee auto builder = TransactionBuilder(consensusParams, 1, std::nullopt); builder.AddSaplingOutput(fvk.ovk, pa, 5000, {}); - EXPECT_EQ("Change cannot be negative", builder.Build().GetError()); + EXPECT_EQ("Change cannot be negative: -0.00006 ZEC", builder.Build().GetError()); // Fail if there is only a transparent output // 0.00005 t-ZEC out, default fee builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore); builder.AddTransparentOutput(taddr, 5000); - EXPECT_EQ("Change cannot be negative", builder.Build().GetError()); + EXPECT_EQ("Change cannot be negative: -0.00006 ZEC", builder.Build().GetError()); // Fails if there is insufficient input // 0.00005 t-ZEC out, default fee, 0.00005999 z-ZEC in builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); - EXPECT_EQ("Change cannot be negative", builder.Build().GetError()); + EXPECT_EQ("Change cannot be negative: -0.00000001 ZEC", builder.Build().GetError()); // Succeeds if there is sufficient input builder.AddTransparentInput(COutPoint(), scriptPubKey, 1); diff --git a/depend/zcash/src/gtest/test_transaction_builder.h b/depend/zcash/src/gtest/test_transaction_builder.h index 0c000b2c2..6e6a658e7 100644 --- a/depend/zcash/src/gtest/test_transaction_builder.h +++ b/depend/zcash/src/gtest/test_transaction_builder.h @@ -45,23 +45,33 @@ class TransactionBuilderCoinsViewDB : public CCoinsView { } uint256 GetBestBlock() const { - uint256 a; - return a; + throw std::runtime_error("`GetBestBlock` unimplemented for mock TransactionBuilderCoinsViewDB"); } uint256 GetBestAnchor(ShieldedType type) const { - uint256 a; - return a; + throw std::runtime_error("`GetBestAnchor` unimplemented for mock TransactionBuilderCoinsViewDB"); + } + + HistoryIndex GetHistoryLength(uint32_t epochId) const { return 0; } + HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const { + throw std::runtime_error("`GetHistoryAt` unimplemented for mock TransactionBuilderCoinsViewDB"); + } + uint256 GetHistoryRoot(uint32_t epochId) const { + throw std::runtime_error("`GetHistoryRoot` unimplemented for mock TransactionBuilderCoinsViewDB"); } bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const uint256 &hashSproutAnchor, const uint256 &hashSaplingAnchor, + const uint256 &hashOrchardAnchor, CAnchorsSproutMap &mapSproutAnchors, CAnchorsSaplingMap &mapSaplingAnchors, + CAnchorsOrchardMap &mapOrchardAnchors, CNullifiersMap &mapSproutNullifiers, - CNullifiersMap saplingNullifiersMap) { + CNullifiersMap &mapSaplingNullifiers, + CNullifiersMap &mapOrchardNullifiers, + CHistoryCacheMap &historyCacheMap) { return false; } diff --git a/depend/zcash/src/gtest/test_validation.cpp b/depend/zcash/src/gtest/test_validation.cpp index d878b5d83..eace1e11d 100644 --- a/depend/zcash/src/gtest/test_validation.cpp +++ b/depend/zcash/src/gtest/test_validation.cpp @@ -32,6 +32,7 @@ class ValidationFakeCoinsViewDB : public CCoinsView { ValidationFakeCoinsViewDB() {} ValidationFakeCoinsViewDB(uint256 blockHash, uint256 txid, CTxOut txOut, int nHeight) : coin(std::make_pair(std::make_pair(blockHash, txid), std::make_pair(txOut, nHeight))) {} + ~ValidationFakeCoinsViewDB() {} bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return false; @@ -41,6 +42,10 @@ class ValidationFakeCoinsViewDB : public CCoinsView { return false; } + bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &tree) const { + return false; + } + bool GetNullifier(const uint256 &nf, ShieldedType type) const { return false; } @@ -80,14 +85,30 @@ class ValidationFakeCoinsViewDB : public CCoinsView { return a; } + HistoryIndex GetHistoryLength(uint32_t branchId) const { + return 0; + } + + HistoryNode GetHistoryAt(uint32_t branchId, HistoryIndex index) const { + return HistoryNode(); + } + + uint256 GetHistoryRoot(uint32_t epochId) const { + return uint256(); + } + bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const uint256 &hashSproutAnchor, const uint256 &hashSaplingAnchor, + const uint256 &hashOrchardAnchor, CAnchorsSproutMap &mapSproutAnchors, CAnchorsSaplingMap &mapSaplingAnchors, + CAnchorsOrchardMap &mapOrchardAnchors, CNullifiersMap &mapSproutNullifiers, - CNullifiersMap saplingNullifiersMap) { + CNullifiersMap &mapSaplingNullifiers, + CNullifiersMap &mapOrchardNullifiers, + CHistoryCacheMap &historyCacheMap) { return false; } diff --git a/depend/zcash/src/gtest/test_weightedmap.cpp b/depend/zcash/src/gtest/test_weightedmap.cpp new file mode 100644 index 000000000..0d484d08f --- /dev/null +++ b/depend/zcash/src/gtest/test_weightedmap.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2019-2023 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include + +#include "weighted_map.h" +#include "gtest/utils.h" +#include "util/test.h" + + +TEST(WeightedMapTests, WeightedMap) +{ + WeightedMap m; + + EXPECT_EQ(0, m.size()); + EXPECT_TRUE(m.empty()); + EXPECT_EQ(0, m.getTotalWeight()); + m.checkInvariants(); + + EXPECT_TRUE(m.add(3, 30, 3)); + EXPECT_EQ(1, m.size()); + EXPECT_FALSE(m.empty()); + EXPECT_EQ(3, m.getTotalWeight()); + m.checkInvariants(); + + EXPECT_TRUE(m.add(1, 10, 2)); + EXPECT_EQ(2, m.size()); + EXPECT_FALSE(m.empty()); + EXPECT_EQ(5, m.getTotalWeight()); + m.checkInvariants(); + + // adding a duplicate element should be ignored + EXPECT_FALSE(m.add(1, 15, 64)); + EXPECT_EQ(2, m.size()); + EXPECT_FALSE(m.empty()); + EXPECT_EQ(5, m.getTotalWeight()); + m.checkInvariants(); + + EXPECT_TRUE(m.add(2, 20, 1)); + EXPECT_EQ(3, m.size()); + EXPECT_FALSE(m.empty()); + EXPECT_EQ(6, m.getTotalWeight()); + m.checkInvariants(); + + // regression test: adding three elements and deleting the first caused an invariant violation (not in committed code) + EXPECT_EQ(30, m.remove(3).value()); + EXPECT_EQ(2, m.size()); + EXPECT_FALSE(m.empty()); + EXPECT_EQ(3, m.getTotalWeight()); + m.checkInvariants(); + + // try to remove a non-existent element + EXPECT_FALSE(m.remove(42).has_value()); + EXPECT_EQ(2, m.size()); + EXPECT_FALSE(m.empty()); + EXPECT_EQ(3, m.getTotalWeight()); + m.checkInvariants(); + + EXPECT_EQ(20, m.remove(2).value()); + EXPECT_EQ(1, m.size()); + EXPECT_FALSE(m.empty()); + EXPECT_EQ(2, m.getTotalWeight()); + m.checkInvariants(); + + EXPECT_TRUE(m.add(2, 20, 1)); + EXPECT_EQ(2, m.size()); + EXPECT_FALSE(m.empty()); + EXPECT_EQ(3, m.getTotalWeight()); + m.checkInvariants(); + + // at this point the map should contain 1->10 (weight 2) and 2->20 (weight 1) + auto [e1, c1, w1] = m.takeRandom().value(); + EXPECT_TRUE(e1 == 1 || e1 == 2); + EXPECT_EQ(c1, e1*10); + EXPECT_EQ(w1, 3-e1); + EXPECT_EQ(1, m.size()); + EXPECT_FALSE(m.empty()); + EXPECT_EQ(3-w1, m.getTotalWeight()); + m.checkInvariants(); + + auto [e2, c2, w2] = m.takeRandom().value(); + EXPECT_EQ(3, e1 + e2); + EXPECT_EQ(c2, e2*10); + EXPECT_EQ(w2, 3-e2); + EXPECT_EQ(0, m.size()); + EXPECT_TRUE(m.empty()); + EXPECT_EQ(0, m.getTotalWeight()); + m.checkInvariants(); + + EXPECT_FALSE(m.takeRandom().has_value()); + EXPECT_EQ(0, m.size()); + EXPECT_TRUE(m.empty()); + EXPECT_EQ(0, m.getTotalWeight()); + m.checkInvariants(); +} + +TEST(WeightedMapTests, WeightedMapRandomOps) +{ + WeightedMap m; + std::map expected; // element -> weight + int total_weight = 0; + const int iterations = 1000; + const int element_range = 20; + const int max_weight = 10; + static_assert(iterations <= std::numeric_limits::max() / max_weight); // ensure total_weight cannot overflow + + EXPECT_EQ(0, m.size()); + EXPECT_TRUE(m.empty()); + EXPECT_EQ(0, m.getTotalWeight()); + m.checkInvariants(); + for (int i = 0; i < iterations; i++) { + switch (GetRandInt(4)) { + // probability of add should be balanced with (remove or takeRandom) + case 0: case 1: { + int e = GetRandInt(element_range); + int w = GetRandInt(max_weight) + 1; + bool added = m.add(e, e*10, w); + EXPECT_EQ(added, expected.count(e) == 0); + if (added) { + total_weight += w; + expected[e] = w; + } + break; + } + case 2: { + int e = GetRandInt(element_range); + auto c = m.remove(e); + if (expected.count(e) == 0) { + EXPECT_FALSE(c.has_value()); + } else { + ASSERT_TRUE(c.has_value()); + EXPECT_EQ(c.value(), e*10); + total_weight -= expected[e]; + expected.erase(e); + } + break; + } + case 3: { + auto r = m.takeRandom(); + if (expected.empty()) { + EXPECT_FALSE(r.has_value()); + } else { + ASSERT_TRUE(r.has_value()); + auto [e, c, w] = r.value(); + EXPECT_EQ(1, expected.count(e)); + EXPECT_EQ(c, e*10); + EXPECT_EQ(w, expected[e]); + total_weight -= expected[e]; + expected.erase(e); + } + break; + } + } + EXPECT_EQ(expected.size(), m.size()); + EXPECT_EQ(expected.empty(), m.empty()); + EXPECT_EQ(total_weight, m.getTotalWeight()); + m.checkInvariants(); + } +} diff --git a/depend/zcash/src/hash.h b/depend/zcash/src/hash.h index 3b689bae2..73865fd72 100644 --- a/depend/zcash/src/hash.h +++ b/depend/zcash/src/hash.h @@ -132,6 +132,11 @@ class CHashWriter int GetType() const { return nType; } int GetVersion() const { return nVersion; } + void write_u8(const unsigned char* pch, size_t nSize) + { + ctx.Write(pch, nSize); + } + void write(const char *pch, size_t size) { ctx.Write((const unsigned char*)pch, size); } diff --git a/depend/zcash/src/init.cpp b/depend/zcash/src/init.cpp index 23ac44fae..c2867b9a2 100644 --- a/depend/zcash/src/init.cpp +++ b/depend/zcash/src/init.cpp @@ -12,6 +12,7 @@ #include "addrman.h" #include "amount.h" #include "checkpoints.h" +#include "compat.h" #include "compat/sanity.h" #include "consensus/upgrades.h" #include "consensus/validation.h" @@ -21,7 +22,7 @@ #include "httpserver.h" #include "httprpc.h" #include "key.h" -#if defined(ENABLE_MINING) || defined(ENABLE_WALLET) +#ifdef ENABLE_MINING #include "key_io.h" #endif #include "main.h" @@ -46,6 +47,7 @@ #include "wallet/walletdb.h" #endif #include "warnings.h" +#include "zip317.h" #include #include #include @@ -67,7 +69,7 @@ #include "zmq/zmqnotificationinterface.h" #endif -#include +#include #include #include @@ -154,6 +156,8 @@ class CCoinsViewErrorCatcher : public CCoinsViewBacked { public: CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {} + ~CCoinsViewErrorCatcher() {} + bool GetCoins(const uint256 &txid, CCoins &coins) const { try { return CCoinsViewBacked::GetCoins(txid, coins); @@ -332,7 +336,7 @@ std::string HelpMessage(HelpMessageMode mode) // When adding new options to the categories, please keep and ensure alphabetical ordering. // Do not translate _(...) -help-debug options, Many technical terms, and only a very small audience, so is unnecessary stress to translators. - string strUsage = HelpMessageGroup(_("Options:")); + std::string strUsage = HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); strUsage += HelpMessageOpt("-version", _("Print version and exit")); strUsage += HelpMessageOpt("-alerts", strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS)); @@ -477,19 +481,17 @@ std::string HelpMessage(HelpMessageMode mode) { strUsage += HelpMessageOpt("-clockoffset=", "Applies offset of seconds to the actual time. Incompatible with -mocktime (default: 0)"); strUsage += HelpMessageOpt("-mocktime=", "Replace actual time with seconds since epoch. Incompatible with -clockoffset (default: 0)"); - strUsage += HelpMessageOpt("-limitfreerelay=", strprintf("Continuously rate-limit free transactions to *1000 bytes per minute (default: %u)", DEFAULT_LIMITFREERELAY)); - strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", DEFAULT_RELAYPRIORITY)); strUsage += HelpMessageOpt("-maxsigcachesize=", strprintf("Limit total size of signature and bundle caches to MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); strUsage += HelpMessageOpt("-maxtipage=", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE)); } - strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), + strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Transactions must have at least this fee rate (in %s per 1000 bytes) for relaying, mining and transaction creation (default: %s). This is not the only fee constraint."), CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE))); strUsage += HelpMessageOpt("-maxtxfee=", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE))); strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of the debug log")); if (showDebug) { - strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction priority and fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY)); + strUsage += HelpMessageOpt("-printpriority", strprintf("Log the modified fee, conventional fee, size, number of logical actions, and number of unpaid actions for each transaction when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY)); } // strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink the debug log on client startup (default: 1 when no -debug)")); @@ -500,9 +502,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY)); strUsage += HelpMessageGroup(_("Block creation options:")); - strUsage += HelpMessageOpt("-blockminsize=", strprintf(_("Set minimum block size in bytes (default: %u)"), DEFAULT_BLOCK_MIN_SIZE)); strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); - strUsage += HelpMessageOpt("-blockprioritysize=", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); + strUsage += HelpMessageOpt("-blockunpaidactionlimit=", strprintf(_("Set the limit on unpaid actions that will be accepted in a block for transactions paying less than the ZIP 317 fee (default: %d)"), DEFAULT_BLOCK_UNPAID_ACTION_LIMIT)); if (GetBoolArg("-help-debug", false)) strUsage += HelpMessageOpt("-blockversion=", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION)); @@ -1226,16 +1227,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (nConnectTimeout <= 0) nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; - // Fee-per-kilobyte amount considered the same as "free" - // If you are mining, be careful setting this: - // if you set it to zero then - // a transaction spammer can cheaply fill blocks using - // 1-satoshi-fee transactions. It should be set above the real + // Fee rate in zatoshis per 1000 bytes required for mempool acceptance and relay. + // TODO(update when ZIP 317 is implemented): + // If you are mining, be careful setting this. If you set it too low then a + // transaction spammer can cheaply fill blocks. It should be set above the real // cost to you of processing a transaction. if (mapArgs.count("-minrelaytxfee")) { CAmount n = 0; - if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0) + if (ParseMoney(mapArgs["-minrelaytxfee"], n)) ::minRelayTxFee = CFeeRate(n); else return InitError(strprintf(_("Invalid amount for -minrelaytxfee=: '%s'"), mapArgs["-minrelaytxfee"])); @@ -1280,9 +1280,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); - KeyIO keyIO(chainparams); #ifdef ENABLE_MINING if (mapArgs.count("-mineraddress")) { + KeyIO keyIO(chainparams); auto addr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]); auto consensus = chainparams.GetConsensus(); int height = consensus.HeightOfLatestSettledUpgrade(); @@ -1294,6 +1294,18 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #endif + if (GetArg("-blockminsize", 0) != 0) { + InitWarning(_("The argument -blockminsize is no longer supported.")); + } + + if (GetArg("-blockprioritysize", 0) != 0) { + InitWarning(_("The argument -blockprioritysize is no longer supported.")); + } + + if (GetArg("-blockunpaidactionlimit", 0) < 0) { + return InitError(_("-blockunpaidactionlimit cannot be configured with a negative value.")); + } + if (!mapMultiArgs["-nuparams"].empty()) { // Allow overriding network upgrade parameters for testing if (chainparams.NetworkIDString() != "regtest") { @@ -1883,11 +1895,16 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #ifdef ENABLE_WALLET bool minerAddressInLocalWallet = false; if (pwalletMain) { + KeyIO keyIO(chainparams); auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]); if (!zaddr.has_value()) { return InitError(_("-mineraddress is not a valid " PACKAGE_NAME " address.")); } - auto ztxoSelector = pwalletMain->ZTXOSelectorForAddress(zaddr.value(), true, false); + auto ztxoSelector = pwalletMain->ZTXOSelectorForAddress( + zaddr.value(), + true, + TransparentCoinbasePolicy::Allow, + false); minerAddressInLocalWallet = ztxoSelector.has_value(); } if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) { diff --git a/depend/zcash/src/int128.h b/depend/zcash/src/int128.h new file mode 100644 index 000000000..c887aa7cd --- /dev/null +++ b/depend/zcash/src/int128.h @@ -0,0 +1,25 @@ +// Copyright (c) 2023 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_INT128_H +#define ZCASH_INT128_H + +#include + +// will define INT128_MAX iff (in some future world) it provides int128_t. +// Otherwise use the __int128 extension which is supported in clang and gcc. +#ifndef INT128_MAX +typedef __int128 int128_t; +#define INT128_MAX (std::numeric_limits::max()) +#define INT128_MIN (std::numeric_limits::min()) +#endif + +// will define UINT128_MAX iff (in some future world) it provides uint128_t. +// Otherwise use the __uint128_t extension which is supported in clang and gcc. +#ifndef UINT128_MAX +typedef __uint128_t uint128_t; +#define UINT128_MAX (std::numeric_limits::max()) +#endif + +#endif // ZCASH_INT128_H diff --git a/depend/zcash/src/key_io.cpp b/depend/zcash/src/key_io.cpp index 33fa6a710..524028d2d 100644 --- a/depend/zcash/src/key_io.cpp +++ b/depend/zcash/src/key_io.cpp @@ -468,7 +468,7 @@ std::optional KeyIO::DecodePaymentAddress(const std::s } // Finally, try parsing as transparent - return std::visit(match { + return examine(DecodeDestination(str), match { [](const CKeyID& keyIdIn) { std::optional keyId = keyIdIn; return keyId; @@ -481,7 +481,7 @@ std::optional KeyIO::DecodePaymentAddress(const std::s std::optional result = std::nullopt; return result; } - }, DecodeDestination(str)); + }); } bool KeyIO::IsValidPaymentAddressString(const std::string& str) const diff --git a/depend/zcash/src/keystore.cpp b/depend/zcash/src/keystore.cpp index b7fe70c52..0e35c915b 100644 --- a/depend/zcash/src/keystore.cpp +++ b/depend/zcash/src/keystore.cpp @@ -15,6 +15,16 @@ bool CKeyStore::AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); } +std::optional CKeyStore::GetUFVKMetadataForAddress( + const CTxDestination& address) const +{ + auto self = this; + return examine(address, match { + [](const CNoDestination&) -> std::optional { return std::nullopt; }, + [&](const auto& addr) { return self->GetUFVKMetadataForReceiver(addr); } + }); +} + bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const { CKey key; @@ -390,12 +400,10 @@ CBasicKeyStore::GetUFVKMetadataForReceiver(const libzcash::Receiver& receiver) c return std::visit(FindUFVKId(*this), receiver); } -std::optional -CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr) const +std::optional +CBasicKeyStore::GetUFVKIdForAddress(const libzcash::UnifiedAddress& addr) const { std::optional ufvkId; - std::optional j; - bool jConflict = false; for (const auto& receiver : addr) { auto rmeta = GetUFVKMetadataForReceiver(receiver); if (rmeta.has_value()) { @@ -408,35 +416,19 @@ CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr) if (rmeta.value().GetUFVKId() != ufvkId.value()) { return std::nullopt; } - - if (rmeta.value().GetDiversifierIndex().has_value()) { - if (j.has_value()) { - if (rmeta.value().GetDiversifierIndex().value() != j.value()) { - jConflict = true; - j = std::nullopt; - } - } else if (!jConflict) { - j = rmeta.value().GetDiversifierIndex().value(); - } - } } else { ufvkId = rmeta.value().GetUFVKId(); - j = rmeta.value().GetDiversifierIndex(); } } } - if (ufvkId.has_value()) { - return AddressUFVKMetadata(ufvkId.value(), j, true); - } else { - return std::nullopt; - } + return ufvkId; } std::optional CBasicKeyStore::GetUFVKIdForViewingKey(const libzcash::ViewingKey& vk) const { std::optional result; - std::visit(match { + examine(vk, match { [&](const libzcash::SproutViewingKey& vk) {}, [&](const libzcash::SaplingExtendedFullViewingKey& extfvk) { const auto saplingIvk = extfvk.ToIncomingViewingKey(); @@ -464,7 +456,7 @@ std::optional CBasicKeyStore::GetUFVKIdForViewingKey(const lib } } } - }, vk); + }); return result; } diff --git a/depend/zcash/src/keystore.h b/depend/zcash/src/keystore.h index cf2321703..62bf3c0f6 100644 --- a/depend/zcash/src/keystore.h +++ b/depend/zcash/src/keystore.h @@ -22,14 +22,14 @@ class AddressUFVKMetadata { private: libzcash::UFVKId ufvkId; - std::optional j; + libzcash::diversifier_index_t j; bool externalAddress; public: - AddressUFVKMetadata(libzcash::UFVKId ufvkId, std::optional j, bool externalAddress) + AddressUFVKMetadata(libzcash::UFVKId ufvkId, libzcash::diversifier_index_t j, bool externalAddress) : ufvkId(ufvkId), j(j), externalAddress(externalAddress) {} libzcash::UFVKId GetUFVKId() const { return ufvkId; } - std::optional GetDiversifierIndex() const { return j; } + libzcash::diversifier_index_t GetDiversifierIndex() const { return j; } bool IsExternalAddress() const { return externalAddress; } }; @@ -145,12 +145,14 @@ class CKeyStore virtual std::optional GetUFVKMetadataForReceiver( const libzcash::Receiver& receiver) const = 0; + std::optional GetUFVKMetadataForAddress( + const CTxDestination& address) const; + /** * If all the receivers of the specified address correspond to a single - * UFVK, return that key's metadata. If all the receivers correspond to - * the same diversifier index, that diversifier index is also returned. + * UFVK, return that key's metadata. */ - virtual std::optional GetUFVKMetadataForAddress( + virtual std::optional GetUFVKIdForAddress( const libzcash::UnifiedAddress& addr) const = 0; virtual std::optional GetUFVKIdForViewingKey( @@ -405,14 +407,14 @@ class CBasicKeyStore : public CKeyStore } } - virtual std::optional GetUFVKMetadataForAddress( + virtual std::optional GetUFVKIdForAddress( const libzcash::UnifiedAddress& addr) const; std::optional GetUFVKForAddress( const libzcash::UnifiedAddress& addr) const { - auto ufvkMeta = GetUFVKMetadataForAddress(addr); - if (ufvkMeta.has_value()) { - return GetUnifiedFullViewingKey(ufvkMeta.value().GetUFVKId()); + auto ufvkId = GetUFVKIdForAddress(addr); + if (ufvkId.has_value()) { + return GetUnifiedFullViewingKey(ufvkId.value()); } else { return std::nullopt; } diff --git a/depend/zcash/src/main.cpp b/depend/zcash/src/main.cpp index cbcaa83c2..ec83157f5 100644 --- a/depend/zcash/src/main.cpp +++ b/depend/zcash/src/main.cpp @@ -27,6 +27,7 @@ #include "policy/policy.h" #include "pow.h" #include "reverse_iterator.h" +#include "time.h" #include "txmempool.h" #include "ui_interface.h" #include "undo.h" @@ -37,6 +38,7 @@ #include "wallet/asyncrpcoperation_shieldcoinbase.h" #include "warnings.h" +#include #include #include #include @@ -979,6 +981,37 @@ bool ContextualCheckTransaction( // https://zips.z.cash/zip-0213#specification uint256 ovk; for (const OutputDescription &output : tx.vShieldedOutput) { + bool zip_212_enabled; + libzcash::SaplingPaymentAddress zaddr; + CAmount value; + + // EoS height for 5.3.3 and 5.4.2 is 2121024 (mainnet). + // On testnet this height will be in the past, as of the 5.5.0 release. + if (nHeight >= 2121200) { + try { + auto decrypted = wallet::try_sapling_output_recovery( + *chainparams.RustNetwork(), + nHeight, + ovk.GetRawBytes(), + { + output.cv.GetRawBytes(), + output.cmu.GetRawBytes(), + output.ephemeralKey.GetRawBytes(), + output.encCiphertext, + output.outCiphertext, + }); + zip_212_enabled = decrypted->zip_212_enabled(); + + libzcash::SaplingNotePlaintext notePt; + std::tie(notePt, zaddr) = SaplingNotePlaintext::from_rust(std::move(decrypted)); + value = notePt.value(); + } catch (const rust::Error &e) { + return state.DoS( + DOS_LEVEL_BLOCK, + error("ContextualCheckTransaction(): failed to recover plaintext of coinbase output description"), + REJECT_INVALID, "bad-cb-output-desc-invalid-outct"); + } + } else { auto outPlaintext = SaplingOutgoingPlaintext::decrypt( output.outCiphertext, ovk, output.cv, output.cmu, output.ephemeralKey); if (!outPlaintext) { @@ -1005,12 +1038,20 @@ bool ContextualCheckTransaction( REJECT_INVALID, "bad-cb-output-desc-invalid-encct"); } + auto leadByte = encPlaintext->get_leadbyte(); + assert(leadByte == 0x01 || leadByte == 0x02); + zip_212_enabled = (leadByte == 0x02); + + zaddr = libzcash::SaplingPaymentAddress(encPlaintext->d, outPlaintext->pk_d); + value = encPlaintext->value(); + } + + { // ZIP 207: detect shielded funding stream elements if (canopyActive) { - libzcash::SaplingPaymentAddress zaddr(encPlaintext->d, outPlaintext->pk_d); for (auto it = fundingStreamElements.begin(); it != fundingStreamElements.end(); ++it) { const libzcash::SaplingPaymentAddress* streamAddr = std::get_if(&(it->first)); - if (streamAddr && zaddr == *streamAddr && encPlaintext->value() == it->second) { + if (streamAddr && zaddr == *streamAddr && value == it->second) { fundingStreamElements.erase(it); break; } @@ -1022,15 +1063,14 @@ bool ContextualCheckTransaction( // to 0x02. This applies even during the grace period, and also applies to // funding stream outputs sent to shielded payment addresses, if any. // https://zips.z.cash/zip-0212#consensus-rule-change-for-coinbase-transactions - auto leadByte = encPlaintext->get_leadbyte(); - assert(leadByte == 0x01 || leadByte == 0x02); - if (canopyActive != (leadByte == 0x02)) { + if (canopyActive != zip_212_enabled) { return state.DoS( DOS_LEVEL_BLOCK, error("ContextualCheckTransaction(): coinbase output description has invalid note plaintext version"), REJECT_INVALID, "bad-cb-output-desc-invalid-note-plaintext-version"); } + } } } } else { @@ -1246,7 +1286,7 @@ bool ContextualCheckShieldedInputs( CValidationState &state, const CCoinsViewCache &view, std::optional>& saplingAuth, - std::optional& orchardAuth, + std::optional>& orchardAuth, const Consensus::Params& consensus, uint32_t consensusBranchId, bool nu5Active, @@ -1293,15 +1333,15 @@ bool ContextualCheckShieldedInputs( if (!tx.vJoinSplit.empty()) { - if (!ed25519_verify(&tx.joinSplitPubKey, &tx.joinSplitSig, dataToBeSigned.begin(), 32)) { + if (!ed25519::verify(tx.joinSplitPubKey, tx.joinSplitSig, {dataToBeSigned.begin(), 32})) { // Check whether the failure was caused by an outdated consensus // branch ID; if so, inform the node that they need to upgrade. We // only check the previous epoch's branch ID, on the assumption that // users creating transactions will notice their transactions // failing before a second network upgrade occurs. - if (ed25519_verify(&tx.joinSplitPubKey, - &tx.joinSplitSig, - prevDataToBeSigned.begin(), 32)) { + if (ed25519::verify(tx.joinSplitPubKey, + tx.joinSplitSig, + {prevDataToBeSigned.begin(), 32})) { return state.DoS( dosLevelPotentiallyRelaxing, false, REJECT_INVALID, strprintf( "old-consensus-branch-id (Expected %s, found %s)", @@ -1371,7 +1411,7 @@ bool ContextualCheckShieldedInputs( // Queue Orchard bundle to be batch-validated. if (orchardAuth.has_value()) { - tx.GetOrchardBundle().QueueAuthValidation(orchardAuth.value(), dataToBeSigned); + tx.GetOrchardBundle().QueueAuthValidation(*orchardAuth.value(), dataToBeSigned); } return true; @@ -1732,32 +1772,6 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return true; } -CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree) -{ - uint256 hash = tx.GetHash(); - double dPriorityDelta = 0; - CAmount nFeeDelta = 0; - pool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); - if (dPriorityDelta > 0 || nFeeDelta > 0) - return 0; - - CAmount nMinFee = ::minRelayTxFee.GetFeeForRelay(nBytes); - - if (fAllowFree) - { - // There is a free transaction area in blocks created by most miners, - // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 - // to be considered to fall into this category. We don't want to encourage sending - // multiple transactions instead of one big transaction to avoid fees. - if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) - nMinFee = 0; - } - - if (!MoneyRange(nMinFee)) - nMinFee = MAX_MONEY; - return nMinFee; -} - /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state) { @@ -1842,7 +1856,7 @@ bool AcceptToMemoryPool( } { - CCoinsView dummy; + CCoinsViewDummy dummy; CCoinsViewCache view(&dummy); CAmount nValueIn = 0; @@ -1900,7 +1914,9 @@ bool AcceptToMemoryPool( CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; - double dPriority = view.GetPriority(tx, chainActive.Height()); + // nModifiedFees includes any fee deltas from PrioritiseTransaction + CAmount nModifiedFees = nFees; + pool.ApplyDelta(hash, nModifiedFees); // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. @@ -1916,46 +1932,20 @@ bool AcceptToMemoryPool( // For v1-v4 transactions, we don't yet know if the transaction commits // to consensusBranchId, but if the entry gets added to the mempool, then // it has passed ContextualCheckInputs and therefore this is correct. - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), fSpendsCoinbase, nSigOps, consensusBranchId); + CTxMemPoolEntry entry(tx, nFees, GetTime(), chainActive.Height(), pool.HasNoInputsOf(tx), fSpendsCoinbase, nSigOps, consensusBranchId); unsigned int nSize = entry.GetTxSize(); - // Before zcashd 4.2.0, we had a condition here to always accept a tx if it contained - // JoinSplits and had at least the default fee. It is no longer necessary to treat - // that as a special case, because the fee returned by GetMinRelayFee is always at - // most DEFAULT_FEE. - - CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); - if (fLimitFree && nFees < txMinFee) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, - strprintf("%d < %d", nFees, txMinFee)); - } - - // Require that free transactions have sufficient priority to be mined in the next block. - if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFeeForRelay(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); - } - - // Continuously rate-limit free (really, very-low-fee) transactions - // This mitigates 'penny-flooding' -- sending thousands of free transactions just to - // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < ::minRelayTxFee.GetFeeForRelay(nSize)) - { - static CCriticalSection csFreeLimiter; - static double dFreeCount; - static int64_t nLastTime; - int64_t nNow = GetTime(); - - LOCK(csFreeLimiter); - - // Use an exponentially decaying ~10-minute window: - dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - // -limitfreerelay unit is thousand-bytes-per-minute - // At default rate it would take over a month to fill 1GB - if (dFreeCount >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000) - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction"); - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); - dFreeCount += nSize; + // No transactions are allowed with modified fee below the minimum relay fee, + // except from disconnected blocks. The minimum relay fee will never be more + // than DEFAULT_FEE zatoshis. + CAmount minRelayFee = ::minRelayTxFee.GetFeeForRelay(nSize); + if (fLimitFree && nModifiedFees < minRelayFee) { + LogPrint("mempool", + "Not accepting transaction with txid %s, size %d bytes, effective fee %d " + MINOR_CURRENCY_UNIT + + ", and fee delta %d " + MINOR_CURRENCY_UNIT + " to the mempool due to insufficient fee. " + + " The minimum acceptance/relay fee for this transaction is %d " + MINOR_CURRENCY_UNIT, + tx.GetHash().ToString(), nSize, nModifiedFees, nModifiedFees - nFees, minRelayFee); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met"); } if (fRejectAbsurdFee && nFees > maxTxFee) { @@ -2009,7 +1999,7 @@ bool AcceptToMemoryPool( // This will be a single-transaction batch, which is still more efficient as every // Orchard bundle contains at least two signatures. - std::optional orchardAuth = orchard::AuthValidator::Batch(true); + std::optional> orchardAuth = orchard::init_batch_validator(true); // Check shielded input signatures. if (!ContextualCheckShieldedInputs( @@ -2032,7 +2022,7 @@ bool AcceptToMemoryPool( if (!saplingAuth.value()->validate()) { return state.DoS(100, false, REJECT_INVALID, "bad-sapling-bundle-authorization"); } - if (!orchardAuth.value().Validate()) { + if (!orchardAuth.value()->validate()) { return state.DoS(100, false, REJECT_INVALID, "bad-orchard-bundle-authorization"); } @@ -3115,8 +3105,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Disable Sapling and Orchard batch validation if possible. std::optional> saplingAuth = fExpensiveChecks ? std::optional(sapling::init_batch_validator(fCacheResults)) : std::nullopt; - std::optional orchardAuth = fExpensiveChecks ? - orchard::AuthValidator::Batch(fCacheResults) : orchard::AuthValidator::Disabled(); + std::optional> orchardAuth = fExpensiveChecks ? + std::optional(orchard::init_batch_validator(fCacheResults)) : std::nullopt; // If in initial block download, and this block is an ancestor of a checkpoint, // and -ibdskiptxverification is set, disable all transaction checks. @@ -3625,7 +3615,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } // Ensure Orchard signatures are valid (if we are checking them) - if (orchardAuth.has_value() && !orchardAuth.value().Validate()) { + if (orchardAuth.has_value() && !orchardAuth.value()->validate()) { return state.DoS(100, error("ConnectBlock(): an Orchard bundle within the block is invalid"), REJECT_INVALID, "bad-orchard-bundle-authorization"); @@ -4119,7 +4109,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, // Increment the count of `ConnectTip` calls. nConnectedSequence += 1; - EnforceNodeDeprecation(pindexNew->nHeight); + EnforceNodeDeprecation(chainparams, pindexNew->nHeight); int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); @@ -4386,7 +4376,17 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindexMostWork = NULL; CBlockIndex *pindexNewTip = NULL; do { - boost::this_thread::interruption_point(); + // Sleep briefly to allow other threads a chance at grabbing cs_main if + // we are connecting a long chain of blocks and would otherwise hold the + // lock almost continuously. As of 2023-02-03 the Zcash mainnet chain is + // around height 1,972,000; the total time slept here while activating + // the best chain from genesis to that height is ~6.6 minutes. This helps + // the internal wallet, if it is enabled, to keep up with the connected + // blocks, reducing the overall time until the node becomes usable. + // + // This is defined to be an interruption point. + // + boost::this_thread::sleep_for(boost::chrono::microseconds(200)); bool fInitialDownload; int nNewHeight; @@ -5442,7 +5442,7 @@ fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix) return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); } -CBlockIndex * InsertBlockIndex(uint256 hash) +CBlockIndex * InsertBlockIndex(const uint256& hash) { if (hash.IsNull()) return NULL; @@ -5664,7 +5664,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); - EnforceNodeDeprecation(chainActive.Height(), true); + EnforceNodeDeprecation(chainparams, chainActive.Height(), true); return true; } @@ -6550,9 +6550,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } - // Track requests for our stuff. - GetMainSignals().Inventory(inv.hash); - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) break; } @@ -6719,6 +6716,10 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string { pfrom->PushMessage("getaddr"); pfrom->fGetAddr = true; + + // When requesting a getaddr, accept an additional MAX_ADDR_TO_SEND addresses in response + // (bypassing the MAX_ADDR_PROCESSING_TOKEN_BUCKET limit). + pfrom->m_addr_token_bucket += MAX_ADDR_TO_SEND; } addrman.Good(pfrom->addr); } else { @@ -6811,13 +6812,38 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string vector vAddrOk; int64_t nNow = GetTime(); int64_t nSince = nNow - 10 * 60; + + // Update/increment addr rate limiting bucket. + const int64_t current_time = GetTimeMicros(); + if (pfrom->m_addr_token_bucket < MAX_ADDR_PROCESSING_TOKEN_BUCKET) { + // Don't increment bucket if it's already full + const auto time_diff = std::max(current_time - pfrom->m_addr_token_timestamp, (int64_t) 0); + const double increment = (time_diff / 1000000) * MAX_ADDR_RATE_PER_SECOND; + pfrom->m_addr_token_bucket = std::min(pfrom->m_addr_token_bucket + increment, MAX_ADDR_PROCESSING_TOKEN_BUCKET); + } + pfrom->m_addr_token_timestamp = current_time; + + uint64_t num_proc = 0; + uint64_t num_rate_limit = 0; + std::shuffle(vAddr.begin(), vAddr.end(), ZcashRandomEngine()); for (CAddress& addr : vAddr) { boost::this_thread::interruption_point(); + // Apply rate limiting if the address is not whitelisted + if (pfrom->m_addr_token_bucket < 1.0) { + if (!pfrom->IsWhitelistedRange(addr)) { + ++num_rate_limit; + continue; + } + } else { + pfrom->m_addr_token_bucket -= 1.0; + } + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; - pfrom->AddAddressKnown(addr); + pfrom->AddAddressIfNotAlreadyKnown(addr); + ++num_proc; bool fReachable = IsReachable(addr); if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { @@ -6848,6 +6874,15 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string if (fReachable) vAddrOk.push_back(addr); } + pfrom->m_addr_processed += num_proc; + pfrom->m_addr_rate_limited += num_rate_limit; + LogPrintf("ProcessMessage: Received addr: %u addresses (%u processed, %u rate-limited) from peer=%d%s\n", + vAddr.size(), + num_proc, + num_rate_limit, + pfrom->GetId(), + fLogIPs ? ", peeraddr=" + pfrom->addr.ToString() : ""); + addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom->fGetAddr = false; @@ -6912,16 +6947,13 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string } else { - pfrom->AddKnownTx(WTxId(inv.hash, inv.hashAux)); + pfrom->AddKnownWTxId(WTxId(inv.hash, inv.hashAux)); if (fBlocksOnly) LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id); else if (!fAlreadyHave && !IsInitialBlockDownload(chainparams.GetConsensus())) pfrom->AskFor(inv); } - // Track requests for our stuff - GetMainSignals().Inventory(inv.hash); - if (pfrom->nSendSize > (SendBufferSize() * 2)) { Misbehaving(pfrom->GetId(), 50); return error("send buffer size() = %u", pfrom->nSendSize); @@ -7061,7 +7093,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string LOCK(cs_main); - pfrom->AddKnownTx(wtxid); + pfrom->AddKnownWTxId(wtxid); bool fMissingInputs = false; CValidationState state; @@ -7125,7 +7157,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString()); } // Has inputs but not accepted to mempool - // Probably non-standard or insufficient fee/priority + // Probably non-standard or insufficient fee LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); vEraseQueue.push_back(orphanHash); // Add the wtxid of this transaction to our reject filter. @@ -7758,9 +7790,8 @@ bool SendMessages(const Consensus::Params& params, CNode* pto) vAddr.reserve(pto->vAddrToSend.size()); for (const CAddress& addr : pto->vAddrToSend) { - if (!pto->addrKnown.contains(addr.GetKey())) + if (pto->AddAddressIfNotAlreadyKnown(addr)) { - pto->addrKnown.insert(addr.GetKey()); vAddr.push_back(addr); // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) @@ -7833,6 +7864,12 @@ bool SendMessages(const Consensus::Params& params, CNode* pto) vector vInv; { LOCK(pto->cs_inventory); + // Avoid possibly adding to pto->filterInventoryKnown after it has been reset in CloseSocketDisconnect. + if (pto->fDisconnect) { + // We can safely return here because SendMessages would, in any case, do nothing after + // this block if pto->fDisconnect is set. + return true; + } vInv.reserve(std::max(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX)); // Add blocks @@ -7880,7 +7917,7 @@ bool SendMessages(const Consensus::Params& params, CNode* pto) if (pto->pfilter) { if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; } - pto->filterInventoryKnown.insert(hash); + pto->AddKnownTxId(hash); vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { pto->PushMessage("inv", vInv); @@ -7915,7 +7952,7 @@ bool SendMessages(const Consensus::Params& params, CNode* pto) // Remove it from the to-be-sent set pto->setInventoryTxToSend.erase(it); // Check if not in the filter already - if (pto->filterInventoryKnown.contains(hash)) { + if (pto->HasKnownTxId(hash)) { continue; } // Not in the mempool anymore? don't bother sending it. @@ -7950,7 +7987,7 @@ bool SendMessages(const Consensus::Params& params, CNode* pto) pto->PushMessage("inv", vInv); vInv.clear(); } - pto->filterInventoryKnown.insert(hash); + pto->AddKnownTxId(hash); } } } diff --git a/depend/zcash/src/main.h b/depend/zcash/src/main.h index dabba715d..39b9cf9fb 100644 --- a/depend/zcash/src/main.h +++ b/depend/zcash/src/main.h @@ -43,7 +43,7 @@ #include #include -#include +#include #include #include @@ -68,14 +68,16 @@ static const unsigned int MAX_REORG_LENGTH = COINBASE_MATURITY - 1; static const bool DEFAULT_WHITELISTRELAY = true; /** Default for DEFAULT_WHITELISTFORCERELAY. */ static const bool DEFAULT_WHITELISTFORCERELAY = true; -/** Default for -minrelaytxfee, minimum relay fee for transactions in zatoshis/kB */ +/** Default for -minrelaytxfee, minimum relay fee rate for transactions in zatoshis per 1000 bytes. TODO(misnamed, this is a rate) */ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100; -//! -maxtxfee default +/** Default for -maxtxfee in zatoshis. */ static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN; -//! Discourage users to set fees higher than this amount (in satoshis) per kB +/** Discourage users from setting fee rates higher than this in zatoshis per 1000 bytes. */ static const CAmount HIGH_TX_FEE_PER_KB = 0.01 * COIN; -//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis) +/** Warn if -maxtxfee is set to a fee higher than this in zatoshis. */ static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB; +//! -maxtxfee will error if called with a fee that won’t allow tx to have this many actions +static const unsigned int LOW_LOGICAL_ACTIONS = 10; /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; /** Default for -limitancestorcount, max number of in-mempool ancestors */ @@ -135,8 +137,6 @@ static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5; * Limits the impact of low-fee transaction floods. */ static const unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL; -static const unsigned int DEFAULT_LIMITFREERELAY = 15; -static const bool DEFAULT_RELAYPRIORITY = false; static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; /** Default for -permitbaremultisig */ @@ -210,9 +210,9 @@ extern bool fIBDSkipTxVerification; // it is unneeded for testing extern bool fCoinbaseEnforcedShieldingEnabled; extern size_t nCoinCacheUsage; -/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */ +/** Transactions must have at least this fee rate (in zatoshis per 1000 bytes) for relaying, mining and transaction creation. */ extern CFeeRate minRelayTxFee; -/** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */ +/** Absolute maximum transaction fee (in zatoshis) used by wallet and mempool (rejects high fee in sendrawtransaction). */ extern CAmount maxTxFee; extern bool fAlerts; /** If the tip is older than this (in seconds), the node is considered to be in initial block download. */ @@ -332,7 +332,7 @@ void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight void UnlinkPrunedFiles(std::set& setFilesToPrune); /** Create a new block index entry for a given block hash */ -CBlockIndex * InsertBlockIndex(uint256 hash); +CBlockIndex * InsertBlockIndex(const uint256& hash); /** Get statistics from node state */ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); /** Increase a node's misbehavior score. */ @@ -359,9 +359,6 @@ struct CNodeStateStats { }; - -CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree); - /** * Count ECDSA signature operations the old-fashioned (pre-0.6) way * @return number of sigops this transaction's outputs will produce when spent @@ -414,7 +411,7 @@ bool ContextualCheckShieldedInputs( CValidationState &state, const CCoinsViewCache &view, std::optional>& saplingAuth, - std::optional& orchardAuth, + std::optional>& orchardAuth, const Consensus::Params& consensus, uint32_t consensusBranchId, bool nu5Active, diff --git a/depend/zcash/src/mempool_limit.cpp b/depend/zcash/src/mempool_limit.cpp index 97c679956..89f6f28b4 100644 --- a/depend/zcash/src/mempool_limit.cpp +++ b/depend/zcash/src/mempool_limit.cpp @@ -5,15 +5,11 @@ #include "mempool_limit.h" #include "core_memusage.h" -#include "logging.h" -#include "random.h" #include "serialize.h" #include "timedata.h" #include "util/time.h" #include "version.h" -const TxWeight ZERO_WEIGHT = TxWeight(0, 0); - void RecentlyEvictedList::pruneList() { if (txIdSet.empty()) { @@ -43,117 +39,13 @@ bool RecentlyEvictedList::contains(const uint256& txId) return txIdSet.count(txId) > 0; } - -TxWeight WeightedTxTree::getWeightAt(size_t index) const -{ - return index < size ? txIdAndWeights[index].txWeight.add(childWeights[index]) : ZERO_WEIGHT; -} - -void WeightedTxTree::backPropagate(size_t fromIndex, const TxWeight& weightDelta) -{ - while (fromIndex > 0) { - fromIndex = (fromIndex - 1) / 2; - childWeights[fromIndex] = childWeights[fromIndex].add(weightDelta); - } -} - -size_t WeightedTxTree::findByEvictionWeight(size_t fromIndex, int64_t weightToFind) const -{ - int leftWeight = getWeightAt(fromIndex * 2 + 1).evictionWeight; - int rightWeight = getWeightAt(fromIndex).evictionWeight - getWeightAt(fromIndex * 2 + 2).evictionWeight; - // On Left - if (weightToFind < leftWeight) { - return findByEvictionWeight(fromIndex * 2 + 1, weightToFind); - } - // Found - if (weightToFind < rightWeight) { - return fromIndex; - } - // On Right - return findByEvictionWeight(fromIndex * 2 + 2, weightToFind - rightWeight); -} - -TxWeight WeightedTxTree::getTotalWeight() const -{ - return getWeightAt(0); -} - - -void WeightedTxTree::add(const WeightedTxInfo& weightedTxInfo) -{ - if (txIdToIndexMap.count(weightedTxInfo.txId) > 0) { - // This should not happen, but should be prevented nonetheless - return; - } - txIdAndWeights.push_back(weightedTxInfo); - childWeights.push_back(ZERO_WEIGHT); - txIdToIndexMap[weightedTxInfo.txId] = size; - backPropagate(size, weightedTxInfo.txWeight); - size += 1; -} - -void WeightedTxTree::remove(const uint256& txId) -{ - if (txIdToIndexMap.find(txId) == txIdToIndexMap.end()) { - // Remove may be called multiple times for a given tx, so this is necessary - return; - } - - size_t removeIndex = txIdToIndexMap[txId]; - - // We reduce the size at the start of this method to avoid saying size - 1 - // when referring to the last element of the array below - size -= 1; - - TxWeight lastChildWeight = txIdAndWeights[size].txWeight; - backPropagate(size, lastChildWeight.negate()); - - if (removeIndex < size) { - TxWeight weightDelta = lastChildWeight.add(txIdAndWeights[removeIndex].txWeight.negate()); - txIdAndWeights[removeIndex] = txIdAndWeights[size]; - txIdToIndexMap[txIdAndWeights[removeIndex].txId] = removeIndex; - backPropagate(removeIndex, weightDelta); - } - - txIdToIndexMap.erase(txId); - txIdAndWeights.pop_back(); - childWeights.pop_back(); -} - -std::optional WeightedTxTree::maybeDropRandom() -{ - TxWeight totalTxWeight = getTotalWeight(); - if (totalTxWeight.cost <= capacity) { - return std::nullopt; - } - LogPrint("mempool", "Mempool cost limit exceeded (cost=%d, limit=%d)\n", totalTxWeight.cost, capacity); - int randomWeight = GetRand(totalTxWeight.evictionWeight); - WeightedTxInfo drop = txIdAndWeights[findByEvictionWeight(0, randomWeight)]; - LogPrint("mempool", "Evicting transaction (txid=%s, cost=%d, evictionWeight=%d)\n", - drop.txId.ToString(), drop.txWeight.cost, drop.txWeight.evictionWeight); - remove(drop.txId); - return drop.txId; -} - - -TxWeight TxWeight::add(const TxWeight& other) const -{ - return TxWeight(cost + other.cost, evictionWeight + other.evictionWeight); -} - -TxWeight TxWeight::negate() const -{ - return TxWeight(-cost, -evictionWeight); -} - - -WeightedTxInfo WeightedTxInfo::from(const CTransaction& tx, const CAmount& fee) +std::pair MempoolCostAndEvictionWeight(const CTransaction& tx, const CAmount& fee) { size_t memUsage = RecursiveDynamicUsage(tx); int64_t cost = std::max((int64_t) memUsage, (int64_t) MIN_TX_COST); int64_t evictionWeight = cost; - if (fee < DEFAULT_FEE) { + if (fee < tx.GetConventionalFee()) { evictionWeight += LOW_FEE_PENALTY; } - return WeightedTxInfo(tx.GetHash(), TxWeight(cost, evictionWeight)); + return std::make_pair(cost, evictionWeight); } diff --git a/depend/zcash/src/mempool_limit.h b/depend/zcash/src/mempool_limit.h index 0075ce803..b9a7ffee5 100644 --- a/depend/zcash/src/mempool_limit.h +++ b/depend/zcash/src/mempool_limit.h @@ -9,19 +9,21 @@ #include #include #include -#include +#include "logging.h" +#include "random.h" #include "primitives/transaction.h" #include "policy/fees.h" #include "uint256.h" #include "util/time.h" +#include "weighted_map.h" const size_t DEFAULT_MEMPOOL_TOTAL_COST_LIMIT = 80000000; const int64_t DEFAULT_MEMPOOL_EVICTION_MEMORY_MINUTES = 60; const size_t EVICTION_MEMORY_ENTRIES = 40000; -const uint64_t MIN_TX_COST = 4000; -const uint64_t LOW_FEE_PENALTY = 16000; +const int64_t MIN_TX_COST = 10000; +const int64_t LOW_FEE_PENALTY = 40000; // This class keeps track of transactions which have been recently evicted from the mempool @@ -57,80 +59,62 @@ class RecentlyEvictedList // The mempool of a node holds a set of transactions. Each transaction has a *cost*, // which is an integer defined as: -// max(serialized transaction size in bytes, 4000) +// max(memory usage in bytes, 4000) +// // Each transaction also has an *eviction weight*, which is *cost* + *fee_penalty*, -// where *fee_penalty* is 16000 if the transaction pays a fee less than 10000 zatoshi, -// otherwise 0. -struct TxWeight { - int64_t cost; - int64_t evictionWeight; // *cost* + *fee_penalty* - - TxWeight(int64_t cost_, int64_t evictionWeight_) - : cost(cost_), evictionWeight(evictionWeight_) {} - - TxWeight add(const TxWeight& other) const; - TxWeight negate() const; -}; +// where *fee_penalty* is 16000 if the transaction pays a fee less than the +// conventional fee, otherwise 0. +// Calculate cost and eviction weight based on the memory usage and fee. +std::pair MempoolCostAndEvictionWeight(const CTransaction& tx, const CAmount& fee); -// This struct is a pair of txid, cost. -struct WeightedTxInfo { - uint256 txId; - TxWeight txWeight; - - WeightedTxInfo(uint256 txId_, TxWeight txWeight_) : txId(txId_), txWeight(txWeight_) {} - - // Factory method which calculates cost based on size in bytes and fee. - static WeightedTxInfo from(const CTransaction& tx, const CAmount& fee); -}; - -// The following class is a collection of transaction ids and their costs. -// In order to be able to remove transactions randomly weighted by their cost, -// we keep track of the total cost of all transactions in this collection. -// For performance reasons, the collection is represented as a complete binary -// tree where each node knows the sum of the weights of the children. This -// allows for addition, removal, and random selection/dropping in logarithmic time. -class WeightedTxTree +class MempoolLimitTxSet { - const int64_t capacity; - size_t size = 0; - - // The following two vectors are the tree representation of this collection. - // We keep track of 3 data points for each node: A transaction's txid, its cost, - // and the sum of the weights of all children and descendant of that node. - std::vector txIdAndWeights; - std::vector childWeights; - - // The following map is to simplify removal. When removing a tx, we do so by txid. - // This map allows looking up the transaction's index in the tree. - std::map txIdToIndexMap; - - // Returns the sum of a node and all of its children's TxWeights for a given index. - TxWeight getWeightAt(size_t index) const; - - // When adding and removing a node we need to update its parent and all of its - // ancestors to reflect its cost. - void backPropagate(size_t fromIndex, const TxWeight& weightDelta); - - // For a given random cost + fee penalty, this method recursively finds the - // correct transaction. This is used by WeightedTxTree::maybeDropRandom(). - size_t findByEvictionWeight(size_t fromIndex, int64_t weightToFind) const; + WeightedMap txmap; + int64_t capacity; + int64_t cost; public: - WeightedTxTree(int64_t capacity_) : capacity(capacity_) { + MempoolLimitTxSet(int64_t capacity_) : capacity(capacity_), cost(0) { assert(capacity >= 0); } - TxWeight getTotalWeight() const; - - void add(const WeightedTxInfo& weightedTxInfo); - void remove(const uint256& txId); + int64_t getTotalWeight() const + { + return txmap.getTotalWeight(); + } + bool empty() const + { + return txmap.empty(); + } + void add(const uint256& txId, int64_t txCost, int64_t txWeight) + { + if (txmap.add(txId, txCost, txWeight)) { + cost += txCost; + } + } + void remove(const uint256& txId) + { + cost -= txmap.remove(txId).value_or(0); + } - // If the total cost limit is exceeded, pick a random number based on the total cost - // of the collection and remove the associated transaction. - std::optional maybeDropRandom(); + // If the total cost limit has not been exceeded, return std::nullopt. Otherwise, + // pick a transaction at random with probability proportional to its eviction weight; + // remove and return that transaction's txid. + std::optional maybeDropRandom() + { + if (cost <= capacity) { + return std::nullopt; + } + LogPrint("mempool", "Mempool cost limit exceeded (cost=%d, limit=%d)\n", cost, capacity); + assert(!txmap.empty()); + auto [txId, txCost, txWeight] = txmap.takeRandom().value(); + cost -= txCost; + LogPrint("mempool", "Evicting transaction (txid=%s, cost=%d, weight=%d)\n", + txId.ToString(), txCost, txWeight); + return txId; + } }; - #endif // ZCASH_MEMPOOL_LIMIT_H diff --git a/depend/zcash/src/metrics.cpp b/depend/zcash/src/metrics.cpp index c0933e49b..1e2fc5289 100644 --- a/depend/zcash/src/metrics.cpp +++ b/depend/zcash/src/metrics.cpp @@ -619,7 +619,7 @@ void ThreadShowMetricsScreen() std::cout << std::endl; // Thank you text - std::cout << strprintf(_("Thank you for running a %s zcashd v%s node!"), WhichNetwork(), FormatVersion(CLIENT_VERSION)) << std::endl; + std::cout << strprintf(_("Thank you for running a %s zcashd %s node!"), WhichNetwork(), FormatFullVersion()) << std::endl; std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl; // Privacy notice text diff --git a/depend/zcash/src/miner.cpp b/depend/zcash/src/miner.cpp index cf35c3bc8..a800fe5d3 100644 --- a/depend/zcash/src/miner.cpp +++ b/depend/zcash/src/miner.cpp @@ -35,9 +35,10 @@ #include "util/system.h" #include "util/moneystr.h" #include "validationinterface.h" +#include "zip317.h" #include -#include +#include #include #include @@ -57,8 +58,8 @@ using namespace std; // // Unconfirmed transactions in the memory pool often depend on other // transactions in the memory pool. When we select transactions from the -// pool, we select by highest priority or fee rate, so we might consider -// transactions that depend on transactions that aren't yet in the block. +// pool, we select by highest fee rate, so we might consider transactions +// that depend on transactions that aren't yet in the block. std::optional last_block_num_txs; std::optional last_block_size; @@ -331,18 +332,55 @@ CMutableTransaction CreateCoinbaseTransaction(const CChainParams& chainparams, C return mtx; } -CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddress& minerAddress, const std::optional& next_cb_mtx) +BlockAssembler::BlockAssembler(const CChainParams& _chainparams) + : chainparams(_chainparams) { - // Create new block - std::unique_ptr pblocktemplate(new CBlockTemplate()); + // Largest block you're willing to create: + nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); + // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: + nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + + // Number of unpaid actions allowed in a block: + nBlockUnpaidActionLimit = (size_t) GetArg("-blockunpaidactionlimit", DEFAULT_BLOCK_UNPAID_ACTION_LIMIT); +} + +void BlockAssembler::resetBlock(const MinerAddress& minerAddress) +{ + inBlock.clear(); + + // Reserve space for coinbase tx + // nBlockMaxSize already includes 1000 bytes for transaction structure overhead. + nBlockSize = examine(minerAddress, match { + [](const libzcash::OrchardRawAddress&) { return 9000; }, + [](const libzcash::SaplingPaymentAddress&) { return 1000; }, + [](const boost::shared_ptr &) { return 1000; }, + }); + nBlockSigOps = 100; + + // These counters do not include coinbase tx + nBlockTx = 0; + nFees = 0; + + sproutValue = 0; + saplingValue = 0; + orchardValue = 0; + monitoring_pool_balances = true; + + lastFewTxs = 0; + blockFinished = false; +} + +CBlockTemplate* BlockAssembler::CreateNewBlock( + const MinerAddress& minerAddress, + const std::optional& next_cb_mtx) +{ + resetBlock(minerAddress); + + pblocktemplate.reset(new CBlockTemplate()); + if(!pblocktemplate.get()) return NULL; - CBlock *pblock = &pblocktemplate->block; // pointer for convenience - - // -regtest only: allow overriding block.nVersion with - // -blockversion=N to test forking scenarios - if (chainparams.MineBlocksOnDemand()) - pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + pblock = &pblocktemplate->block; // pointer for convenience // Add dummy coinbase tx as first transaction pblock->vtx.push_back(CTransaction()); @@ -351,334 +389,350 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre // If we're given a coinbase tx, it's been precomputed, its fees are zero, // so we can't include any mempool transactions; this will be an empty block. - bool nonEmptyBlock = !next_cb_mtx; + blockFinished = blockFinished || next_cb_mtx; - // Largest block you're willing to create: - unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); - // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: - nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + LOCK2(cs_main, mempool.cs); + CBlockIndex* pindexPrev = chainActive.Tip(); + nHeight = pindexPrev->nHeight + 1; + uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus()); - // How much of the block should be dedicated to high-priority transactions, - // included regardless of the fees they pay - unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); - nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios + if (chainparams.MineBlocksOnDemand()) + pblock->nVersion = GetArg("-blockversion", pblock->nVersion); - // Minimum block size you want to create; block will be filled with free transactions - // until there are no more or the block reaches this size: - unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE); - nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); + pblock->nTime = GetTime(); + const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); + CCoinsViewCache view(pcoinsTip); + + SaplingMerkleTree sapling_tree; + assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); + + nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) + ? nMedianTimePast + : pblock->GetBlockTime(); + + // We want to track the value pool, but if the miner gets + // invoked on an old block before the hardcoded fallback + // is active we don't want to trip up any assertions. So, + // we only adhere to the turnstile (as a miner) if we + // actually have all of the information necessary to do + // so. + if (chainparams.ZIP209Enabled()) { + if (pindexPrev->nChainSproutValue) { + sproutValue = *pindexPrev->nChainSproutValue; + } else { + monitoring_pool_balances = false; + } + if (pindexPrev->nChainSaplingValue) { + saplingValue = *pindexPrev->nChainSaplingValue; + } else { + monitoring_pool_balances = false; + } + if (pindexPrev->nChainOrchardValue) { + orchardValue = *pindexPrev->nChainOrchardValue; + } else { + monitoring_pool_balances = false; + } + } - // Collect memory pool transactions into the block - CTxMemPool::setEntries inBlock; - CTxMemPool::setEntries waitSet; + constructZIP317BlockTemplate(); - // This vector will be sorted into a priority queue: - vector vecPriority; - TxCoinAgePriorityCompare pricomparer; - std::map waitPriMap; - typedef std::map::iterator waitPriIter; - double actualPriority = -1; + last_block_num_txs = nBlockTx; + last_block_size = nBlockSize; + LogPrintf("%s: total size %u (excluding coinbase) txs: %u fees: %ld sigops %d", __func__, nBlockSize, nBlockTx, nFees, nBlockSigOps); - std::priority_queue, ScoreCompare> clearedTxs; - bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); - uint64_t nBlockSize = 1000; - uint64_t nBlockTx = 0; - unsigned int nBlockSigOps = 100; - int lastFewTxs = 0; - CAmount nFees = 0; + // Create coinbase tx + if (next_cb_mtx) { + pblock->vtx[0] = *next_cb_mtx; + } else { + pblock->vtx[0] = CreateCoinbaseTransaction(chainparams, nFees, minerAddress, nHeight); + } + pblocktemplate->vTxFees[0] = -nFees; - { - LOCK2(cs_main, mempool.cs); - CBlockIndex* pindexPrev = chainActive.Tip(); - const int nHeight = pindexPrev->nHeight + 1; - uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus()); - pblock->nTime = GetTime(); - const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); - CCoinsViewCache view(pcoinsTip); - - SaplingMerkleTree sapling_tree; - assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); - - int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) - ? nMedianTimePast - : pblock->GetBlockTime(); - - bool fPriorityBlock = nBlockPrioritySize > 0; - if (nonEmptyBlock && fPriorityBlock) { - vecPriority.reserve(mempool.mapTx.size()); - for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); - mi != mempool.mapTx.end(); ++mi) - { - double dPriority = mi->GetPriority(nHeight); - CAmount dummy; - mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy); - vecPriority.push_back(TxCoinAgePriority(dPriority, mi)); - } - std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + // Update the Sapling commitment tree. + for (const CTransaction& tx : pblock->vtx) { + for (const OutputDescription& odesc : tx.vShieldedOutput) { + sapling_tree.append(odesc.cmu); } + } - CTxMemPool::indexed_transaction_set::nth_index<2>::type::iterator mi = mempool.mapTx.get<2>().begin(); - CTxMemPool::txiter iter; + // Randomise nonce + arith_uint256 nonce = UintToArith256(GetRandHash()); + // Clear the top and bottom 16 bits (for local use as thread flags and counters) + nonce <<= 32; + nonce >>= 16; + pblock->nNonce = ArithToUint256(nonce); + + uint32_t prevConsensusBranchId = CurrentEpochBranchId(pindexPrev->nHeight, chainparams.GetConsensus()); + + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_NU5)) { + // hashBlockCommitments depends on the block transactions, so we have to + // update it whenever the coinbase transaction changes. + // + // - For the internal miner (either directly or via the `generate` RPC), this + // will occur in `IncrementExtraNonce()`, like for `hashMerkleRoot`. + // - For `getblocktemplate`, we have two sets of fields to handle: + // - The `defaultroots` fields, which contain both the default value (if + // nothing in the template is altered), and the roots that can be used to + // recalculate it (if some or all of the template is altered). + // - The legacy `finalsaplingroothash`, `lightclientroothash`, and + // `blockcommitmentshash` fields, which had the semantics of "place this + // value into the block header and things will work" (except for in + // v4.6.0 where they were accidentally set to always be the NU5 value). + // + // To accommodate all use cases, we calculate the `hashBlockCommitments` + // default value here (unlike `hashMerkleRoot`), and additionally cache the + // values necessary to recalculate it. + pblocktemplate->hashChainHistoryRoot = view.GetHistoryRoot(prevConsensusBranchId); + pblocktemplate->hashAuthDataRoot = pblock->BuildAuthDataMerkleTree(); + pblock->hashBlockCommitments = DeriveBlockCommitmentsHash( + pblocktemplate->hashChainHistoryRoot, + pblocktemplate->hashAuthDataRoot); + } else if (IsActivationHeight(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_HEARTWOOD)) { + pblocktemplate->hashChainHistoryRoot.SetNull(); + pblocktemplate->hashAuthDataRoot.SetNull(); + pblock->hashBlockCommitments.SetNull(); + } else if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD)) { + pblocktemplate->hashChainHistoryRoot = view.GetHistoryRoot(prevConsensusBranchId); + pblocktemplate->hashAuthDataRoot.SetNull(); + pblock->hashBlockCommitments = pblocktemplate->hashChainHistoryRoot; + } else { + pblocktemplate->hashChainHistoryRoot.SetNull(); + pblocktemplate->hashAuthDataRoot.SetNull(); + pblock->hashBlockCommitments = sapling_tree.root(); + } + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); + pblock->nSolution.clear(); + pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); - // We want to track the value pool, but if the miner gets - // invoked on an old block before the hardcoded fallback - // is active we don't want to trip up any assertions. So, - // we only adhere to the turnstile (as a miner) if we - // actually have all of the information necessary to do - // so. - CAmount sproutValue = 0; - CAmount saplingValue = 0; - CAmount orchardValue = 0; - bool monitoring_pool_balances = true; - if (chainparams.ZIP209Enabled()) { - if (pindexPrev->nChainSproutValue) { - sproutValue = *pindexPrev->nChainSproutValue; - } else { - monitoring_pool_balances = false; - } - if (pindexPrev->nChainSaplingValue) { - saplingValue = *pindexPrev->nChainSaplingValue; - } else { - monitoring_pool_balances = false; - } - if (pindexPrev->nChainOrchardValue) { - orchardValue = *pindexPrev->nChainOrchardValue; - } else { - monitoring_pool_balances = false; - } + CValidationState state; + if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, true)) { + throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); + } + + return pblocktemplate.release(); +} + +bool BlockAssembler::isStillDependent(CTxMemPool::txiter iter) +{ + for (CTxMemPool::txiter parent : mempool.GetMemPoolParents(iter)) + { + if (!inBlock.count(parent)) { + return true; } + } + return false; +} - while (nonEmptyBlock && (mi != mempool.mapTx.get<2>().end() || !clearedTxs.empty())) - { - bool priorityTx = false; - if (fPriorityBlock && !vecPriority.empty()) { // add a tx from priority queue to fill the blockprioritysize - priorityTx = true; - iter = vecPriority.front().second; - actualPriority = vecPriority.front().first; - std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer); - vecPriority.pop_back(); - } - else if (clearedTxs.empty()) { // add tx with next highest score - iter = mempool.mapTx.project<0>(mi); - mi++; - } - else { // try to add a previously postponed child tx - iter = clearedTxs.top(); - clearedTxs.pop(); - } - if (inBlock.count(iter)) - continue; // could have been added to the priorityBlock - const CTransaction& tx = iter->GetTx(); - const uint256& hash = tx.GetHash(); +bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter) +{ + if (nBlockSize + iter->GetTxSize() >= nBlockMaxSize) { + // If the block is so close to full that no more txs will fit + // or if we've tried more than 50 times to fill remaining space + // then flag that the block is finished + if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { + blockFinished = true; + return false; + } + // Once we're within 1000 bytes of a full block, only look at 50 more txs + // to try to fill the remaining space. + if (nBlockSize > nBlockMaxSize - 1000) { + lastFewTxs++; + } + LogPrintf("%s: skipping tx %s: exceeded maximum block size %u.", + __func__, + iter->GetTx().GetHash().GetHex(), + nBlockMaxSize); + return false; + } - bool fOrphan = false; - for (CTxMemPool::txiter parent : mempool.GetMemPoolParents(iter)) - { - if (!inBlock.count(parent)) { - fOrphan = true; - break; - } - } - if (fOrphan) { - if (priorityTx) - waitPriMap.insert(std::make_pair(iter,actualPriority)); - else - waitSet.insert(iter); - continue; - } + if (nBlockSigOps + iter->GetSigOpCount() >= MAX_BLOCK_SIGOPS) { + // If the block has room for no more sig ops then + // flag that the block is finished + if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { + blockFinished = true; + return false; + } + // Otherwise attempt to find another tx with fewer sigops + // to put in the block. + LogPrintf("%s: skipping tx %s: exceeds legacy max sigops %u.", + __func__, + iter->GetTx().GetHash().GetHex(), + MAX_BLOCK_SIGOPS); + return false; + } - unsigned int nTxSize = iter->GetTxSize(); - if (fPriorityBlock && - (nBlockSize + nTxSize >= nBlockPrioritySize || !AllowFree(actualPriority))) { - fPriorityBlock = false; - waitPriMap.clear(); - } - if (!priorityTx && - (iter->GetModifiedFee() < ::minRelayTxFee.GetFee(nTxSize)) && - (iter->GetModifiedFee() < DEFAULT_FEE) && - (nBlockSize >= nBlockMinSize)) { - break; - } - if (nBlockSize + nTxSize >= nBlockMaxSize) { - if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { - break; - } - // Once we're within 1000 bytes of a full block, only look at 50 more txs - // to try to fill the remaining space. - if (nBlockSize > nBlockMaxSize - 1000) { - lastFewTxs++; - } - LogPrintf("%s: skipping tx %s: exceeded maximum block size %u.", __func__, hash.GetHex(), nBlockMaxSize); - continue; - } + // Must check that lock times are still valid + // This can be removed once MTP is always enforced + // as long as reorgs keep the mempool consistent. + if (!IsFinalTx(iter->GetTx(), nHeight, nLockTimeCutoff)) + return false; - if (!IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight)) - continue; + // Must check that expiry heights are still valid. + if (IsExpiredTx(iter->GetTx(), nHeight)) + return false; - unsigned int nTxSigOps = iter->GetSigOpCount(); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) { - if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { - break; - } - LogPrintf("%s: skipping tx %s: exceeds legacy max sigops %u.", __func__, hash.GetHex(), MAX_BLOCK_SIGOPS); - continue; - } + if (chainparams.ZIP209Enabled() && monitoring_pool_balances) { + // Does this transaction lead to a turnstile violation? - if (chainparams.ZIP209Enabled() && monitoring_pool_balances) { - // Does this transaction lead to a turnstile violation? + CAmount sproutValueDummy = sproutValue; + CAmount saplingValueDummy = saplingValue; + CAmount orchardValueDummy = orchardValue; - CAmount sproutValueDummy = sproutValue; - CAmount saplingValueDummy = saplingValue; - CAmount orchardValueDummy = orchardValue; + saplingValueDummy += -iter->GetTx().GetValueBalanceSapling(); + orchardValueDummy += -iter->GetTx().GetOrchardBundle().GetValueBalance(); - saplingValueDummy += -tx.GetValueBalanceSapling(); - orchardValueDummy += -tx.GetOrchardBundle().GetValueBalance(); + for (auto js : iter->GetTx().vJoinSplit) { + sproutValueDummy += js.vpub_old; + sproutValueDummy -= js.vpub_new; + } - for (auto js : tx.vJoinSplit) { - sproutValueDummy += js.vpub_old; - sproutValueDummy -= js.vpub_new; - } + if (sproutValueDummy < 0) { + LogPrintf("CreateNewBlock: tx %s appears to violate Sprout turnstile\n", + iter->GetTx().GetHash().GetHex()); + return false; + } + if (saplingValueDummy < 0) { + LogPrintf("CreateNewBlock: tx %s appears to violate Sapling turnstile\n", + iter->GetTx().GetHash().GetHex()); + return false; + } + if (orchardValueDummy < 0) { + LogPrintf("CreateNewBlock: tx %s appears to violate Orchard turnstile\n", + iter->GetTx().GetHash().GetHex()); + return false; + } - if (sproutValueDummy < 0) { - LogPrintf("%s: tx %s appears to violate Sprout turnstile\n", __func__, hash.GetHex()); - continue; - } - if (saplingValueDummy < 0) { - LogPrintf("%s: tx %s appears to violate Sapling turnstile\n", __func__, hash.GetHex()); - continue; - } - if (orchardValueDummy < 0) { - LogPrintf("%s: tx %s appears to violate Orchard turnstile\n", __func__, hash.GetHex()); - continue; - } + // We update this here instead of in AddToBlock to avoid recalculating + // the deltas, because there are no more checks and we know that the + // transaction will be added to the block. + sproutValue = sproutValueDummy; + saplingValue = saplingValueDummy; + orchardValue = orchardValueDummy; + } - sproutValue = sproutValueDummy; - saplingValue = saplingValueDummy; - orchardValue = orchardValueDummy; - } + return true; +} - CAmount nTxFees = iter->GetFee(); - // Added - pblock->vtx.push_back(tx); - pblocktemplate->vTxFees.push_back(nTxFees); - pblocktemplate->vTxSigOps.push_back(nTxSigOps); - nBlockSize += nTxSize; - ++nBlockTx; - nBlockSigOps += nTxSigOps; - nFees += nTxFees; - - if (fPrintPriority) - { - double dPriority = iter->GetPriority(nHeight); - CAmount dummy; - mempool.ApplyDeltas(tx.GetHash(), dPriority, dummy); - LogPrintf("%s: priority %.1f fee %s txid %s\n", - __func__, dPriority , CFeeRate(iter->GetModifiedFee(), nTxSize).ToString(), hash.GetHex()); - } +void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) +{ + pblock->vtx.push_back(iter->GetTx()); + pblocktemplate->vTxFees.push_back(iter->GetFee()); + pblocktemplate->vTxSigOps.push_back(iter->GetSigOpCount()); + nBlockSize += iter->GetTxSize(); + ++nBlockTx; + nBlockSigOps += iter->GetSigOpCount(); + nFees += iter->GetFee(); + inBlock.insert(iter); - inBlock.insert(iter); + bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); + if (fPrintPriority) { + LogPrintf("%s: txid %s; modified fee %s; conventional fee %s; size %d bytes; logical actions %d; unpaid actions %d\n", + __func__, + iter->GetTx().GetHash().ToString(), + FormatMoney(iter->GetModifiedFee()), + FormatMoney(iter->GetTx().GetConventionalFee()), + iter->GetTxSize(), + iter->GetTx().GetLogicalActionCount(), + iter->GetUnpaidActionCount()); + } +} - // Add transactions that depend on this one to the priority queue - for (CTxMemPool::txiter child : mempool.GetMemPoolChildren(iter)) - { - if (fPriorityBlock) { - waitPriIter wpiter = waitPriMap.find(child); - if (wpiter != waitPriMap.end()) { - vecPriority.push_back(TxCoinAgePriority(wpiter->second,child)); - std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); - waitPriMap.erase(wpiter); - } - } - else { - if (waitSet.count(child)) { - clearedTxs.push(child); - waitSet.erase(child); - } - } - } +void BlockAssembler::constructZIP317BlockTemplate() +{ + CTxMemPool::weightedCandidates candidatesPayingConventionalFee; + CTxMemPool::weightedCandidates candidatesNotPayingConventionalFee; + + for (auto mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) + { + int128_t weightRatio = mi->GetWeightRatio(); + if (weightRatio >= WEIGHT_RATIO_SCALE) { + candidatesPayingConventionalFee.add(mi->GetTx().GetHash(), mi, weightRatio); + } else { + candidatesNotPayingConventionalFee.add(mi->GetTx().GetHash(), mi, weightRatio); } - last_block_num_txs = nBlockTx; - last_block_size = nBlockSize; - LogPrintf("%s: total size %u (excluding coinbase) txs: %u fees: %ld sigops %d", __func__, nBlockSize, nBlockTx, nFees, nBlockSigOps); + } - // Create coinbase tx - if (next_cb_mtx) { - pblock->vtx[0] = *next_cb_mtx; + CTxMemPool::queueEntries waiting; + CTxMemPool::queueEntries cleared; + addTransactions(candidatesPayingConventionalFee, waiting, cleared); + addTransactions(candidatesNotPayingConventionalFee, waiting, cleared); +} + +void BlockAssembler::addTransactions( + CTxMemPool::weightedCandidates& candidates, + CTxMemPool::queueEntries& waiting, + CTxMemPool::queueEntries& cleared) +{ + size_t nBlockUnpaidActions = 0; + + while (!blockFinished && !(candidates.empty() && cleared.empty())) + { + CTxMemPool::txiter iter; + if (cleared.empty()) { + // If no txs that were previously postponed are available to try + // again, then select the next transaction randomly by weight ratio + // from the candidate set. + assert(!candidates.empty()); + iter = std::get<1>(candidates.takeRandom().value()); } else { - pblock->vtx[0] = CreateCoinbaseTransaction(chainparams, nFees, minerAddress, nHeight); + // If a previously postponed tx is available to try again, then it + // has already been randomly sampled, so just take it in order. + iter = cleared.front(); + cleared.pop_front(); } - pblocktemplate->vTxFees[0] = -nFees; - // Update the Sapling commitment tree. - for (const CTransaction& tx : pblock->vtx) { - for (const OutputDescription& odesc : tx.vShieldedOutput) { - sapling_tree.append(odesc.cmu); - } + // The tx should never already be in the block for ZIP 317. + assert(inBlock.count(iter) == 0); + + // If the tx would cause the block to exceed the unpaid action limit, skip it. + // A tx that pays at least the conventional fee will have no unpaid actions. + size_t txUnpaidActions = iter->GetUnpaidActionCount(); + if (nBlockUnpaidActions + txUnpaidActions > nBlockUnpaidActionLimit) { + continue; } - // Randomise nonce - arith_uint256 nonce = UintToArith256(GetRandHash()); - // Clear the top and bottom 16 bits (for local use as thread flags and counters) - nonce <<= 32; - nonce >>= 16; - pblock->nNonce = ArithToUint256(nonce); + // If tx is dependent on other mempool transactions that haven't yet been + // included then put it in the waiting queue. + if (isStillDependent(iter)) { + waiting.push_back(iter); + continue; + } - uint32_t prevConsensusBranchId = CurrentEpochBranchId(pindexPrev->nHeight, chainparams.GetConsensus()); + // If this tx fits in the block add it, otherwise keep looping. + if (TestForBlock(iter)) { + AddToBlock(iter); + nBlockUnpaidActions += txUnpaidActions; - // Fill in header - pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_NU5)) { - // hashBlockCommitments depends on the block transactions, so we have to - // update it whenever the coinbase transaction changes. + // This tx was successfully added, so add waiting transactions that + // depend on this one to the cleared queue to try again. // - // - For the internal miner (either directly or via the `generate` RPC), this - // will occur in `IncrementExtraNonce()`, like for `hashMerkleRoot`. - // - For `getblocktemplate`, we have two sets of fields to handle: - // - The `defaultroots` fields, which contain both the default value (if - // nothing in the template is altered), and the roots that can be used to - // recalculate it (if some or all of the template is altered). - // - The legacy `finalsaplingroothash`, `lightclientroothash`, and - // `blockcommitmentshash` fields, which had the semantics of "place this - // value into the block header and things will work" (except for in - // v4.6.0 where they were accidentally set to always be the NU5 value). - // - // To accommodate all use cases, we calculate the `hashBlockCommitments` - // default value here (unlike `hashMerkleRoot`), and additionally cache the - // values necessary to recalculate it. - pblocktemplate->hashChainHistoryRoot = view.GetHistoryRoot(prevConsensusBranchId); - pblocktemplate->hashAuthDataRoot = pblock->BuildAuthDataMerkleTree(); - pblock->hashBlockCommitments = DeriveBlockCommitmentsHash( - pblocktemplate->hashChainHistoryRoot, - pblocktemplate->hashAuthDataRoot); - } else if (IsActivationHeight(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_HEARTWOOD)) { - pblocktemplate->hashChainHistoryRoot.SetNull(); - pblocktemplate->hashAuthDataRoot.SetNull(); - pblock->hashBlockCommitments.SetNull(); - } else if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD)) { - pblocktemplate->hashChainHistoryRoot = view.GetHistoryRoot(prevConsensusBranchId); - pblocktemplate->hashAuthDataRoot.SetNull(); - pblock->hashBlockCommitments = pblocktemplate->hashChainHistoryRoot; - } else { - pblocktemplate->hashChainHistoryRoot.SetNull(); - pblocktemplate->hashAuthDataRoot.SetNull(); - pblock->hashBlockCommitments = sapling_tree.root(); - } - UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); - pblock->nSolution.clear(); - pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); - - CValidationState state; - if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, true)) { - throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); + // TODO: This makes the overall algorithm O(n^2 log n) in the worst case + // of a linear dependency chain. (children is a std::map; its count method + // is O(log n) given the maximum number of children at each step, and is + // called O(n^2) times.) Daira conjectures that O(n log n) is possible. + auto children = mempool.GetMemPoolChildren(iter); + CTxMemPool::queueEntries stillWaiting; + for (CTxMemPool::txiter maybeChild : waiting) + { + if (children.count(maybeChild) > 0) { + cleared.push_back(maybeChild); + } else { + stillWaiting.push_back(maybeChild); + } + } + waiting.swap(stillWaiting); } } - - return pblocktemplate.release(); } + ////////////////////////////////////////////////////////////////////////////// // // Internal miner @@ -713,14 +767,12 @@ std::optional ExtractMinerAddress::operator()(const libzcash::Sapl std::optional ExtractMinerAddress::operator()(const libzcash::UnifiedAddress &addr) const { auto preferred = addr.GetPreferredRecipientAddress(consensus, height); if (preferred.has_value()) { - std::optional ret; - std::visit(match { - [&](const libzcash::OrchardRawAddress addr) { ret = MinerAddress(addr); }, - [&](const libzcash::SaplingPaymentAddress addr) { ret = MinerAddress(addr); }, - [&](const CKeyID keyID) { ret = operator()(keyID); }, - [&](const auto other) { ret = std::nullopt; } - }, preferred.value()); - return ret; + return examine(preferred.value(), match { + [&](const libzcash::OrchardRawAddress addr) -> std::optional { return MinerAddress(addr); }, + [&](const libzcash::SaplingPaymentAddress addr) -> std::optional { return MinerAddress(addr); }, + [&](const CKeyID keyID) -> std::optional { return operator()(keyID); }, + [&](const auto other) -> std::optional { return std::nullopt; } + }); } else { return std::nullopt; } @@ -791,9 +843,6 @@ static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainpar return error("ZcashMiner: generated block is stale"); } - // Inform about the new block - GetMainSignals().BlockFound(pblock->GetHash()); - // Process this block the same as if we had received it from another node CValidationState state; if (!ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL)) @@ -874,7 +923,7 @@ void static BitcoinMiner(const CChainParams& chainparams) continue; } - unique_ptr pblocktemplate(CreateNewBlock(chainparams, minerAddress)); + unique_ptr pblocktemplate(BlockAssembler(chainparams).CreateNewBlock(minerAddress)); if (!pblocktemplate.get()) { if (GetArg("-mineraddress", "").empty()) { @@ -957,37 +1006,12 @@ void static BitcoinMiner(const CChainParams& chainparams) // TODO: factor this out into a function with the same API for each solver. if (solver == "tromp") { - // Create solver and initialize it. - equi eq(1); - eq.setstate(curr_state.inner); - - // Initialization done, start algo driver. - eq.digit0(0); - eq.xfull = eq.bfull = eq.hfull = 0; - eq.showbsizes(0); - for (u32 r = 1; r < WK; r++) { - (r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0); - eq.xfull = eq.bfull = eq.hfull = 0; - eq.showbsizes(r); - } - eq.digitK(0); - ehSolverRuns.increment(); - - // Convert solution indices to byte array (decompress) and pass it to validBlock method. - for (size_t s = 0; s < std::min(MAXSOLS, eq.nsols); s++) { + std::function incrementRuns = [&]() { ehSolverRuns.increment(); }; + std::function&)> checkSolution = [&](size_t s, const std::vector& index_vector) { LogPrint("pow", "Checking solution %d\n", s+1); - std::vector index_vector(PROOFSIZE); - for (size_t i = 0; i < PROOFSIZE; i++) { - index_vector[i] = eq.sols[s][i]; - } - std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS); - - if (validBlock(sol_char)) { - // If we find a POW solution, do not try other solutions - // because they become invalid as we created a new block in blockchain. - break; - } - } + return validBlock(GetMinimalFromIndices(index_vector, DIGITBITS)); + }; + equihash_solve(curr_state.inner, incrementRuns, checkSolution); } else { try { // If we find a valid block, we rebuild diff --git a/depend/zcash/src/miner.h b/depend/zcash/src/miner.h index 742e1b47c..4707a1f70 100644 --- a/depend/zcash/src/miner.h +++ b/depend/zcash/src/miner.h @@ -8,8 +8,11 @@ #define BITCOIN_MINER_H #include "primitives/block.h" +#include "txmempool.h" +#include "weighted_map.h" #include +#include #include #include @@ -17,6 +20,7 @@ class CBlockIndex; class CChainParams; class CScript; + namespace Consensus { struct Params; }; static const bool DEFAULT_GENERATE = false; @@ -95,7 +99,70 @@ struct CBlockTemplate CMutableTransaction CreateCoinbaseTransaction(const CChainParams& chainparams, CAmount nFees, const MinerAddress& minerAddress, int nHeight); /** Generate a new block, without valid proof-of-work */ -CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddress& minerAddress, const std::optional& next_coinbase_mtx = std::nullopt); +class BlockAssembler +{ +private: + // The constructed block template + std::unique_ptr pblocktemplate; + // A convenience pointer that always refers to the CBlock in pblocktemplate + CBlock* pblock; + + // Configuration parameters for the block size and unpaid action limit + unsigned int nBlockMaxSize; + size_t nBlockUnpaidActionLimit; + + // Information on the current status of the block + uint64_t nBlockSize; + uint64_t nBlockTx; + unsigned int nBlockSigOps; + CAmount nFees; + CTxMemPool::setEntries inBlock; + + // Information on the current chain state after this block + CAmount sproutValue; + CAmount saplingValue; + CAmount orchardValue; + bool monitoring_pool_balances; + + // Chain context for the block + int nHeight; + int64_t nLockTimeCutoff; + const CChainParams& chainparams; + + // Variables used for addScoreTxs and addPriorityTxs + int lastFewTxs; + bool blockFinished; + +public: + BlockAssembler(const CChainParams& chainparams); + /** Construct a new block template with coinbase to minerAddress */ + CBlockTemplate* CreateNewBlock( + const MinerAddress& minerAddress, + const std::optional& next_coinbase_mtx = std::nullopt); + +private: + void constructZIP317BlockTemplate(); + void addTransactions( + CTxMemPool::weightedCandidates& candidates, + CTxMemPool::queueEntries& waiting, + CTxMemPool::queueEntries& cleared); + + // utility functions + /** Clear the block's state and prepare for assembling a new block */ + void resetBlock(const MinerAddress& minerAddress); + /** Add a tx to the block */ + void AddToBlock(CTxMemPool::txiter iter); + + // Methods for how to add transactions to a block. + /** Add transactions based on modified feerate */ + void addScoreTxs(); + + // helper function for addScoreTxs and addPriorityTxs + /** Test if tx will still "fit" in the block */ + bool TestForBlock(CTxMemPool::txiter iter); + /** Test if tx still has unconfirmed parents not yet in block */ + bool isStillDependent(CTxMemPool::txiter iter); +}; #ifdef ENABLE_MINING /** Get -mineraddress */ diff --git a/depend/zcash/src/net.cpp b/depend/zcash/src/net.cpp index 4086cc98b..9877b3a52 100644 --- a/depend/zcash/src/net.cpp +++ b/depend/zcash/src/net.cpp @@ -440,6 +440,14 @@ void CNode::CloseSocketDisconnect() CloseSocket(hSocket); } } + { + LOCK(cs_addrKnown); + addrKnown.reset(); + } + { + LOCK(cs_inventory); + filterInventoryKnown.reset(); + } // in case this fails, we'll empty the recv buffer when the CNode is deleted TRY_LOCK(cs_vRecvMsg, lockRecv); @@ -700,6 +708,9 @@ void CNode::copyStats(CNodeStats &stats) stats.dPingTime = (((double)nPingUsecTime) / 1e6); stats.dPingWait = (((double)nPingUsecWait) / 1e6); + stats.m_addr_processed = m_addr_processed.load(); + stats.m_addr_rate_limited = m_addr_rate_limited.load(); + // Leave string empty if addrLocal invalid (not filled in yet) CService addrLocalUnlocked = GetAddrLocal(); stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : ""; @@ -2221,7 +2232,6 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nSendOffset = 0; hashContinue = uint256(); nStartingHeight = -1; - filterInventoryKnown.reset(); fSendMempool = false; fGetAddr = false; nNextLocalAddrSend = 0; diff --git a/depend/zcash/src/net.h b/depend/zcash/src/net.h index cc2b9f35e..51b902202 100644 --- a/depend/zcash/src/net.h +++ b/depend/zcash/src/net.h @@ -50,6 +50,13 @@ static const int TIMEOUT_INTERVAL = 20 * 60; static const unsigned int MAX_INV_SZ = 50000; /** The maximum number of new addresses to accumulate before announcing. */ static const unsigned int MAX_ADDR_TO_SEND = 1000; +/** The maximum rate of address records we're willing to process on average. Can be bypassed using + * the NetPermissionFlags::Addr permission. */ +static constexpr double MAX_ADDR_RATE_PER_SECOND{0.1}; +/** The soft limit of the address processing token bucket (the regular MAX_ADDR_RATE_PER_SECOND + * based increments won't go above this, but the MAX_ADDR_TO_SEND increment following GETADDR + * is exempt from this limit. */ +static constexpr size_t MAX_ADDR_PROCESSING_TOKEN_BUCKET{MAX_ADDR_TO_SEND}; /** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */ static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; /** Maximum length of strSubVer in `version` message */ @@ -205,6 +212,8 @@ class CNodeStats double dPingTime; double dPingWait; std::string addrLocal; + uint64_t m_addr_processed{0}; + uint64_t m_addr_rate_limited{0}; }; @@ -304,6 +313,12 @@ class CNode CBloomFilter* pfilter; NodeId id; std::atomic nRefCount; + CRollingBloomFilter addrKnown; + mutable CCriticalSection cs_addrKnown; + + // Inventory based relay + // This filter is protected by cs_inventory and contains both txids and wtxids. + CRollingBloomFilter filterInventoryKnown; const uint64_t nKeyedNetGroup; @@ -333,22 +348,29 @@ class CNode // flood relay std::vector vAddrToSend; - CRollingBloomFilter addrKnown; bool fGetAddr; std::set setKnown; int64_t nNextAddrSend; int64_t nNextLocalAddrSend; - // inventory based relay - CRollingBloomFilter filterInventoryKnown; + /** Number of addr messages that can be processed from this peer. Start at 1 to + * permit self-announcement. */ + double m_addr_token_bucket{1.0}; + /** When m_addr_token_bucket was last updated */ + int64_t m_addr_token_timestamp{GetTimeMicros()}; + /** Total number of addresses that were dropped due to rate limiting. */ + std::atomic m_addr_rate_limited{0}; + /** Total number of addresses that were processed (excludes rate limited ones). */ + std::atomic m_addr_processed{0}; + // Set of transaction ids we still have to announce. // They are sorted by the mempool before relay, so the order is not important. std::set setInventoryTxToSend; - // List of block ids we still have announce. + // List of block ids we still have to announce. // There is no final sorting before sending, as they are always sent immediately // and in the order requested. std::vector vInventoryBlockToSend; - CCriticalSection cs_inventory; + mutable CCriticalSection cs_inventory; std::set setAskFor; std::multimap mapAskFor; int64_t nNextInvSend; @@ -448,10 +470,25 @@ class CNode } + bool AddAddressIfNotAlreadyKnown(const CAddress& addr) + { + LOCK(cs_addrKnown); + // Avoid adding to addrKnown after it has been reset in CloseSocketDisconnect. + if (fDisconnect) { + return false; + } + if (!addrKnown.contains(addr.GetKey())) { + addrKnown.insert(addr.GetKey()); + return true; + } else { + return false; + } + } - void AddAddressKnown(const CAddress& addr) + bool IsAddressKnown(const CAddress& addr) const { - addrKnown.insert(addr.GetKey()); + LOCK(cs_addrKnown); + return addrKnown.contains(addr.GetKey()); } void PushAddress(const CAddress& addr, FastRandomContext &insecure_rand) @@ -459,7 +496,7 @@ class CNode // Known checking here is only to save space from duplicates. // SendMessages will filter it again for knowns that were added // after addresses were pushed. - if (addr.IsValid() && !addrKnown.contains(addr.GetKey())) { + if (addr.IsValid() && !IsAddressKnown(addr)) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = addr; } else { @@ -469,18 +506,32 @@ class CNode } - void AddKnownTx(const WTxId& wtxid) + void AddKnownWTxId(const WTxId& wtxid) { - { - LOCK(cs_inventory); + LOCK(cs_inventory); + if (!fDisconnect) { filterInventoryKnown.insert(wtxid.ToBytes()); } } + void AddKnownTxId(const uint256& txid) + { + LOCK(cs_inventory); + if (!fDisconnect) { + filterInventoryKnown.insert(txid); + } + } + + bool HasKnownTxId(const uint256& txid) const + { + LOCK(cs_inventory); + return filterInventoryKnown.contains(txid); + } + void PushTxInventory(const WTxId& wtxid) { LOCK(cs_inventory); - if (!filterInventoryKnown.contains(wtxid.ToBytes())) { + if (!fDisconnect && !filterInventoryKnown.contains(wtxid.ToBytes())) { setInventoryTxToSend.insert(wtxid.hash); } } @@ -488,7 +539,9 @@ class CNode void PushBlockInventory(const uint256& hash) { LOCK(cs_inventory); - vInventoryBlockToSend.push_back(hash); + if (!fDisconnect) { + vInventoryBlockToSend.push_back(hash); + } } void AskFor(const CInv& inv); diff --git a/depend/zcash/src/policy/fees.cpp b/depend/zcash/src/policy/fees.cpp index 4b2908529..49fd4c293 100644 --- a/depend/zcash/src/policy/fees.cpp +++ b/depend/zcash/src/policy/fees.cpp @@ -12,10 +12,9 @@ #include "util/system.h" void TxConfirmStats::Initialize(std::vector& defaultBuckets, - unsigned int maxConfirms, double _decay, std::string _dataTypeString) + unsigned int maxConfirms, double _decay) { decay = _decay; - dataTypeString = _dataTypeString; buckets.insert(buckets.end(), defaultBuckets.begin(), defaultBuckets.end()); buckets.push_back(std::numeric_limits::infinity()); @@ -95,10 +94,10 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, int maxbucketindex = buckets.size() - 1; - // requireGreater means we are looking for the lowest fee/priority such that all higher - // values pass, so we start at maxbucketindex (highest fee) and look at succesively + // requireGreater means we are looking for the lowest feerate such that all higher + // values pass, so we start at maxbucketindex (highest feerate) and look at successively // smaller buckets until we reach failure. Otherwise, we are looking for the highest - // fee/priority such that all lower values fail, and we go in the opposite direction. + // feerate such that all lower values fail, and we go in the opposite direction. unsigned int startbucket = requireGreater ? maxbucketindex : 0; int step = requireGreater ? -1 : 1; @@ -115,7 +114,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, bool foundAnswer = false; unsigned int bins = unconfTxs.size(); - // Start counting from highest(default) or lowest fee/pri transactions + // Start counting from highest(default) or lowest feerate transactions for (int bucket = startbucket; bucket >= 0 && bucket <= maxbucketindex; bucket += step) { curFarBucket = bucket; nConf += confAvg[confTarget - 1][bucket]; @@ -153,8 +152,8 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, double median = -1; double txSum = 0; - // Calculate the "average" fee of the best bucket range that met success conditions - // Find the bucket with the median transaction and then report the average fee from that bucket + // Calculate the "average" feerate of the best bucket range that met success conditions + // Find the bucket with the median transaction and then report the average feerate from that bucket // This is a compromise between finding the median which we can't since we don't save all tx's // and reporting the average which is less accurate unsigned int minBucket = bestNearBucket < bestFarBucket ? bestNearBucket : bestFarBucket; @@ -174,8 +173,8 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, } } - LogPrint("estimatefee", "%3d: For conf success %s %4.2f need %s %s: %12.5g from buckets %8g - %8g Cur Bucket stats %6.2f%% %8.1f/(%.1f+%d mempool)\n", - confTarget, requireGreater ? ">" : "<", successBreakPoint, dataTypeString, + LogPrint("estimatefee", "%3d: For conf success %s %4.2f need feerate %s: %12.5g from buckets %8g - %8g Cur Bucket stats %6.2f%% %8.1f/(%.1f+%d mempool)\n", + confTarget, requireGreater ? ">" : "<", successBreakPoint, requireGreater ? ">" : "<", median, buckets[minBucket], buckets[maxBucket], 100 * nConf / (totalNum + extraNum), nConf, totalNum, extraNum); @@ -208,10 +207,10 @@ void TxConfirmStats::Read(CAutoFile& filein) filein >> fileBuckets; numBuckets = fileBuckets.size(); if (numBuckets <= 1 || numBuckets > 1000) - throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 fee/pri buckets"); + throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets"); filein >> fileAvg; if (fileAvg.size() != numBuckets) - throw std::runtime_error("Corrupt estimates file. Mismatch in fee/pri average bucket count"); + throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count"); filein >> fileTxCtAvg; if (fileTxCtAvg.size() != numBuckets) throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count"); @@ -221,9 +220,9 @@ void TxConfirmStats::Read(CAutoFile& filein) throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms"); for (unsigned int i = 0; i < maxConfirms; i++) { if (fileConfAvg[i].size() != numBuckets) - throw std::runtime_error("Corrupt estimates file. Mismatch in fee/pri conf average bucket count"); + throw std::runtime_error("Corrupt estimates file. Mismatch in feerate conf average bucket count"); } - // Now that we've processed the entire fee estimate data file and not + // Now that we've processed the entire feerate estimate data file and not // thrown any errors, we can copy it to our data structures decay = fileDecay; buckets = fileBuckets; @@ -250,8 +249,8 @@ void TxConfirmStats::Read(CAutoFile& filein) for (unsigned int i = 0; i < buckets.size(); i++) bucketMap[buckets[i]] = i; - LogPrint("estimatefee", "Reading estimates: %u %s buckets counting confirms up to %u blocks\n", - numBuckets, dataTypeString, maxConfirms); + LogPrint("estimatefee", "Reading estimates: %u buckets counting confirms up to %u blocks\n", + numBuckets, maxConfirms); } unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val) @@ -259,7 +258,6 @@ unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val) unsigned int bucketindex = FindBucketIndex(val); unsigned int blockIndex = nBlockHeight % unconfTxs.size(); unconfTxs[blockIndex][bucketindex]++; - LogPrint("estimatefee", "adding to %s", dataTypeString); return bucketindex; } @@ -299,12 +297,10 @@ void CBlockPolicyEstimator::removeTx(uint256 hash) hash.ToString().c_str()); return; } - TxConfirmStats *stats = pos->second.stats; unsigned int entryHeight = pos->second.blockHeight; unsigned int bucketIndex = pos->second.bucketIndex; - if (stats != NULL) - stats->removeTx(entryHeight, nBestSeenHeight, bucketIndex); + feeStats.removeTx(entryHeight, nBestSeenHeight, bucketIndex); mapMemPoolTxs.erase(hash); } @@ -316,47 +312,17 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee) for (double bucketBoundary = minTrackedFee.GetFeePerK(); bucketBoundary <= MAX_FEERATE; bucketBoundary *= FEE_SPACING) { vfeelist.push_back(bucketBoundary); } - feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "FeeRate"); - - minTrackedPriority = AllowFreeThreshold() < MIN_PRIORITY ? MIN_PRIORITY : AllowFreeThreshold(); - std::vector vprilist; - for (double bucketBoundary = minTrackedPriority; bucketBoundary <= MAX_PRIORITY; bucketBoundary *= PRI_SPACING) { - vprilist.push_back(bucketBoundary); - } - priStats.Initialize(vprilist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "Priority"); - - feeUnlikely = CFeeRate(0); - feeLikely = CFeeRate(INF_FEERATE); - priUnlikely = 0; - priLikely = INF_PRIORITY; -} - -bool CBlockPolicyEstimator::isFeeDataPoint(const CFeeRate &fee, double pri) -{ - if ((pri < minTrackedPriority && fee >= minTrackedFee) || - (pri < priUnlikely && fee > feeLikely)) { - return true; - } - return false; -} - -bool CBlockPolicyEstimator::isPriDataPoint(const CFeeRate &fee, double pri) -{ - if ((fee < minTrackedFee && pri >= minTrackedPriority) || - (fee < feeUnlikely && pri > priLikely)) { - return true; - } - return false; + feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY); } void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool fCurrentEstimate) { unsigned int txHeight = entry.GetHeight(); uint256 hash = entry.GetTx().GetHash(); - if (mapMemPoolTxs[hash].stats != NULL) { + if (mapMemPoolTxs.count(hash)) { LogPrint("estimatefee", "Blockpolicy error mempool tx %s already being tracked\n", hash.ToString().c_str()); - return; + return; } if (txHeight < nBestSeenHeight) { @@ -377,30 +343,11 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo return; } - // Fees are stored and reported as BTC-per-kb: + // Feerates are stored and reported as zatoshis per 1000 bytes: CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); - // Want the priority of the tx at confirmation. However we don't know - // what that will be and its too hard to continue updating it - // so use starting priority as a proxy - double curPri = entry.GetPriority(txHeight); mapMemPoolTxs[hash].blockHeight = txHeight; - - LogPrint("estimatefee", "Blockpolicy mempool tx %s ", hash.ToString().substr(0,10)); - // Record this as a priority estimate - if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) { - mapMemPoolTxs[hash].stats = &priStats; - mapMemPoolTxs[hash].bucketIndex = priStats.NewTx(txHeight, curPri); - } - // Record this as a fee estimate - else if (isFeeDataPoint(feeRate, curPri)) { - mapMemPoolTxs[hash].stats = &feeStats; - mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK()); - } - else { - LogPrint("estimatefee", "not adding"); - } - LogPrint("estimatefee", "\n"); + mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK()); } void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry) @@ -423,21 +370,10 @@ void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM return; } - // Fees are stored and reported as BTC-per-kb: + // Feerates are stored and reported as zatoshis per 1000 bytes: CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); - // Want the priority of the tx at confirmation. The priority when it - // entered the mempool could easily be very small and change quickly - double curPri = entry.GetPriority(nBlockHeight); - - // Record this as a priority estimate - if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) { - priStats.Record(blocksToConfirm, curPri); - } - // Record this as a fee estimate - else if (isFeeDataPoint(feeRate, curPri)) { - feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK()); - } + feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK()); } void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, @@ -458,41 +394,15 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, if (!fCurrentEstimate) return; - // Update the dynamic cutoffs - // a fee/priority is "likely" the reason your tx was included in a block if >85% of such tx's - // were confirmed in 2 blocks and is "unlikely" if <50% were confirmed in 10 blocks - LogPrint("estimatefee", "Blockpolicy recalculating dynamic cutoffs:\n"); - priLikely = priStats.EstimateMedianVal(2, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBlockHeight); - if (priLikely == -1) - priLikely = INF_PRIORITY; - - double feeLikelyEst = feeStats.EstimateMedianVal(2, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBlockHeight); - if (feeLikelyEst == -1) - feeLikely = CFeeRate(INF_FEERATE); - else - feeLikely = CFeeRate(feeLikelyEst); - - priUnlikely = priStats.EstimateMedianVal(10, SUFFICIENT_PRITXS, UNLIKELY_PCT, false, nBlockHeight); - if (priUnlikely == -1) - priUnlikely = 0; - - double feeUnlikelyEst = feeStats.EstimateMedianVal(10, SUFFICIENT_FEETXS, UNLIKELY_PCT, false, nBlockHeight); - if (feeUnlikelyEst == -1) - feeUnlikely = CFeeRate(0); - else - feeUnlikely = CFeeRate(feeUnlikelyEst); - - // Clear the current block states + // Clear the current block state feeStats.ClearCurrent(nBlockHeight); - priStats.ClearCurrent(nBlockHeight); // Repopulate the current block states for (unsigned int i = 0; i < entries.size(); i++) processBlockTx(nBlockHeight, entries[i]); - // Update all exponential averages with the current block states + // Update all exponential averages with the current block state feeStats.UpdateMovingAverages(); - priStats.UpdateMovingAverages(); LogPrint("estimatefee", "Blockpolicy after updating estimates for %u confirmed entries, new mempool map size %u\n", entries.size(), mapMemPoolTxs.size()); @@ -512,27 +422,20 @@ CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) return CFeeRate(median); } -double CBlockPolicyEstimator::estimatePriority(int confTarget) -{ - // Return failure if trying to analyze a target we're not tracking - if (confTarget <= 0 || (unsigned int)confTarget > priStats.GetMaxConfirms()) - return -1; - - return priStats.EstimateMedianVal(confTarget, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight); -} - void CBlockPolicyEstimator::Write(CAutoFile& fileout) { fileout << nBestSeenHeight; feeStats.Write(fileout); - priStats.Write(fileout); } -void CBlockPolicyEstimator::Read(CAutoFile& filein) +void CBlockPolicyEstimator::Read(CAutoFile& filein, int nFileVersion) { int nFileBestSeenHeight; filein >> nFileBestSeenHeight; feeStats.Read(filein); - priStats.Read(filein); nBestSeenHeight = nFileBestSeenHeight; + if (nFileVersion < FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION) { + TxConfirmStats priStats; + priStats.Read(filein); + } } diff --git a/depend/zcash/src/policy/fees.h b/depend/zcash/src/policy/fees.h index 383d629d1..1618ec4c8 100644 --- a/depend/zcash/src/policy/fees.h +++ b/depend/zcash/src/policy/fees.h @@ -19,51 +19,42 @@ class CFeeRate; class CTxMemPoolEntry; /** \class CBlockPolicyEstimator - * The BlockPolicyEstimator is used for estimating the fee or priority needed + * The BlockPolicyEstimator is used for estimating the feerate needed * for a transaction to be included in a block within a certain number of * blocks. * * At a high level the algorithm works by grouping transactions into buckets - * based on having similar priorities or fees and then tracking how long it + * based on having similar feerates and then tracking how long it * takes transactions in the various buckets to be mined. It operates under - * the assumption that in general transactions of higher fee/priority will be - * included in blocks before transactions of lower fee/priority. So for - * example if you wanted to know what fee you should put on a transaction to + * the assumption that in general transactions of higher feerate will be + * included in blocks before transactions of lower feerate. So for + * example if you wanted to know what feerate you should put on a transaction to * be included in a block within the next 5 blocks, you would start by looking - * at the bucket with the highest fee transactions and verifying that a + * at the bucket with the highest feerate transactions and verifying that a * sufficiently high percentage of them were confirmed within 5 blocks and - * then you would look at the next highest fee bucket, and so on, stopping at - * the last bucket to pass the test. The average fee of transactions in this - * bucket will give you an indication of the lowest fee you can put on a + * then you would look at the next highest feerate bucket, and so on, stopping at + * the last bucket to pass the test. The average feerate of transactions in this + * bucket will give you an indication of the lowest feerate you can put on a * transaction and still have a sufficiently high chance of being confirmed * within your desired 5 blocks. * - * When a transaction enters the mempool or is included within a block we - * decide whether it can be used as a data point for fee estimation, priority - * estimation or neither. If the value of exactly one of those properties was - * below the required minimum it can be used to estimate the other. In - * addition, if a priori our estimation code would indicate that the - * transaction would be much more quickly included in a block because of one - * of the properties compared to the other, we can also decide to use it as - * an estimate for that property. - * - * Here is a brief description of the implementation for fee estimation. - * When a transaction that counts for fee estimation enters the mempool, we + * Here is a brief description of the implementation: + * When a transaction enters the mempool, we * track the height of the block chain at entry. Whenever a block comes in, - * we count the number of transactions in each bucket and the total amount of fee + * we count the number of transactions in each bucket and the total amount of feerate * paid in each bucket. Then we calculate how many blocks Y it took each * transaction to be mined and we track an array of counters in each bucket * for how long it to took transactions to get confirmed from 1 to a max of 25 * and we increment all the counters from Y up to 25. This is because for any * number Z>=Y the transaction was successfully mined within Z blocks. We * want to save a history of this information, so at any time we have a - * counter of the total number of transactions that happened in a given fee + * counter of the total number of transactions that happened in a given feerate * bucket and the total number that were confirmed in each number 1-25 blocks * or less for any bucket. We save this history by keeping an exponentially * decaying moving average of each one of these stats. Furthermore we also * keep track of the number unmined (in mempool) transactions in each bucket * and for how many blocks they have been outstanding and use that to increase - * the number of transactions we've seen in that fee bucket when calculating + * the number of transactions we've seen in that feerate bucket when calculating * an estimate for any number of confirmations below the number of blocks * they've been outstanding. */ @@ -72,12 +63,11 @@ class CTxMemPoolEntry; static const double DEFAULT_DECAY = .998; /** - * We will instantiate two instances of this class, one to track transactions - * that were included in a block due to fee, and one for txs included due to - * priority. We will lump transactions into a bucket according to their approximate - * fee or priority and then track how long it took for those txs to be included + * We will instantiate an instance of this class to track transactions that were + * included in a block. We will lump transactions into a bucket according to their + * approximate feerate and then track how long it took for those txs to be included * in a block. There is always a bucket into which any given double value - * (representing a fee or priority) falls. + * (representing a fee) falls. * * The tracking of unconfirmed (mempool) transactions is completely independent of the * historical tracking of transactions that have been confirmed in a block. @@ -85,7 +75,7 @@ static const double DEFAULT_DECAY = .998; class TxConfirmStats { private: - //Define the buckets we will group transactions into (both fee buckets and priority buckets) + //Define the buckets we will group transactions into std::vector buckets; // The upper-bound of the range for the bucket (inclusive) std::map bucketMap; // Map of bucket upper-bound to index into all vectors by bucket @@ -102,16 +92,15 @@ class TxConfirmStats // and calculate the totals for the current block to update the moving averages std::vector > curBlockConf; // curBlockConf[Y][X] - // Sum the total priority/fee of all txs in each bucket + // Sum the total feerate of all tx's in each bucket // Track the historical moving average of this total over blocks std::vector avg; // and calculate the total for the current block to update the moving average std::vector curBlockVal; // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X - // Combine the total value with the tx counts to calculate the avg fee/priority per bucket + // Combine the total value with the tx counts to calculate the avg feerate per bucket - std::string dataTypeString; double decay = DEFAULT_DECAY; // Mempool counts of outstanding transactions @@ -133,9 +122,8 @@ class TxConfirmStats * @param defaultBuckets contains the upper limits for the bucket boundaries * @param maxConfirms max number of confirms to track * @param decay how much to decay the historical moving average per block - * @param dataTypeString for logging purposes */ - void Initialize(std::vector& defaultBuckets, unsigned int maxConfirms, double decay, std::string dataTypeString); + void Initialize(std::vector& defaultBuckets, unsigned int maxConfirms, double decay); /** Clear the state of the curBlock variables to start counting for the new block */ void ClearCurrent(unsigned int nBlockHeight); @@ -143,7 +131,7 @@ class TxConfirmStats /** * Record a new transaction data point in the current block stats * @param blocksToConfirm the number of blocks it took this transaction to confirm - * @param val either the fee or the priority when entered of the transaction + * @param val the feerate of the transaction * @warning blocksToConfirm is 1-based and has to be >= 1 */ void Record(int blocksToConfirm, double val); @@ -160,14 +148,14 @@ class TxConfirmStats void UpdateMovingAverages(); /** - * Calculate a fee or priority estimate. Find the lowest value bucket (or range of buckets + * Calculate a feerate estimate. Find the lowest value bucket (or range of buckets * to make sure we have enough data points) whose transactions still have sufficient likelihood * of being confirmed within the target number of confirmations * @param confTarget target number of confirmations * @param sufficientTxVal required average number of transactions per block in a bucket range * @param minSuccess the success probability we require - * @param requireGreater return the lowest fee/pri such that all higher values pass minSuccess OR - * return the highest fee/pri such that all lower values fail minSuccess + * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR + * return the highest feerate such that all lower values fail minSuccess * @param nBlockHeight the current block height */ double EstimateMedianVal(int confTarget, double sufficientTxVal, @@ -191,35 +179,26 @@ class TxConfirmStats /** Track confirm delays up to 25 blocks, can't estimate beyond that */ static const unsigned int MAX_BLOCK_CONFIRMS = 25; -/** Require greater than 85% of X fee transactions to be confirmed within Y blocks for X to be big enough */ +/** Require greater than 85% of X feerate transactions to be confirmed within Y blocks for X to be big enough */ static const double MIN_SUCCESS_PCT = .85; static const double UNLIKELY_PCT = .5; -/** Require an avg of 1 tx in the combined fee bucket per block to have stat significance */ +/** Require an avg of 1 tx in the combined feerate bucket per block to have stat significance */ static const double SUFFICIENT_FEETXS = 1; -/** Require only an avg of 1 tx every 5 blocks in the combined pri bucket (way less pri txs) */ -static const double SUFFICIENT_PRITXS = .2; - -// Minimum and Maximum values for tracking fees and priorities +// Minimum and Maximum values for tracking feerates static const double MIN_FEERATE = 10; static const double MAX_FEERATE = 1e7; static const double INF_FEERATE = MAX_MONEY; -static const double MIN_PRIORITY = 10; -static const double MAX_PRIORITY = 1e16; -static const double INF_PRIORITY = 1e9 * MAX_MONEY; -// We have to lump transactions into buckets based on fee or priority, but we want to be able -// to give accurate estimates over a large range of potential fees and priorities +// We have to lump transactions into buckets based on feerate, but we want to be able +// to give accurate estimates over a large range of potential feerates // Therefore it makes sense to exponentially space the buckets /** Spacing of FeeRate buckets */ static const double FEE_SPACING = 1.1; -/** Spacing of Priority buckets */ -static const double PRI_SPACING = 2; - /** - * We want to be able to estimate fees or priorities that are needed on txs to be included in + * We want to be able to estimate feerates that are needed on tx's to be included in * a certain number of blocks. Every time a block is added to the best chain, this class records * stats on the transactions included in that block */ @@ -242,44 +221,29 @@ class CBlockPolicyEstimator /** Remove a transaction from the mempool tracking stats*/ void removeTx(uint256 hash); - /** Is this transaction likely included in a block because of its fee?*/ - bool isFeeDataPoint(const CFeeRate &fee, double pri); - - /** Is this transaction likely included in a block because of its priority?*/ - bool isPriDataPoint(const CFeeRate &fee, double pri); - - /** Return a fee estimate */ + /** Return a feerate estimate */ CFeeRate estimateFee(int confTarget); - /** Return a priority estimate */ - double estimatePriority(int confTarget); - /** Write estimation data to a file */ void Write(CAutoFile& fileout); /** Read estimation data from a file */ - void Read(CAutoFile& filein); + void Read(CAutoFile& filein, int nFileVersion); private: CFeeRate minTrackedFee; //!< Passed to constructor to avoid dependency on main - double minTrackedPriority; //!< Set to AllowFreeThreshold unsigned int nBestSeenHeight; struct TxStatsInfo { - TxConfirmStats *stats; unsigned int blockHeight; unsigned int bucketIndex; - TxStatsInfo() : stats(NULL), blockHeight(0), bucketIndex(0) {} + TxStatsInfo() : blockHeight(0), bucketIndex(0) {} }; // map of txids to information about that transaction std::map mapMemPoolTxs; /** Classes to track historical data on transaction confirmations */ - TxConfirmStats feeStats, priStats; - - /** Breakpoints to help determine whether a transaction was confirmed by priority or Fee */ - CFeeRate feeLikely, feeUnlikely; - double priLikely, priUnlikely; + TxConfirmStats feeStats; }; #endif // BITCOIN_POLICY_FEES_H diff --git a/depend/zcash/src/policy/policy.cpp b/depend/zcash/src/policy/policy.cpp index 967801e78..e44f9ac23 100644 --- a/depend/zcash/src/policy/policy.cpp +++ b/depend/zcash/src/policy/policy.cpp @@ -119,7 +119,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const CChainParam else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { reason = "bare-multisig"; return false; - } else if (txout.IsDust(::minRelayTxFee)) { + } else if (txout.IsDust()) { reason = "dust"; return false; } diff --git a/depend/zcash/src/policy/policy.h b/depend/zcash/src/policy/policy.h index eb635b7c8..2bf61c915 100644 --- a/depend/zcash/src/policy/policy.h +++ b/depend/zcash/src/policy/policy.h @@ -15,11 +15,9 @@ class CChainParams; class CCoinsViewCache; -/** Default for -blockmaxsize and -blockminsize, which control the range of sizes the mining code will create **/ +/** Default for -blockmaxsize, which controls the maximum block size the mining code will create **/ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = MAX_BLOCK_SIZE; -static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0; -/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ -static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = DEFAULT_BLOCK_MAX_SIZE / 2; + /** Maximum number of signature check operations in an IsStandard() P2SH script */ static const unsigned int MAX_P2SH_SIGOPS = 15; /** The maximum number of sigops we're willing to relay/mine in a single tx */ @@ -42,9 +40,56 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY /** For convenience, standard but not mandatory verify flags. */ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; +/** + * The rate used to calculate the dust threshold, in zatoshis per 1000 bytes. + * This will effectively be multiplied by 3 (it is necessary to express it this + * way to ensure that rounding will be calculated the same as before #6542). + * + * Bitcoin Core added the concept of "dust" in bitcoin/bitcoin#2577. At that + * point the dust threshold rate was tied to three times the minRelayTxFee rate, + * with the motivation that if you'd pay more than a third of the minimum relay + * fee to spend something, it should be considered dust. This was implemented + * as a standard rule rejecting dust outputs. + * + * This motivation will not apply after ZIP 317 block construction is implemented: + * at that point the ZIP 317 marginal fee will be 5000 zats per logical action, + * but the dust threshold rate will still be 300 zats per 1000 bytes. Those costs + * would only coincide if the marginal size per logical action were + * 5000/300 * 1000 ~= 16667 bytes, and in practice the marginal size for any + * kind of input is much smaller than that. + * + * However, to avoid interoperability problems (older wallets creating transactions + * that newer nodes will reject because they view the outputs as dust), we will have + * to coordinate any increase in the dust threshold carefully. + * + * More history: in Zcash the minRelayTxFee rate was 5000 zats/1000 bytes at launch, + * changed to 1000 zats/1000 bytes in zcashd v1.0.3 and to 100 zats/1000 bytes in + * zcashd v1.0.7-1 (#2141). The relaying problem for shielded transactions (#1969) + * that prompted the latter change was fixed more thoroughly by the addition of + * `CFeeRate::GetFeeForRelay` in #4916, ensuring that a transaction paying + * `DEFAULT_FEE` can always be relayed. At the same time the default fee was set + * to 1000 zats, per ZIP 313. + * + * #6542 changed relaying policy to be more strict about enforcing minRelayTxFee. + * It also allowed `-minrelaytxfee=0`, which we are using to avoid some test + * breakage. But if the dust threshold rate were still set to three times the + * minRelayTxFee rate, then setting `-minrelaytxfee=0` would have the side effect + * of setting the dust threshold to zero, which is not intended. + * + * Bitcoin Core took a different approach to disentangling the dust threshold from + * the relay threshold, adding a `-dustrelayfee` option (bitcoin/bitcoin#9380). + * We don't want to do that because it is likely that we will change the dust policy + * again, and adding a user-visible config option might conflict with that. Also, + * it isn't a good idea for the dust threshold rate to be configurable per node; + * it's a standard rule parameter and should only be changed with network-wide + * coordination (if it is increased then wallets have to change before nodes, and + * vice versa if it is decreased). So for now we set it to a constant that matches + * the behaviour before #6542. + */ +static const unsigned int ONE_THIRD_DUST_THRESHOLD_RATE = 100; + // Sanity check the magic numbers when we change them static_assert(DEFAULT_BLOCK_MAX_SIZE <= MAX_BLOCK_SIZE); -static_assert(DEFAULT_BLOCK_PRIORITY_SIZE <= DEFAULT_BLOCK_MAX_SIZE); bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); /** diff --git a/depend/zcash/src/pow/tromp/equi.h b/depend/zcash/src/pow/tromp/equi.h index 8df0f42ef..0f829f132 100644 --- a/depend/zcash/src/pow/tromp/equi.h +++ b/depend/zcash/src/pow/tromp/equi.h @@ -1,5 +1,5 @@ // Equihash solver -// Copyright (c) 2016-2016 John Tromp, The Zcash developers +// Copyright (c) 2016-2023 John Tromp, The Zcash developers #ifndef ZCASH_POW_TROMP_EQUI_H #define ZCASH_POW_TROMP_EQUI_H @@ -43,64 +43,19 @@ typedef u32 proof[PROOFSIZE]; enum verify_code { POW_OK, POW_DUPLICATE, POW_OUT_OF_ORDER, POW_NONZERO_XOR }; -const char *errstr[] = { "OK", "duplicate index", "indices out of order", "nonzero xor" }; - -void genhash(const rust::Box& ctx, u32 idx, uchar *hash) { - auto state = ctx->box_clone(); - u32 leb = htole32(idx / HASHESPERBLAKE); - state->update({(const uchar *)&leb, sizeof(u32)}); - uchar blakehash[HASHOUT]; - state->finalize({blakehash, HASHOUT}); - memcpy(hash, blakehash + (idx % HASHESPERBLAKE) * WN/8, WN/8); -} - -int verifyrec(const rust::Box& ctx, u32 *indices, uchar *hash, int r) { - if (r == 0) { - genhash(ctx, *indices, hash); - return POW_OK; - } - u32 *indices1 = indices + (1 << (r-1)); - if (*indices >= *indices1) - return POW_OUT_OF_ORDER; - uchar hash0[WN/8], hash1[WN/8]; - int vrf0 = verifyrec(ctx, indices, hash0, r-1); - if (vrf0 != POW_OK) - return vrf0; - int vrf1 = verifyrec(ctx, indices1, hash1, r-1); - if (vrf1 != POW_OK) - return vrf1; - for (int i=0; i < WN/8; i++) - hash[i] = hash0[i] ^ hash1[i]; - int i, b = r * DIGITBITS; - for (i = 0; i < b/8; i++) - if (hash[i]) - return POW_NONZERO_XOR; - if ((b%8) && hash[i] >> (8-(b%8))) - return POW_NONZERO_XOR; - return POW_OK; -} - -int compu32(const void *pa, const void *pb) { - u32 a = *(u32 *)pa, b = *(u32 *)pb; - return a& ctx, u32 idx, uchar *hash); +int verifyrec(const rust::Box& ctx, u32 *indices, uchar *hash, int r); +int compu32(const void *pa, const void *pb); +bool duped(proof prf); // verify Wagner conditions -int verify(u32 indices[PROOFSIZE], const rust::Box ctx) { - if (duped(indices)) - return POW_DUPLICATE; - uchar hash[WN/8]; - return verifyrec(ctx, indices, hash, WK); -} +int verify(u32 indices[PROOFSIZE], const rust::Box ctx); + +bool equihash_solve( + const rust::Box& curr_state, + std::function& incrementRuns, + std::function&)>& checkSolution); #endif // ZCASH_POW_TROMP_EQUI_H diff --git a/depend/zcash/src/pow/tromp/equi_miner.h b/depend/zcash/src/pow/tromp/equi_miner.h index fe4df3cb1..448b4ba02 100644 --- a/depend/zcash/src/pow/tromp/equi_miner.h +++ b/depend/zcash/src/pow/tromp/equi_miner.h @@ -1,5 +1,5 @@ // Equihash solver -// Copyright (c) 2016 John Tromp, The Zcash developers +// Copyright (c) 2016-2023 John Tromp, The Zcash developers // Fix N, K, such that n = N/(k+1) is integer // Fix M = 2^{n+1} hashes each of length N bits, @@ -75,6 +75,8 @@ static const u32 NBLOCKS = (NHASHES+HASHESPERBLAKE-1)/HASHESPERBLAKE; // nothing larger found in 100000 runs static const u32 MAXSOLS = 8; +const char *errstr[] = { "OK", "duplicate index", "indices out of order", "nonzero xor" }; + // tree node identifying its children as two different slots in // a bucket on previous layer with the same rest bits (x-tra hash) struct tree { @@ -646,4 +648,98 @@ void *worker(void *vp) { return 0; } +void genhash(const rust::Box& ctx, u32 idx, uchar *hash) { + auto state = ctx->box_clone(); + u32 leb = htole32(idx / HASHESPERBLAKE); + state->update({(const uchar *)&leb, sizeof(u32)}); + uchar blakehash[HASHOUT]; + state->finalize({blakehash, HASHOUT}); + memcpy(hash, blakehash + (idx % HASHESPERBLAKE) * WN/8, WN/8); +} + +int verifyrec(const rust::Box& ctx, u32 *indices, uchar *hash, int r) { + if (r == 0) { + genhash(ctx, *indices, hash); + return POW_OK; + } + u32 *indices1 = indices + (1 << (r-1)); + if (*indices >= *indices1) + return POW_OUT_OF_ORDER; + uchar hash0[WN/8], hash1[WN/8]; + int vrf0 = verifyrec(ctx, indices, hash0, r-1); + if (vrf0 != POW_OK) + return vrf0; + int vrf1 = verifyrec(ctx, indices1, hash1, r-1); + if (vrf1 != POW_OK) + return vrf1; + for (int i=0; i < WN/8; i++) + hash[i] = hash0[i] ^ hash1[i]; + int i, b = r * DIGITBITS; + for (i = 0; i < b/8; i++) + if (hash[i]) + return POW_NONZERO_XOR; + if ((b%8) && hash[i] >> (8-(b%8))) + return POW_NONZERO_XOR; + return POW_OK; +} + +int compu32(const void *pa, const void *pb) { + u32 a = *(u32 *)pa, b = *(u32 *)pb; + return a ctx) { + if (duped(indices)) + return POW_DUPLICATE; + uchar hash[WN/8]; + return verifyrec(ctx, indices, hash, WK); +} + +bool equihash_solve( + const rust::Box& curr_state, + std::function& incrementRuns, + std::function&)>& checkSolution) +{ + // Create solver and initialize it. + equi eq(1); + eq.setstate(curr_state); + + // Initialization done, start algo driver. + eq.digit0(0); + eq.xfull = eq.bfull = eq.hfull = 0; + eq.showbsizes(0); + for (u32 r = 1; r < WK; r++) { + (r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0); + eq.xfull = eq.bfull = eq.hfull = 0; + eq.showbsizes(r); + } + eq.digitK(0); + incrementRuns(); + + // Decompress solution indices and pass to checkSolution. + for (size_t s = 0; s < std::min(MAXSOLS, eq.nsols); s++) { + std::vector index_vector(PROOFSIZE); + for (size_t i = 0; i < PROOFSIZE; i++) { + index_vector[i] = eq.sols[s][i]; + } + if (checkSolution(s, index_vector)) { + // If we find a POW solution, do not try other solutions + // because they become invalid as we created a new block in blockchain. + return true; + } + } + return false; +} + #endif // ZCASH_POW_TROMP_EQUI_MINER_H diff --git a/depend/zcash/src/primitives/orchard.h b/depend/zcash/src/primitives/orchard.h index 644915e5a..20b6d99a5 100644 --- a/depend/zcash/src/primitives/orchard.h +++ b/depend/zcash/src/primitives/orchard.h @@ -6,10 +6,11 @@ #define ZCASH_PRIMITIVES_ORCHARD_H #include "streams.h" +#include "streams_rust.h" #include -#include -#include + +#include #include #include "zcash/address/orchard.hpp" @@ -23,22 +24,22 @@ namespace orchard { class UnauthorizedBundle; } class OrchardBundle { private: - /// An optional Orchard bundle (with `nullptr` corresponding to `None`). + /// An optional Orchard bundle. /// Memory is allocated by Rust. - std::unique_ptr inner; + rust::Box inner; - OrchardBundle(OrchardBundlePtr* bundle) : inner(bundle, orchard_bundle_free) {} + OrchardBundle(OrchardBundlePtr* bundle) : inner(orchard_bundle::from_raw_box(bundle)) {} friend class OrchardMerkleFrontier; friend class OrchardWallet; friend class orchard::UnauthorizedBundle; public: - OrchardBundle() : inner(nullptr, orchard_bundle_free) {} + OrchardBundle() : inner(orchard_bundle::none()) {} OrchardBundle(OrchardBundle&& bundle) : inner(std::move(bundle.inner)) {} OrchardBundle(const OrchardBundle& bundle) : - inner(orchard_bundle_clone(bundle.inner.get()), orchard_bundle_free) {} + inner(bundle.inner->box_clone()) {} OrchardBundle& operator=(OrchardBundle&& bundle) { @@ -51,87 +52,88 @@ class OrchardBundle OrchardBundle& operator=(const OrchardBundle& bundle) { if (this != &bundle) { - inner.reset(orchard_bundle_clone(bundle.inner.get())); + inner = bundle.inner->box_clone(); } return *this; } - rust::Box GetDetails() const { - return orchard_bundle::from_tx_bundle(reinterpret_cast(inner.get())); + const rust::Box& GetDetails() const { + return inner; } size_t RecursiveDynamicUsage() const { - return orchard_bundle_recursive_dynamic_usage(inner.get()); + return inner->recursive_dynamic_usage(); } template void Serialize(Stream& s) const { - RustStream rs(s); - if (!orchard_bundle_serialize(inner.get(), &rs, RustStream::write_callback)) { - throw std::ios_base::failure("Failed to serialize v5 Orchard bundle"); + try { + inner->serialize(*ToRustStream(s)); + } catch (const std::exception& e) { + throw std::ios_base::failure(e.what()); } } template void Unserialize(Stream& s) { - RustStream rs(s); - OrchardBundlePtr* bundle; - if (!orchard_bundle_parse(&rs, RustStream::read_callback, &bundle)) { - throw std::ios_base::failure("Failed to parse v5 Orchard bundle"); + try { + inner = orchard_bundle::parse(*ToRustStream(s)); + } catch (const std::exception& e) { + throw std::ios_base::failure(e.what()); } - inner.reset(bundle); } /// Returns true if this contains an Orchard bundle, or false if there is no /// Orchard component. - bool IsPresent() const { return (bool)inner; } + bool IsPresent() const { return inner->is_present(); } /// Returns the net value entering or exiting the Orchard pool as a result of this /// bundle. CAmount GetValueBalance() const { - return orchard_bundle_value_balance(inner.get()); + return inner->value_balance_zat(); } /// Queues this bundle's authorization for validation. /// /// `sighash` must be for the transaction this bundle is within. void QueueAuthValidation( - orchard::AuthValidator& batch, const uint256& sighash) const + orchard::BatchValidator& batch, const uint256& sighash) const { - batch.Queue(inner.get(), sighash.begin()); + batch.add_bundle(inner->box_clone(), sighash.GetRawBytes()); } const size_t GetNumActions() const { - return orchard_bundle_actions_len(inner.get()); + return inner->num_actions(); } const std::vector GetNullifiers() const { - size_t actions_len = orchard_bundle_actions_len(inner.get()); - std::vector result(actions_len); - auto nullifiers_ok = orchard_bundle_nullifiers(inner.get(), result.data(), actions_len); - assert(nullifiers_ok); + const auto actions = inner->actions(); + std::vector result; + result.reserve(actions.size()); + for (const auto& action : actions) { + result.push_back(uint256::FromRawBytes(action.nullifier())); + } return result; } const std::optional GetAnchor() const { - uint256 result; - if (orchard_bundle_anchor(inner.get(), result.begin())) { - return result; + if (IsPresent()) { + return uint256::FromRawBytes(inner->anchor()); } else { return std::nullopt; } } bool OutputsEnabled() const { - return orchard_bundle_outputs_enabled(inner.get()); + return inner->enable_outputs(); } bool SpendsEnabled() const { - return orchard_bundle_spends_enabled(inner.get()); + return inner->enable_spends(); } bool CoinbaseOutputsAreValid() const { - return orchard_bundle_coinbase_outputs_are_valid(inner.get()); + return inner->coinbase_outputs_are_valid(); } }; diff --git a/depend/zcash/src/primitives/transaction.cpp b/depend/zcash/src/primitives/transaction.cpp index 9e78e9421..2b3b0a835 100644 --- a/depend/zcash/src/primitives/transaction.cpp +++ b/depend/zcash/src/primitives/transaction.cpp @@ -5,10 +5,12 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php . #include "primitives/transaction.h" +#include "policy/policy.h" #include "hash.h" #include "tinyformat.h" #include "util/strencodings.h" +#include "zip317.h" #include @@ -122,6 +124,22 @@ CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn) scriptPubKey = scriptPubKeyIn; } +CAmount CTxOut::GetDustThreshold() const +{ + // See the comment on ONE_THIRD_DUST_THRESHOLD_RATE in policy.h. + static const CFeeRate oneThirdDustThresholdRate {ONE_THIRD_DUST_THRESHOLD_RATE}; + + if (scriptPubKey.IsUnspendable()) + return 0; + + // A typical spendable txout is 34 bytes, and will need a txin of at + // least 148 bytes to spend. With ONE_THIRD_DUST_THRESHOLD_RATE == 100, + // the dust threshold for such a txout would be + // 3*floor(100*(34 + 148)/1000) zats = 54 zats. + size_t nSize = GetSerializeSize(*this, SER_DISK, 0) + 148u; + return 3*oneThirdDustThresholdRate.GetFee(nSize); +} + uint256 CTxOut::GetHash() const { return SerializeHash(*this); @@ -254,8 +272,8 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) { *const_cast*>(&vShieldedOutput) = tx.vShieldedOutput; orchardBundle = tx.orchardBundle; *const_cast*>(&vJoinSplit) = tx.vJoinSplit; - *const_cast(&joinSplitPubKey) = tx.joinSplitPubKey; - *const_cast(&joinSplitSig) = tx.joinSplitSig; + *const_cast(&joinSplitPubKey) = tx.joinSplitPubKey; + *const_cast(&joinSplitSig) = tx.joinSplitSig; *const_cast(&bindingSig) = tx.bindingSig; *const_cast(&wtxid.hash) = tx.wtxid.hash; *const_cast(&wtxid.authDigest) = tx.wtxid.authDigest; @@ -359,30 +377,18 @@ CAmount CTransaction::GetShieldedValueIn() const return nValue; } -double CTransaction::ComputePriority(double dPriorityInputs, unsigned int nTxSize) const -{ - nTxSize = CalculateModifiedSize(nTxSize); - if (nTxSize == 0) return 0.0; - - return dPriorityInputs / nTxSize; +CAmount CTransaction::GetConventionalFee() const { + return CalculateConventionalFee(GetLogicalActionCount()); } -unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const -{ - // In order to avoid disincentivizing cleaning up the UTXO set we don't count - // the constant overhead for each txin and up to 110 bytes of scriptSig (which - // is enough to cover a compressed pubkey p2sh redemption) for priority. - // Providing any more cleanup incentive than making additional inputs free would - // risk encouraging people to create junk outputs to redeem later. - if (nTxSize == 0) - nTxSize = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); - for (std::vector::const_iterator it(vin.begin()); it != vin.end(); ++it) - { - unsigned int offset = 41U + std::min(110U, (unsigned int)it->scriptSig.size()); - if (nTxSize > offset) - nTxSize -= offset; - } - return nTxSize; +size_t CTransaction::GetLogicalActionCount() const { + return CalculateLogicalActionCount( + vin, + vout, + vJoinSplit.size(), + vShieldedSpend.size(), + vShieldedOutput.size(), + orchardBundle.GetNumActions()); } std::string CTransaction::ToString() const diff --git a/depend/zcash/src/primitives/transaction.h b/depend/zcash/src/primitives/transaction.h index 49a4fa90f..0b9b01ffb 100644 --- a/depend/zcash/src/primitives/transaction.h +++ b/depend/zcash/src/primitives/transaction.h @@ -23,7 +23,7 @@ #include "zcash/Zcash.h" #include "zcash/Proof.hpp" -#include +#include #include // Overwinter transaction version group id @@ -625,26 +625,11 @@ class CTxOut uint256 GetHash() const; - CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const - { - // "Dust" is defined in terms of CTransaction::minRelayTxFee, - // which has units satoshis-per-kilobyte. - // If you'd pay more than 1/3 in fees - // to spend something, then we consider it dust. - // A typical spendable txout is 34 bytes big, and will - // need a CTxIn of at least 148 bytes to spend: - // so dust is a spendable txout less than - // 54*minRelayTxFee/1000 (in satoshis) - if (scriptPubKey.IsUnspendable()) - return 0; - - size_t nSize = GetSerializeSize(*this, SER_DISK, 0) + 148u; - return 3*minRelayTxFee.GetFee(nSize); - } + CAmount GetDustThreshold() const; - bool IsDust(const CFeeRate &minRelayTxFee) const + bool IsDust() const { - return (nValue < GetDustThreshold(minRelayTxFee)); + return nValue < GetDustThreshold(); } friend bool operator==(const CTxOut& a, const CTxOut& b) @@ -772,8 +757,8 @@ class CTransaction const std::vector vShieldedSpend; const std::vector vShieldedOutput; const std::vector vJoinSplit; - const Ed25519VerificationKey joinSplitPubKey; - const Ed25519Signature joinSplitSig; + const ed25519::VerificationKey joinSplitPubKey; + const ed25519::Signature joinSplitSig; const binding_sig_t bindingSig = {{0}}; /** Construct a CTransaction that qualifies as IsNull() */ @@ -888,8 +873,8 @@ class CTransaction auto os = WithVersion(&s, static_cast(header)); ::SerReadWrite(os, *const_cast*>(&vJoinSplit), ser_action); if (vJoinSplit.size() > 0) { - READWRITE(*const_cast(&joinSplitPubKey)); - READWRITE(*const_cast(&joinSplitSig)); + READWRITE(*const_cast(&joinSplitPubKey)); + READWRITE(*const_cast(&joinSplitSig)); } } if ((isSaplingV4 || isFuture) && !(vShieldedSpend.empty() && vShieldedOutput.empty())) { @@ -971,11 +956,13 @@ class CTransaction // Return sum of (positive valueBalanceSapling or zero) and JoinSplit vpub_new CAmount GetShieldedValueIn() const; - // Compute priority, given priority of inputs and (optionally) tx size - double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const; + // Return the conventional fee for this transaction calculated according to + // . + CAmount GetConventionalFee() const; - // Compute modified tx size for priority calculation (optionally given tx size) - unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const; + // Return the number of logical actions calculated according to + // . + size_t GetLogicalActionCount() const; bool IsCoinBase() const { @@ -1013,8 +1000,8 @@ struct CMutableTransaction std::vector vShieldedOutput; OrchardBundle orchardBundle; std::vector vJoinSplit; - Ed25519VerificationKey joinSplitPubKey; - Ed25519Signature joinSplitSig; + ed25519::VerificationKey joinSplitPubKey; + ed25519::Signature joinSplitSig; CTransaction::binding_sig_t bindingSig = {{0}}; CMutableTransaction(); diff --git a/depend/zcash/src/proof_verifier.cpp b/depend/zcash/src/proof_verifier.cpp index b3ed8d5e3..4710c568e 100644 --- a/depend/zcash/src/proof_verifier.cpp +++ b/depend/zcash/src/proof_verifier.cpp @@ -13,13 +13,13 @@ class SproutProofVerifier { ProofVerifier& verifier; - const Ed25519VerificationKey& joinSplitPubKey; + const ed25519::VerificationKey& joinSplitPubKey; const JSDescription& jsdesc; public: SproutProofVerifier( ProofVerifier& verifier, - const Ed25519VerificationKey& joinSplitPubKey, + const ed25519::VerificationKey& joinSplitPubKey, const JSDescription& jsdesc ) : jsdesc(jsdesc), verifier(verifier), joinSplitPubKey(joinSplitPubKey) {} @@ -60,7 +60,7 @@ ProofVerifier ProofVerifier::Disabled() { bool ProofVerifier::VerifySprout( const JSDescription& jsdesc, - const Ed25519VerificationKey& joinSplitPubKey + const ed25519::VerificationKey& joinSplitPubKey ) { if (!perform_verification) { return true; diff --git a/depend/zcash/src/proof_verifier.h b/depend/zcash/src/proof_verifier.h index 0ecc68e1a..1d24047c4 100644 --- a/depend/zcash/src/proof_verifier.h +++ b/depend/zcash/src/proof_verifier.h @@ -8,7 +8,7 @@ #include #include -#include +#include class ProofVerifier { private: @@ -35,7 +35,7 @@ class ProofVerifier { // Verifies that the JoinSplit proof is correct. bool VerifySprout( const JSDescription& jsdesc, - const Ed25519VerificationKey& joinSplitPubKey + const ed25519::VerificationKey& joinSplitPubKey ); }; diff --git a/depend/zcash/src/random.cpp b/depend/zcash/src/random.cpp index f92d39279..97e64bc8e 100644 --- a/depend/zcash/src/random.cpp +++ b/depend/zcash/src/random.cpp @@ -40,23 +40,31 @@ void GetRandBytes(unsigned char* buf, size_t num) librustzcash_getrandom(buf, num); } +uint128_t GetRandUInt128(uint128_t nMax) +{ + return GetRandGeneric(nMax); +} + +int128_t GetRandInt128(int128_t nMax) +{ + assert(nMax >= 0); + return GetRandUInt128(nMax); +} + uint64_t GetRand(uint64_t nMax) { - if (nMax == 0) - return 0; - - // The range of the random source must be a multiple of the modulus - // to give every possible output value an equal possibility - uint64_t nRange = (std::numeric_limits::max() / nMax) * nMax; - uint64_t nRand = 0; - do { - GetRandBytes((unsigned char*)&nRand, sizeof(nRand)); - } while (nRand >= nRange); - return (nRand % nMax); + return GetRandGeneric(nMax); +} + +int64_t GetRandInt64(int64_t nMax) +{ + assert(nMax >= 0); + return GetRand(nMax); } int GetRandInt(int nMax) { + assert(nMax >= 0); return GetRand(nMax); } diff --git a/depend/zcash/src/random.h b/depend/zcash/src/random.h index 171f0be41..007fb36e1 100644 --- a/depend/zcash/src/random.h +++ b/depend/zcash/src/random.h @@ -13,14 +13,49 @@ #include #include -#include -/** +#include "int128.h" + +/** @file * Functions to gather random data via the rand_core OsRng */ + +/** Fill the buffer buf of length num with random unformly distributed bytes, via the rand_core OsRng. */ void GetRandBytes(unsigned char* buf, size_t num); + +/** + * Return a random value of type I uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. + * I must be an unsigned numeric type. + */ +template +I GetRandGeneric(I nMax) +{ + static_assert(std::numeric_limits::min() == 0); + // I() for primitive integer types returns 0. + if (nMax == I()) + return I(); + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + I nRange = (std::numeric_limits::max() / nMax) * nMax; + I nRand = I(); + do { + GetRandBytes((unsigned char*)&nRand, sizeof(nRand)); + } while (nRand >= nRange); + return (nRand % nMax); +} + +/** Return a random uint128_t uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. */ +uint128_t GetRandUInt128(uint128_t nMax); +/** Return a random int128_t uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. nMax must be >= 0. */ +int128_t GetRandInt128(int128_t nMax); +/** Return a random uint64_t uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. */ uint64_t GetRand(uint64_t nMax); +/** Return a random int64_t uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. nMax must be >= 0. */ +int64_t GetRandInt64(int64_t nMax); +/** Return a random int uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. nMax must be >= 0. */ int GetRandInt(int nMax); +/** Return a random uniformly distributed uint256. */ uint256 GetRandHash(); /** diff --git a/depend/zcash/src/rest.cpp b/depend/zcash/src/rest.cpp index 9df1b22f4..1495b75f3 100644 --- a/depend/zcash/src/rest.cpp +++ b/depend/zcash/src/rest.cpp @@ -511,7 +511,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) { LOCK2(cs_main, mempool.cs); - CCoinsView viewDummy; + CCoinsViewDummy viewDummy; CCoinsViewCache view(&viewDummy); CCoinsViewCache& viewChain = *pcoinsTip; diff --git a/depend/zcash/src/rpc/blockchain.cpp b/depend/zcash/src/rpc/blockchain.cpp index ed8aa74a5..f9b91e53c 100644 --- a/depend/zcash/src/rpc/blockchain.cpp +++ b/depend/zcash/src/rpc/blockchain.cpp @@ -349,11 +349,9 @@ UniValue mempoolToJSON(bool fVerbose = false) info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); info.pushKV("time", e.GetTime()); info.pushKV("height", (int)e.GetHeight()); - info.pushKV("startingpriority", e.GetPriority(e.GetHeight())); - info.pushKV("currentpriority", e.GetPriority(chainActive.Height())); info.pushKV("descendantcount", e.GetCountWithDescendants()); info.pushKV("descendantsize", e.GetSizeWithDescendants()); - info.pushKV("descendantfees", e.GetFeesWithDescendants()); + info.pushKV("descendantfees", e.GetModFeesWithDescendants()); const CTransaction& tx = e.GetTx(); set setDepends; for (const CTxIn& txin : tx.vin) @@ -407,11 +405,9 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" - " \"currentpriority\" : n, (numeric) transaction priority now\n" " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" " \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n" - " \"descendantfees\" : n, (numeric) fees of in-mempool descendants (including this one)\n" + " \"descendantfees\" : n, (numeric) modified fees (see \"modifiedfee\" above) of in-mempool descendants (including this one)\n" " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" " ... ]\n" diff --git a/depend/zcash/src/rpc/client.cpp b/depend/zcash/src/rpc/client.cpp index 998f11a2c..0adcfd4b4 100644 --- a/depend/zcash/src/rpc/client.cpp +++ b/depend/zcash/src/rpc/client.cpp @@ -6,6 +6,7 @@ #include "rpc/client.h" #include "rpc/protocol.h" +#include "util/match.h" #include "util/system.h" #include @@ -13,188 +14,262 @@ #include -using namespace std; - -class CRPCConvertParam -{ -public: - std::string methodName; //!< method whose params want conversion - int paramIdx; //!< 0-based idx of param to convert +enum class Conversion { + ///< passed as a JSON string, verbatim + None, + /// the literal string `null` is passed as JSON `null`, otherwise passed as a verbatim string + /// NB: This needs to be used carefully, as it could discard the valid string “null”. + NullableString, + /// parsed as a JSON value + JSON, }; -static const CRPCConvertParam vRPCConvertParams[] = -{ - { "stop", 0 }, - { "setmocktime", 0 }, - { "getaddednodeinfo", 0 }, - { "setgenerate", 0 }, - { "setgenerate", 1 }, - { "generate", 0 }, - { "getnetworkhashps", 0 }, - { "getnetworkhashps", 1 }, - { "getnetworksolps", 0 }, - { "getnetworksolps", 1 }, - { "sendtoaddress", 1 }, - { "sendtoaddress", 4 }, - { "settxfee", 0 }, - { "getreceivedbyaddress", 1 }, - { "getreceivedbyaddress", 2 }, - { "listreceivedbyaddress", 0 }, - { "listreceivedbyaddress", 1 }, - { "listreceivedbyaddress", 2 }, - { "getbalance", 1 }, - { "getbalance", 2 }, - { "getbalance", 3 }, - { "getblockhash", 0 }, - { "listtransactions", 1 }, - { "listtransactions", 2 }, - { "listtransactions", 3 }, - { "walletpassphrase", 1 }, - { "getblocktemplate", 0 }, - { "listsinceblock", 1 }, - { "listsinceblock", 2 }, - { "sendmany", 1 }, - { "sendmany", 2 }, - { "sendmany", 4 }, - { "addmultisigaddress", 0 }, - { "addmultisigaddress", 1 }, - { "createmultisig", 0 }, - { "createmultisig", 1 }, - { "listunspent", 0 }, - { "listunspent", 1 }, - { "listunspent", 2 }, - { "getblock", 1 }, - { "getblockheader", 1 }, - { "gettransaction", 1 }, - { "getrawtransaction", 1 }, - { "createrawtransaction", 0 }, - { "createrawtransaction", 1 }, - { "createrawtransaction", 2 }, - { "createrawtransaction", 3 }, - { "signrawtransaction", 1 }, - { "signrawtransaction", 2 }, - { "sendrawtransaction", 1 }, - { "fundrawtransaction", 1 }, - { "gettxout", 1 }, - { "gettxout", 2 }, - { "gettxoutproof", 0 }, - { "lockunspent", 0 }, - { "lockunspent", 1 }, - { "importprivkey", 2 }, - { "importaddress", 2 }, - { "importaddress", 3 }, - { "importpubkey", 2 }, - { "verifychain", 0 }, - { "verifychain", 1 }, - { "keypoolrefill", 0 }, - { "getrawmempool", 0 }, - { "estimatefee", 0 }, - { "estimatepriority", 0 }, - { "prioritisetransaction", 1 }, - { "prioritisetransaction", 2 }, - { "setban", 2 }, - { "setban", 3 }, - { "getspentinfo", 0}, - { "getaddresstxids", 0}, - { "getaddressbalance", 0}, - { "getaddressdeltas", 0}, - { "getaddressutxos", 0}, - { "getaddressmempool", 0}, - { "getblockhashes", 0}, - { "getblockhashes", 1}, - { "getblockhashes", 2}, - { "getblockdeltas", 0}, - { "zcbenchmark", 1 }, - { "zcbenchmark", 2 }, - { "getblocksubsidy", 0}, - { "z_listaddresses", 0}, - { "z_listreceivedbyaddress", 1}, - { "z_listunspent", 0 }, - { "z_listunspent", 1 }, - { "z_listunspent", 2 }, - { "z_listunspent", 3 }, - { "z_getaddressforaccount", 0}, - { "z_getaddressforaccount", 1}, - { "z_getaddressforaccount", 2}, - { "z_getbalance", 1}, - { "z_getbalance", 2}, - { "z_getbalanceforaccount", 0}, - { "z_getbalanceforaccount", 1}, - { "z_getbalanceforaddress", 1}, - { "z_gettotalbalance", 0}, - { "z_gettotalbalance", 1}, - { "z_gettotalbalance", 2}, - { "z_mergetoaddress", 0}, - { "z_mergetoaddress", 2}, - { "z_mergetoaddress", 3}, - { "z_mergetoaddress", 4}, - { "z_sendmany", 1}, - { "z_sendmany", 2}, - { "z_sendmany", 3}, - { "z_shieldcoinbase", 2}, - { "z_shieldcoinbase", 3}, - { "z_getoperationstatus", 0}, - { "z_getoperationresult", 0}, - { "z_importkey", 2 }, - { "z_importviewingkey", 2 }, - { "z_getpaymentdisclosure", 1}, - { "z_getpaymentdisclosure", 2}, - { "z_setmigration", 0}, - { "z_getnotescount", 0}, -}; +typedef std::pair, std::vector> ParameterSpec; -class CRPCConvertTable -{ -private: - std::set > members; +typedef std::map CRPCConvertTable; -public: - CRPCConvertTable(); +// Single-character aliases for alignment and scannability in the table. +static constexpr Conversion s = Conversion::None; +static constexpr Conversion n = Conversion::NullableString; +static constexpr Conversion o = Conversion::JSON; - bool convert(const std::string& method, int idx) { - return (members.count(std::make_pair(method, idx)) > 0); - } +static const CRPCConvertTable rpcCvtTable = +{ + // operation {required params, optional params} + // blockchain + { "getblockcount", {{}, {}} }, + { "getbestblockhash", {{}, {}} }, + { "getdifficulty", {{}, {}} }, + { "getrawmempool", {{}, {o}} }, + { "getblockdeltas", {{o}, {}} }, + { "getblockhashes", {{o, o}, {o}} }, + { "getblockhash", {{o}, {}} }, + { "getblockheader", {{s}, {o}} }, + { "getblock", {{s}, {o}} }, + { "gettxoutsetinfo", {{}, {}} }, + { "gettxout", {{s, o}, {o}} }, + { "verifychain", {{}, {o, o}} }, + { "getblockchaininfo", {{}, {}} }, + { "getchaintips", {{}, {}} }, + { "z_gettreestate", {{s}, {}} }, + { "getmempoolinfo", {{}, {}} }, + { "invalidateblock", {{s}, {}} }, + { "reconsiderblock", {{s}, {}} }, + // mining + { "getlocalsolps", {{}, {}} }, + { "getnetworksolps", {{}, {o, o}} }, + { "getnetworkhashps", {{}, {o, o}} }, + { "getgenerate", {{}, {}} }, + { "generate", {{o}, {}} }, + { "setgenerate", {{o}, {o}} }, + { "getmininginfo", {{}, {}} }, + { "prioritisetransaction", {{s, o, o}, {}} }, + { "getblocktemplate", {{}, {o}} }, + // NB: The second argument _should_ be an object, but upstream treats it as a string, so we + // preserve that here. + { "submitblock", {{s}, {s}} }, + { "estimatefee", {{o}, {}} }, + { "getblocksubsidy", {{o}, {}} }, + // misc + { "getinfo", {{}, {}} }, + { "validateaddress", {{s}, {}} }, + { "z_validateaddress", {{s}, {}} }, + { "createmultisig", {{o, o}, {}} }, + { "verifymessage", {{s, s ,s}, {}} }, + { "setmocktime", {{o}, {}} }, + { "getexperimentalfeatures", {{}, {}} }, + { "getaddressmempool", {{o}, {}} }, + { "getaddressutxos", {{o}, {}} }, + { "getaddressdeltas", {{o}, {}} }, + { "getaddressbalance", {{o}, {}} }, + { "getaddresstxids", {{o}, {}} }, + { "getspentinfo", {{o}, {}} }, + { "getmemoryinfo", {{}, {}} }, + // net + { "getconnectioncount", {{}, {}} }, + { "ping", {{}, {}} }, + { "getpeerinfo", {{}, {}} }, + { "addnode", {{s, s}, {}} }, + { "disconnectnode", {{s}, {}} }, + { "getaddednodeinfo", {{o}, {s}} }, + { "getnettotals", {{}, {}} }, + { "getdeprecationinfo", {{}, {}} }, + { "getnetworkinfo", {{}, {}} }, + { "setban", {{s, s}, {o, o}} }, + { "listbanned", {{}, {}} }, + { "clearbanned", {{}, {}} }, + // rawtransaction + { "getrawtransaction", {{s}, {o, s}} }, + { "gettxoutproof", {{o}, {s}} }, + { "verifytxoutproof", {{s}, {}} }, + { "createrawtransaction", {{o, o}, {o, o}} }, + { "decoderawtransaction", {{s}, {}} }, + { "decodescript", {{s}, {}} }, + { "signrawtransaction", {{s}, {o, o, s, s}} }, + { "sendrawtransaction", {{s}, {o}} }, + // rpcdisclosure + { "z_getpaymentdisclosure", {{s, o, o}, {s}} }, + { "z_validatepaymentdisclosure", {{s}, {}} }, + // rpcdump + { "importprivkey", {{s}, {s, o}} }, + { "importaddress", {{s}, {s, o, o}} }, + { "importpubkey", {{s}, {s, o}} }, + { "z_importwallet", {{s}, {}} }, + { "importwallet", {{s}, {}} }, + { "dumpprivkey", {{s}, {}} }, + { "z_exportwallet", {{s}, {}} }, + { "z_importkey", {{s}, {s, o}} }, + { "z_importviewingkey", {{s}, {s, o}} }, + { "z_exportkey", {{s}, {}} }, + { "z_exportviewingkey", {{s}, {}} }, + // rpcwallet + { "getnewaddress", {{}, {s}} }, + { "getrawchangeaddress", {{}, {}} }, + { "sendtoaddress", {{s, o}, {s, s, o}} }, + { "listaddresses", {{}, {}} }, + { "listaddressgroupings", {{}, {o}} }, + { "signmessage", {{s, s}, {}} }, + { "getreceivedbyaddress", {{s}, {o, o, o}} }, + { "getbalance", {{}, {s, o, o, o, o}} }, + { "sendmany", {{s, o}, {o, s, o}} }, + { "addmultisigaddress", {{o, o}, {s}} }, + { "listreceivedbyaddress", {{}, {o, o, o, s, o, o}} }, + { "listtransactions", {{}, {s, o, o, o, o}} }, + { "listsinceblock", {{}, {s, o, o, o, o, o}} }, + { "gettransaction", {{s}, {o, o, o}} }, + { "backupwallet", {{s}, {}} }, + { "keypoolrefill", {{}, {o}} }, + { "walletpassphrase", {{s, o}, {}} }, + { "walletpassphrasechange", {{s, s}, {}} }, + { "walletconfirmbackup", {{s}, {}} }, + { "walletlock", {{}, {}} }, + { "encryptwallet", {{s}, {}} }, + { "lockunspent", {{o, o}, {}} }, + { "listlockunspent", {{}, {}} }, + { "settxfee", {{o}, {}} }, + { "getwalletinfo", {{}, {o}} }, + { "resendwallettransactions", {{}, {}} }, + { "listunspent", {{}, {o, o, o, o, o, o}} }, + { "z_listunspent", {{}, {o, o, o, o, o}} }, + { "fundrawtransaction", {{s}, {o}} }, + { "zcsamplejoinsplit", {{}, {}} }, + { "zcbenchmark", {{s, o}, {o}} }, + { "z_getnewaddress", {{}, {s}} }, + { "z_getnewaccount", {{}, {}} }, + { "z_getaddressforaccount", {{o}, {o, o}} }, + { "z_listaccounts", {{}, {}} }, + { "z_listaddresses", {{}, {o}} }, + { "z_listunifiedreceivers", {{s}, {}} }, + { "z_listreceivedbyaddress", {{s}, {o, o}} }, + { "z_getbalance", {{s}, {o, o}} }, + { "z_getbalanceforviewingkey", {{s}, {o, o}} }, + { "z_getbalanceforaccount", {{o}, {o, o}} }, + { "z_gettotalbalance", {{}, {o, o}} }, + { "z_viewtransaction", {{s}, {}} }, + { "z_getoperationresult", {{}, {o}} }, + { "z_getoperationstatus", {{}, {o}} }, + { "z_sendmany", {{s, o}, {o, o, s}} }, + { "z_setmigration", {{o}, {}} }, + { "z_getmigrationstatus", {{}, {o}} }, + { "z_shieldcoinbase", {{s, s}, {o, o, n, s}} }, + { "z_mergetoaddress", {{o, s}, {o, o, o, n, s}} }, + { "z_listoperationids", {{}, {s}} }, + { "z_getnotescount", {{}, {o, o}} }, + // server + { "help", {{}, {s}} }, + { "setlogfilter", {{s}, {}} }, + { "stop", {{}, {o}} }, }; -CRPCConvertTable::CRPCConvertTable() +std::string FormatConversionFailure(const std::string& strMethod, const ConversionFailure& failure) { - const unsigned int n_elem = - (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); + return examine(failure, match { + [&](const UnknownRPCMethod&) { + return tinyformat::format("Unknown RPC method, %s", strMethod); + }, + [&](const WrongNumberOfParams& err) { + return tinyformat::format( + "%s for method, %s. Needed between %u and %u, but received %u", + err.providedParams < err.requiredParams + ? "Not enough parameters" + : "Too many parameters", + strMethod, + err.requiredParams, + err.requiredParams + err.optionalParams, + err.providedParams); + }, + [](const UnparseableParam& err) { + return std::string("Error parsing JSON: ") + err.unparsedParam; + } + }); +} - for (unsigned int i = 0; i < n_elem; i++) { - members.insert(std::make_pair(vRPCConvertParams[i].methodName, - vRPCConvertParams[i].paramIdx)); +std::optional +ParamsToConvertFor(const std::string& strMethod) +{ + auto search = rpcCvtTable.find(strMethod); + if (search != rpcCvtTable.end()) { + return search->second; + } else { + return std::nullopt; } } -static CRPCConvertTable rpcCvtTable; - -/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) - * as well as objects and arrays. - */ -UniValue ParseNonRFCJSONValue(const std::string& strVal) +std::optional ParseNonRFCJSONValue(const std::string& strVal) { UniValue jVal; - if (!jVal.read(std::string("[")+strVal+std::string("]")) || - !jVal.isArray() || jVal.size()!=1) - throw runtime_error(string("Error parsing JSON:")+strVal); - return jVal[0]; + if (jVal.read(std::string("[")+strVal+std::string("]")) && jVal.isArray() && jVal.size() == 1) { + return jVal[0]; + } else { + return std::nullopt; + } } -/** Convert strings to command-specific RPC representation */ -UniValue RPCConvertValues(const std::string &strMethod, const std::vector &strParams) +tl::expected +RPCConvertValues(const std::string &strMethod, const std::vector &strParams) { UniValue params(UniValue::VARR); + auto paramsToConvert = ParamsToConvertFor(strMethod); + if (!paramsToConvert.has_value()) { + return tl::make_unexpected(UnknownRPCMethod()); + } + const auto [requiredParams, optionalParams] = paramsToConvert.value(); + + if (strParams.size() < requiredParams.size() + || requiredParams.size() + optionalParams.size() < strParams.size()) { + return tl::make_unexpected( + WrongNumberOfParams(requiredParams.size(), optionalParams.size(), strParams.size())); + } + + std::vector allParams(requiredParams.begin(), requiredParams.end()); + allParams.reserve(requiredParams.size() + optionalParams.size()); + allParams.insert(allParams.end(), optionalParams.begin(), optionalParams.end()); - for (unsigned int idx = 0; idx < strParams.size(); idx++) { + for (std::vector::size_type idx = 0; idx < strParams.size(); idx++) { + const Conversion conversion = allParams[idx]; const std::string& strVal = strParams[idx]; + auto parsedParam = ParseNonRFCJSONValue(strVal); - if (!rpcCvtTable.convert(strMethod, idx)) { - // insert string value directly - params.push_back(strVal); - } else { - // parse string as JSON, insert bool/number/object/etc. value - params.push_back(ParseNonRFCJSONValue(strVal)); + switch (conversion) { + case Conversion::None: + params.push_back(strVal); + break; + case Conversion::NullableString: + if (parsedParam.has_value() && parsedParam.value().isNull()) { + params.push_back(parsedParam.value()); + } else { + params.push_back(strVal); + } + break; + case Conversion::JSON: + if (parsedParam.has_value()) { + params.push_back(parsedParam.value()); + } else { + return tl::make_unexpected(UnparseableParam(strVal)); + } + break; + default: + assert(false); } } diff --git a/depend/zcash/src/rpc/client.h b/depend/zcash/src/rpc/client.h index 11a4dac3d..cf6a5cdd5 100644 --- a/depend/zcash/src/rpc/client.h +++ b/depend/zcash/src/rpc/client.h @@ -7,12 +7,54 @@ #ifndef BITCOIN_RPC_CLIENT_H #define BITCOIN_RPC_CLIENT_H +#include #include -UniValue RPCConvertValues(const std::string& strMethod, const std::vector& strParams); +class UnknownRPCMethod { + public: + UnknownRPCMethod() {}; +}; + +class WrongNumberOfParams { + public: + std::vector::size_type requiredParams; + std::vector::size_type optionalParams; + std::vector::size_type providedParams; + + WrongNumberOfParams( + std::vector::size_type requiredParams, + std::vector::size_type optionalParams, + std::vector::size_type providedParams) : + requiredParams(requiredParams), + optionalParams(optionalParams), + providedParams(providedParams) {} +}; + +class UnparseableParam { + public: + std::string unparsedParam; + + UnparseableParam(std::string unparsedParam) : + unparsedParam(unparsedParam) {} +}; + +typedef std::variant< + UnknownRPCMethod, + WrongNumberOfParams, + UnparseableParam + > ConversionFailure; + +// TODO: This should be closer to the leaves, but don’t have a good place for it, since it’s +// currently shared by bitcoin-cli and tests. +std::string FormatConversionFailure(const std::string& strMethod, const ConversionFailure& failure); + +/** Convert strings to command-specific RPC representation */ +tl::expected + RPCConvertValues(const std::string& strMethod, const std::vector& strParams); + /** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) - * as well as objects and arrays. + * as well as objects and arrays. Returns `std::nullopt` if `strVal` couldn’t be parsed. */ -UniValue ParseNonRFCJSONValue(const std::string& strVal); +std::optional ParseNonRFCJSONValue(const std::string& strVal); #endif // BITCOIN_RPC_CLIENT_H diff --git a/depend/zcash/src/rpc/mining.cpp b/depend/zcash/src/rpc/mining.cpp index e1a3c06a4..5209b9281 100644 --- a/depend/zcash/src/rpc/mining.cpp +++ b/depend/zcash/src/rpc/mining.cpp @@ -221,7 +221,7 @@ UniValue generate(const UniValue& params, bool fHelp) unsigned int k = Params().GetConsensus().nEquihashK; while (nHeight < nHeightEnd) { - std::unique_ptr pblocktemplate(CreateNewBlock(Params(), minerAddress)); + std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(minerAddress)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; @@ -372,20 +372,18 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) } -// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts +// NOTE: Unlike wallet RPCs (which use ZEC values), mining RPCs follow GBT (BIP 22) in using zatoshi amounts. UniValue prioritisetransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 3) - throw runtime_error( + throw std::runtime_error( "prioritisetransaction \n" "Accepts the transaction into mined blocks at a higher (or lower) priority\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id.\n" - "2. priority delta (numeric, required) The priority to add or subtract.\n" - " The transaction selection algorithm considers the tx as it would have a higher priority.\n" - " (priority of a transaction is calculated: coinage * value_in_satoshis / txsize) \n" - "3. fee delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n" - " The fee is not actually paid, only the algorithm for selecting transactions into a block\n" + "2. priority_delta (numeric, optional) Fee-independent priority adjustment. Not supported, so must be zero or null.\n" + "3. fee_delta (numeric, required) The fee value (in " + MINOR_CURRENCY_UNIT + ") to add (or subtract, if negative).\n" + " The modified fee is not actually paid, only the algorithm for selecting transactions into a block\n" " considers the transaction as it would have paid a higher (or lower) fee.\n" "\nResult\n" "true (boolean) Returns true\n" @@ -399,7 +397,11 @@ UniValue prioritisetransaction(const UniValue& params, bool fHelp) uint256 hash = ParseHashStr(params[0].get_str(), "txid"); CAmount nAmount = params[2].get_int64(); - mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount); + if (!(params[1].isNull() || params[1].get_real() == 0)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is not supported, and adjustment thereof must be zero."); + } + + mempool.PrioritiseTransaction(hash, params[0].get_str(), nAmount); return true; } @@ -474,7 +476,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" " ,...\n" " ],\n" - " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" + " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in " + MINOR_CURRENCY_UNIT + "); for coinbase transactions, this is a negative Number of the total collected block fees (i.e., not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" " \"sigops\" : n, (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n" " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" " }\n" @@ -483,7 +485,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) // " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" // " \"flags\" : \"flags\" (string) \n" // " },\n" -// " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n" +// " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in " + MINOR_CURRENCY_UNIT + ")\n" " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" " \"target\" : \"xxxx\", (string) The hash target\n" " \"longpollid\" : \"str\", (string) an id to include with a request to longpoll on an update to this template\n" @@ -700,7 +702,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INTERNAL_ERROR, "No miner address available (mining requires a wallet or -mineraddress)"); } - pblocktemplate = CreateNewBlock(Params(), minerAddress, next_cb_mtx); + pblocktemplate = BlockAssembler(Params()).CreateNewBlock(minerAddress, next_cb_mtx); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -940,34 +942,6 @@ UniValue estimatefee(const UniValue& params, bool fHelp) return ValueFromAmount(feeRate.GetFeePerK()); } -UniValue estimatepriority(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "estimatepriority nblocks\n" - "\nEstimates the approximate priority\n" - "a zero-fee transaction needs to begin confirmation\n" - "within nblocks blocks.\n" - "\nArguments:\n" - "1. nblocks (numeric)\n" - "\nResult:\n" - "n : (numeric) estimated priority\n" - "\n" - "-1.0 is returned if not enough transactions and\n" - "blocks have been observed to make an estimate.\n" - "\nExample:\n" - + HelpExampleCli("estimatepriority", "6") - ); - - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - - int nBlocks = params[0].get_int(); - if (nBlocks < 1) - nBlocks = 1; - - return mempool.estimatePriority(nBlocks); -} - UniValue getblocksubsidy(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -1074,7 +1048,6 @@ static const CRPCCommand commands[] = #endif { "util", "estimatefee", &estimatefee, true }, - { "util", "estimatepriority", &estimatepriority, true }, }; void RegisterMiningRPCCommands(CRPCTable &tableRPC) diff --git a/depend/zcash/src/rpc/misc.cpp b/depend/zcash/src/rpc/misc.cpp index 330b60716..401e2ed25 100644 --- a/depend/zcash/src/rpc/misc.cpp +++ b/depend/zcash/src/rpc/misc.cpp @@ -67,8 +67,8 @@ UniValue getinfo(const UniValue& params, bool fHelp) " \"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" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee rate set in " + CURRENCY_UNIT + " per 1000 bytes\n" + " \"relayfee\": x.xxxx, (numeric) minimum relay fee rate for transactions in " + CURRENCY_UNIT + " per 1000 bytes\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" @@ -661,7 +661,7 @@ UniValue getaddressmempool(const UniValue& params, bool fHelp) " \"address\" (string) The base58check encoded address\n" " \"txid\" (string) The related txid\n" " \"index\" (number) The related input or output index\n" - " \"satoshis\" (number) The difference of zatoshis\n" + " \"satoshis\" (number) The difference of " + MINOR_CURRENCY_UNIT + "\n" " \"timestamp\" (number) The time the transaction entered the mempool (seconds)\n" " \"prevtxid\" (string) The previous txid (if spending)\n" " \"prevout\" (string) The previous transaction output index (if spending)\n" @@ -743,7 +743,7 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) " \"height\" (number) The block height\n" " \"outputIndex\" (number) The output index\n" " \"script\" (string) The script hex encoded\n" - " \"satoshis\" (number) The number of zatoshis of the output\n" + " \"satoshis\" (number) The number of " + MINOR_CURRENCY_UNIT + " of the output\n" " }, ...\n" "]\n\n" "(or, if chainInfo is true):\n\n" @@ -756,7 +756,7 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) " \"height\" (number) The block height\n" " \"outputIndex\" (number) The output index\n" " \"script\" (string) The script hex encoded\n" - " \"satoshis\" (number) The number of zatoshis of the output\n" + " \"satoshis\" (number) The number of " + MINOR_CURRENCY_UNIT + " of the output\n" " }, ...\n" " ],\n" " \"hash\" (string) The block hash\n" @@ -899,7 +899,7 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) "\nResult:\n" "[\n" " {\n" - " \"satoshis\" (number) The difference of zatoshis\n" + " \"satoshis\" (number) The difference of " + MINOR_CURRENCY_UNIT + "\n" " \"txid\" (string) The related txid\n" " \"index\" (number) The related input or output index\n" " \"height\" (number) The block height\n" @@ -911,7 +911,7 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) " \"deltas\":\n" " [\n" " {\n" - " \"satoshis\" (number) The difference of zatoshis\n" + " \"satoshis\" (number) The difference of " + MINOR_CURRENCY_UNIT + "\n" " \"txid\" (string) The related txid\n" " \"index\" (number) The related input or output index\n" " \"height\" (number) The block height\n" @@ -1022,8 +1022,8 @@ UniValue getaddressbalance(const UniValue& params, bool fHelp) "\"address\" (string) The base58check encoded address\n" "\nResult:\n" "{\n" - " \"balance\" (string) The current balance in zatoshis\n" - " \"received\" (string) The total number of zatoshis received (including change)\n" + " \"balance\" (string) The current balance in " + MINOR_CURRENCY_UNIT + "\n" + " \"received\" (string) The total number of " + MINOR_CURRENCY_UNIT + " received (including change)\n" "}\n" "\nExamples:\n" + HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"]}'") diff --git a/depend/zcash/src/rpc/net.cpp b/depend/zcash/src/rpc/net.cpp index d6b9c9b01..80fd3a307 100644 --- a/depend/zcash/src/rpc/net.cpp +++ b/depend/zcash/src/rpc/net.cpp @@ -158,6 +158,8 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) } obj.pushKV("inflight", heights); } + obj.pushKV("addr_processed", stats.m_addr_processed); + obj.pushKV("addr_rate_limited", stats.m_addr_rate_limited); obj.pushKV("whitelisted", stats.fWhitelisted); ret.push_back(obj); @@ -427,15 +429,29 @@ static UniValue GetNetworksInfo() UniValue getdeprecationinfo(const UniValue& params, bool fHelp) { const CChainParams& chainparams = Params(); - if (fHelp || params.size() != 0 || chainparams.NetworkIDString() != "main") + if (fHelp || params.size() != 0) throw runtime_error( "getdeprecationinfo\n" "Returns an object containing current version and deprecation block height. Applicable only on mainnet.\n" "\nResult:\n" "{\n" - " \"version\": xxxxx, (numeric) the server version\n" + " \"version\": xxxxx, (numeric) the server version\n" " \"subversion\": \"/MagicBean:x.y.z[-v]/\", (string) the server subversion string\n" - " \"deprecationheight\": xxxxx, (numeric) the block height at which this version will deprecate and shut down\n" + " \"deprecationheight\": xxxxx, (numeric, deprecated) the block height at which this version will deprecate and shut down\n" + " \"end_of_service\": { (object, optional) information about end-of-service halt, not present for testnet\n" + " or regtest nodes\n" + " \"block_height\": xxxxx, (numeric) the block height at which this version will reach its the end of its service period and shut down\n" + " \"estimated_time\": xxxxx (numeric) the approximate time at which this version is expected to reach the end-of-service height,\n" + " in seconds since epoch (midnight Jan 1 1970 GMT). Please note that given the variability of block times,\n" + " the actual end-of-service halt may vary from this time by hours or even days; this value is provided\n" + " solely for informational purposes and should not be relied upon to remain accurate. If the end-of-service\n" + " height for this node has already been reached, this timestamp may be in the past.\n" + " },\n" + " \"deprecated_features\": [...], (array of string) a list of currently-deprecated but not yet disabled features\n" + " \"disabled_features\": [...] (array of string) a list of the deprecated and currently-disabled features that should be expected to be\n" + " removed in an upcoming release. These features can be re-enabled via use of the '-allowdeprecated'\n" + " configuration option; please see https://zcash.github.io/zcash/user/deprecation.html for additional\n" + " information.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getdeprecationinfo", "") @@ -446,7 +462,33 @@ UniValue getdeprecationinfo(const UniValue& params, bool fHelp) obj.pushKV("version", CLIENT_VERSION); obj.pushKV("subversion", FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); - obj.pushKV("deprecationheight", DEPRECATION_HEIGHT); + + if (chainparams.NetworkIDString() == "main") { + if (fEnableDeprecationInfoDeprecationHeight) { + obj.pushKV("deprecationheight", DEPRECATION_HEIGHT); + } + + UniValue eos(UniValue::VOBJ); + eos.pushKV("block_height", DEPRECATION_HEIGHT); + { + LOCK(cs_main); + + eos.pushKV("estimated_time", EstimatedNodeDeprecationTime(*GetNodeClock(), chainActive.Height())); + } + obj.pushKV("end_of_service", eos); + } + + UniValue deprecated(UniValue::VARR); + for (const auto feature : DEFAULT_ALLOW_DEPRECATED) { + deprecated.push_back(feature); + } + obj.pushKV("deprecated_features", deprecated); + + UniValue disabled(UniValue::VARR); + for (const auto feature : DEFAULT_DENY_DEPRECATED) { + disabled.push_back(feature); + } + obj.pushKV("disabled_features", disabled); return obj; } @@ -474,7 +516,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) " }\n" " ,...\n" " ],\n" - " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" + " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee rate for transactions in " + CURRENCY_UNIT + " per 1000 bytes\n" " \"localaddresses\": [ (array) list of local addresses\n" " {\n" " \"address\": \"xxxx\", (string) network address\n" diff --git a/depend/zcash/src/rpc/rawtransaction.cpp b/depend/zcash/src/rpc/rawtransaction.cpp index cf2479420..68386b18f 100644 --- a/depend/zcash/src/rpc/rawtransaction.cpp +++ b/depend/zcash/src/rpc/rawtransaction.cpp @@ -32,7 +32,7 @@ #include #include -#include +#include using namespace std; @@ -181,7 +181,7 @@ UniValue TxActionsToJSON(const rust::Vec& actions) // See https://zips.z.cash/zip-0225 UniValue TxOrchardBundleToJSON(const CTransaction& tx, UniValue& entry) { - auto bundle = tx.GetOrchardBundle().GetDetails(); + const auto& bundle = tx.GetOrchardBundle().GetDetails(); UniValue obj(UniValue::VOBJ); auto actions = bundle->actions(); @@ -309,12 +309,12 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) // it is byte-flipped in the RPC output. uint256 joinSplitPubKey; std::copy( - tx.joinSplitPubKey.bytes, - tx.joinSplitPubKey.bytes + ED25519_VERIFICATION_KEY_LEN, + tx.joinSplitPubKey.bytes.begin(), + tx.joinSplitPubKey.bytes.end(), joinSplitPubKey.begin()); entry.pushKV("joinSplitPubKey", joinSplitPubKey.GetHex()); entry.pushKV("joinSplitSig", - HexStr(tx.joinSplitSig.bytes, tx.joinSplitSig.bytes + ED25519_SIGNATURE_LEN)); + HexStr(tx.joinSplitSig.bytes.begin(), tx.joinSplitSig.bytes.end())); } if (!hashBlock.IsNull()) { @@ -1036,7 +1036,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) CMutableTransaction mergedTx(txVariants[0]); // Fetch previous transactions (inputs): - CCoinsView viewDummy; + CCoinsViewDummy viewDummy; CCoinsViewCache view(&viewDummy); { LOCK(mempool.cs); @@ -1285,7 +1285,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - if (!AcceptToMemoryPool(chainparams, mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) { + if (!AcceptToMemoryPool(chainparams, mempool, state, tx, true, &fMissingInputs, !fOverrideFees)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { diff --git a/depend/zcash/src/rust/bin/inspect/block.rs b/depend/zcash/src/rust/bin/inspect/block.rs index f9be3b113..15e8ef22d 100644 --- a/depend/zcash/src/rust/bin/inspect/block.rs +++ b/depend/zcash/src/rust/bin/inspect/block.rs @@ -188,7 +188,7 @@ impl Block { let mut inner_hasher = Sha256::new(); inner_hasher.update(merkle_tree[j + i]); inner_hasher.update(merkle_tree[j + i2]); - merkle_tree.push(Sha256::digest(&inner_hasher.finalize())); + merkle_tree.push(Sha256::digest(inner_hasher.finalize())); i += 2; } j += size; diff --git a/depend/zcash/src/rust/bin/inspect/transaction.rs b/depend/zcash/src/rust/bin/inspect/transaction.rs index 4de4df25f..56af769cd 100644 --- a/depend/zcash/src/rust/bin/inspect/transaction.rs +++ b/depend/zcash/src/rust/bin/inspect/transaction.rs @@ -19,7 +19,7 @@ use zcash_primitives::{ memo::{Memo, MemoBytes}, sapling::note_encryption::SaplingDomain, transaction::{ - components::{orchard as orchard_serialization, sapling, transparent, Amount}, + components::{sapling, transparent, Amount}, sighash::{signature_hash, SignableInput, TransparentAuthorizingContext}, txid::TxIdDigester, Authorization, Transaction, TransactionData, TxId, TxVersion, @@ -139,44 +139,6 @@ impl transparent::MapAuth for MapTrans } } -// TODO: Move these trait impls into `zcash_primitives` so they can be on `()`. -struct IdentityMap; - -impl sapling::MapAuth for IdentityMap { - fn map_proof( - &self, - p: ::Proof, - ) -> ::Proof { - p - } - - fn map_auth_sig( - &self, - s: ::AuthSig, - ) -> ::AuthSig { - s - } - - fn map_authorization(&self, a: sapling::Authorized) -> sapling::Authorized { - a - } -} - -impl orchard_serialization::MapAuth - for IdentityMap -{ - fn map_spend_auth( - &self, - s: ::SpendAuth, - ) -> ::SpendAuth { - s - } - - fn map_authorization(&self, a: orchard::bundle::Authorized) -> orchard::bundle::Authorized { - a - } -} - pub(crate) struct PrecomputedAuth; impl Authorization for PrecomputedAuth { @@ -230,8 +192,7 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { let tx = Transaction::read(&buf[..], tx.consensus_branch_id()).unwrap(); let tx: TransactionData = - tx.into_data() - .map_authorization(f_transparent, IdentityMap, IdentityMap); + tx.into_data().map_authorization(f_transparent, (), ()); let txid_parts = tx.digest(TxIdDigester); (tx, txid_parts) }); @@ -412,23 +373,23 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { } if let Some(bundle) = tx.sapling_bundle() { - assert!(!(bundle.shielded_spends.is_empty() && bundle.shielded_outputs.is_empty())); + assert!(!(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty())); // TODO: Separate into checking proofs, signatures, and other structural details. let mut ctx = SaplingVerificationContext::new(true); - if !bundle.shielded_spends.is_empty() { - eprintln!(" - {} Sapling Spend(s)", bundle.shielded_spends.len()); + if !bundle.shielded_spends().is_empty() { + eprintln!(" - {} Sapling Spend(s)", bundle.shielded_spends().len()); if let Some(sighash) = &common_sighash { - for (i, spend) in bundle.shielded_spends.iter().enumerate() { + for (i, spend) in bundle.shielded_spends().iter().enumerate() { if !ctx.check_spend( - spend.cv, - spend.anchor, - &spend.nullifier.0, - spend.rk.clone(), + spend.cv(), + *spend.anchor(), + &spend.nullifier().0, + spend.rk().clone(), sighash.as_ref(), - spend.spend_auth_sig, - groth16::Proof::read(&spend.zkproof[..]).unwrap(), + *spend.spend_auth_sig(), + groth16::Proof::read(&spend.zkproof()[..]).unwrap(), &GROTH16_PARAMS.spend_vk, ) { eprintln!(" ⚠️ Spend {} is invalid", i); @@ -441,9 +402,9 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { } } - if !bundle.shielded_outputs.is_empty() { - eprintln!(" - {} Sapling Output(s)", bundle.shielded_outputs.len()); - for (i, output) in bundle.shielded_outputs.iter().enumerate() { + if !bundle.shielded_outputs().is_empty() { + eprintln!(" - {} Sapling Output(s)", bundle.shielded_outputs().len()); + for (i, output) in bundle.shielded_outputs().iter().enumerate() { if is_coinbase { if let Some((params, addr_net)) = context .as_ref() @@ -453,17 +414,17 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { &SaplingDomain::for_height(params, height.unwrap()), &zcash_primitives::keys::OutgoingViewingKey([0; 32]), output, - &output.cv, - &output.out_ciphertext, + output.cv(), + output.out_ciphertext(), ) { - if note.value == 0 { + if note.value().inner() == 0 { eprintln!(" - Output {} (dummy output):", i); } else { let zaddr = ZcashAddress::from_sapling(addr_net, addr.to_bytes()); eprintln!(" - Output {}:", i); eprintln!(" - {}", zaddr); - eprintln!(" - {}", render_value(note.value)); + eprintln!(" - {}", render_value(note.value().inner())); } eprintln!(" - {}", render_memo(memo)); } else { @@ -478,10 +439,10 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { } if !ctx.check_output( - output.cv, - output.cmu, - jubjub::ExtendedPoint::from_bytes(&output.ephemeral_key.0).unwrap(), - groth16::Proof::read(&output.zkproof[..]).unwrap(), + output.cv(), + *output.cmu(), + jubjub::ExtendedPoint::from_bytes(&output.ephemeral_key().0).unwrap(), + groth16::Proof::read(&output.zkproof()[..]).unwrap(), &GROTH16_PARAMS.output_vk, ) { eprintln!(" ⚠️ Output {} is invalid", i); @@ -491,9 +452,9 @@ pub(crate) fn inspect(tx: Transaction, context: Option) { if let Some(sighash) = &common_sighash { if !ctx.final_check( - bundle.value_balance, + *bundle.value_balance(), sighash.as_ref(), - bundle.authorization.binding_sig, + bundle.authorization().binding_sig, ) { eprintln!("⚠️ Sapling bindingSig is invalid"); } diff --git a/depend/zcash/src/rust/include/librustzcash.h b/depend/zcash/src/rust/include/librustzcash.h index 83f107b69..5ff1d5f2e 100644 --- a/depend/zcash/src/rust/include/librustzcash.h +++ b/depend/zcash/src/rust/include/librustzcash.h @@ -97,7 +97,6 @@ extern "C" { /// The result is also of length 32 and placed in `result`. /// Returns false if the diversifier or pk_d is not valid bool librustzcash_sapling_compute_cmu( - bool zip216_enabled, const unsigned char *diversifier, const unsigned char *pk_d, const uint64_t value, @@ -105,15 +104,17 @@ extern "C" { unsigned char *result ); - /// Compute [sk] [8] P for some 32-byte - /// point P, and 32-byte Fs. If P or sk - /// are invalid, returns false. Otherwise, - /// the result is written to the 32-byte - /// `result` buffer. - bool librustzcash_sapling_ka_agree( - bool zip216_enabled, + /// Compute KDF^Sapling(KA^Agree(sk, P), ephemeral_key). + /// + /// P and sk must point to 32-byte buffers. If P does not + /// represent a Jubjub point or sk does not represent a + /// canonical Jubjub scalar, this function returns false. + /// Otherwise, it writes the result to the 32-byte `result` + /// buffer and returns true. + bool librustzcash_sapling_ka_derive_symmetric_key( const unsigned char *p, const unsigned char *sk, + const unsigned char *ephemeral_key, unsigned char *result ); diff --git a/depend/zcash/src/rust/include/rust/ed25519.h b/depend/zcash/src/rust/include/rust/ed25519.h deleted file mode 100644 index c9b17eb56..000000000 --- a/depend/zcash/src/rust/include/rust/ed25519.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2020-2023 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef ZCASH_RUST_INCLUDE_RUST_ED25519_H -#define ZCASH_RUST_INCLUDE_RUST_ED25519_H - -#include "ed25519/types.h" - -#include - -#ifndef __cplusplus -#include -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/// Generates a new Ed25519 keypair. -void ed25519_generate_keypair( - Ed25519SigningKey* sk, - Ed25519VerificationKey* vk); - -/// Creates a signature on msg using the given signing key. -bool ed25519_sign( - const Ed25519SigningKey* sk, - const unsigned char* msg, - size_t msglen, - Ed25519Signature* signature); - -/// Verifies a purported `signature` on the given `msg`. -/// -/// # Zcash-specific consensus properties -/// -/// Ed25519 checks are described in §5.4.5 of the Zcash protocol specification -/// and in ZIP 215. The verification criteria for an (encoded) `signature` -/// (`R_bytes`, `s_bytes`) with (encoded) verification key `A_bytes` are: -/// -/// - `A_bytes` and `R_bytes` MUST be encodings of points `A` and `R` -/// respectively on the twisted Edwards form of Curve25519, and non-canonical -/// encodings MUST be accepted; -/// - `s_bytes` MUST represent an integer `s` less than `l`, the order of the -/// prime-order subgroup of Curve25519; -/// - the verification equation `[8][s]B = [8]R + [8][k]A` MUST be satisfied; -/// - the alternate verification equation `[s]B = R + [k]A`, allowed by RFC 8032, -/// MUST NOT be used. -bool ed25519_verify( - const Ed25519VerificationKey* vk, - const Ed25519Signature* signature, - const unsigned char* msg, - size_t msglen); - -#ifdef __cplusplus -} -#endif - -#endif // ZCASH_RUST_INCLUDE_RUST_ED25519_H diff --git a/depend/zcash/src/rust/include/rust/ed25519/types.h b/depend/zcash/src/rust/include/rust/ed25519/types.h deleted file mode 100644 index a717e4714..000000000 --- a/depend/zcash/src/rust/include/rust/ed25519/types.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2020-2023 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef ZCASH_RUST_INCLUDE_RUST_ED25519_TYPES_H -#define ZCASH_RUST_INCLUDE_RUST_ED25519_TYPES_H - -#ifndef __cplusplus -#include -#include -#endif - -#define ED25519_SIGNING_KEY_LEN 32U -#define ED25519_VERIFICATION_KEY_LEN 32U -#define ED25519_SIGNATURE_LEN 64U - -/// An Ed25519 signing key. -/// -/// This is also called a secret key by other implementations. -typedef struct Ed25519SigningKey { - unsigned char bytes[ED25519_SIGNING_KEY_LEN]; -} Ed25519SigningKey; -static_assert( - sizeof(Ed25519SigningKey) == ED25519_SIGNING_KEY_LEN, - "Ed25519SigningKey struct is not the same size as the underlying byte array"); -static_assert( - alignof(Ed25519SigningKey) == 1, - "Ed25519SigningKey struct alignment is not 1"); - -/// An encoded Ed25519 verification key. -/// -/// This is also called a public key by other implementations. -/// -/// This type does not guarantee validity of the verification key. -typedef struct Ed25519VerificationKey { - unsigned char bytes[ED25519_VERIFICATION_KEY_LEN]; -} Ed25519VerificationKey; -static_assert( - sizeof(Ed25519VerificationKey) == ED25519_VERIFICATION_KEY_LEN, - "Ed25519VerificationKey struct is not the same size as the underlying byte array"); -static_assert( - alignof(Ed25519VerificationKey) == 1, - "Ed25519VerificationKey struct alignment is not 1"); - -/// An Ed25519 signature. -typedef struct Ed25519Signature { - unsigned char bytes[ED25519_SIGNATURE_LEN]; -} Ed25519Signature; -static_assert( - sizeof(Ed25519Signature) == ED25519_SIGNATURE_LEN, - "Ed25519Signature struct is not the same size as the underlying byte array"); -static_assert( - alignof(Ed25519Signature) == 1, - "Ed25519Signature struct alignment is not 1"); - -#endif // ZCASH_RUST_INCLUDE_RUST_ED25519_TYPES_H diff --git a/depend/zcash/src/rust/include/rust/orchard.h b/depend/zcash/src/rust/include/rust/orchard.h index b397c2980..2e6a6995a 100644 --- a/depend/zcash/src/rust/include/rust/orchard.h +++ b/depend/zcash/src/rust/include/rust/orchard.h @@ -17,172 +17,8 @@ extern "C" { struct OrchardBundlePtr; typedef struct OrchardBundlePtr OrchardBundlePtr; -struct OrchardBatchValidatorPtr; -typedef struct OrchardBatchValidatorPtr OrchardBatchValidatorPtr; - -/// Clones the given Orchard bundle. -/// -/// Both bundles need to be separately freed when they go out of scope. -OrchardBundlePtr* orchard_bundle_clone(const OrchardBundlePtr* bundle); - -/// Frees an Orchard bundle returned from `orchard_parse_bundle`. -void orchard_bundle_free(OrchardBundlePtr* bundle); - -/// Returns the amount of dynamically-allocated memory used by this bundle. -size_t orchard_bundle_recursive_dynamic_usage(const OrchardBundlePtr* bundle); - -/// Parses an authorized Orchard bundle from the given stream. -/// -/// - If no error occurs, `bundle_ret` will point to a Rust-allocated Orchard bundle. -/// - If an error occurs, `bundle_ret` will be unaltered. -bool orchard_bundle_parse( - void* stream, - read_callback_t read_cb, - OrchardBundlePtr** bundle_ret); - -/// Serializes an authorized Orchard bundle to the given stream -/// -/// If `bundle == nullptr`, this serializes `nActionsOrchard = 0`. -bool orchard_bundle_serialize( - const OrchardBundlePtr* bundle, - void* stream, - write_callback_t write_cb); - -/// Returns the value balance for this Orchard bundle. -/// -/// A transaction with no Orchard component has a value balance of zero. -int64_t orchard_bundle_value_balance(const OrchardBundlePtr* bundle); - -/// Returns the number of actions associated with the bundle. -size_t orchard_bundle_actions_len(const OrchardBundlePtr* bundle); - -/// Returns the nullifiers for the bundle by copying them into the provided -/// vector of 32-byte arrays `nullifiers_ret`, which should be sized to the -/// number of anchors. -/// -/// Returns `false` if the number of nullifiers specified varies from the -/// number of the actions in the bundle, `true` otherwise. -bool orchard_bundle_nullifiers( - const OrchardBundlePtr* bundle, - void* nullifiers_ret, - size_t nullifiers_len - ); - -/// Returns the anchor for the bundle by copying them into -/// the provided value. -/// -/// Returns `false` if the bundle was absent, `true` otherwise. -bool orchard_bundle_anchor( - const OrchardBundlePtr* bundle, - unsigned char* anchor_ret); - -/// Initializes a new Orchard batch validator. -/// -/// Please free this with `orchard_batch_validation_free` when you are done with -/// it. -OrchardBatchValidatorPtr* orchard_batch_validation_init(bool cache_store); - -/// Frees a batch validator returned from `orchard_batch_validation_init`. -void orchard_batch_validation_free(OrchardBatchValidatorPtr* batch); - -/// Adds an Orchard bundle to this batch. -void orchard_batch_add_bundle( - OrchardBatchValidatorPtr* batch, - const OrchardBundlePtr* bundle, - const unsigned char* sighash); - -/// Validates this batch. -/// -/// Returns false if any item in the batch is invalid. -/// -/// ## Consensus rules -/// -/// [§4.6](https://zips.z.cash/protocol/protocol.pdf#actiondesc): -/// - Canonical element encodings are enforced by `orchard_bundle_parse`. -/// - SpendAuthSig^Orchard validity is enforced here. -/// - Proof validity is enforced here. -/// -/// [§7.1](https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus): -/// - `bindingSigOrchard` validity is enforced here. -bool orchard_batch_validate(const OrchardBatchValidatorPtr* batch); - -/// Returns whether the Orchard bundle is present and outputs -/// are enabled. -bool orchard_bundle_outputs_enabled(const OrchardBundlePtr* bundle); - -/// Returns whether the Orchard bundle is present and spends -/// are enabled. -bool orchard_bundle_spends_enabled(const OrchardBundlePtr* bundle); - -/// Returns whether all actions contained in the Orchard bundle -/// can be decrypted with the all-zeros OVK. Returns `true` -/// if no Orchard actions are present. -bool orchard_bundle_coinbase_outputs_are_valid(const OrchardBundlePtr* bundle); - #ifdef __cplusplus } #endif -#ifdef __cplusplus -namespace orchard -{ -/** - * A validator for the Orchard authorization component of a transaction. - */ -class AuthValidator -{ -private: - /// An optional batch validator (with `nullptr` corresponding to `None`). - /// Memory is allocated by Rust. - std::unique_ptr inner; - - AuthValidator() : inner(nullptr, orchard_batch_validation_free) {} - -public: - // AuthValidator should never be copied - AuthValidator(const AuthValidator&) = delete; - AuthValidator& operator=(const AuthValidator&) = delete; - AuthValidator(AuthValidator&& bundle) : inner(std::move(bundle.inner)) {} - AuthValidator& operator=(AuthValidator&& bundle) - { - if (this != &bundle) { - inner = std::move(bundle.inner); - } - return *this; - } - - /// Creates a validation context that batch-validates Orchard proofs and - /// signatures. - static AuthValidator Batch(bool cacheResult) { - auto batch = AuthValidator(); - batch.inner.reset(orchard_batch_validation_init(cacheResult)); - return batch; - } - - /// Creates a validation context that performs no validation. This can be - /// used when avoiding duplicate effort such as during reindexing. - static std::optional Disabled() { - return std::nullopt; - } - - /// Queues an Orchard bundle for validation. - void Queue(const OrchardBundlePtr* bundle, const unsigned char* sighash) { - orchard_batch_add_bundle(inner.get(), bundle, sighash); - } - - /// Validates the queued Orchard authorizations, returning `true` if all - /// proofs and signatures were valid, and `false` otherwise. - /// - /// Throws `std::logic_error` if called more than once. - bool Validate() { - if (!inner) { - throw std::logic_error("orchard::AuthValidator has already been used"); - } - - return orchard_batch_validate(inner.release()); - } -}; -} // namespace orchard -#endif - #endif // ZCASH_RUST_INCLUDE_RUST_ORCHARD_H diff --git a/depend/zcash/src/rust/include/rust/orchard/incremental_merkle_tree.h b/depend/zcash/src/rust/include/rust/orchard/incremental_merkle_tree.h deleted file mode 100644 index 9ef667963..000000000 --- a/depend/zcash/src/rust/include/rust/orchard/incremental_merkle_tree.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2021-2023 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef ZCASH_RUST_INCLUDE_RUST_ORCHARD_INCREMENTAL_MERKLE_TREE_H -#define ZCASH_RUST_INCLUDE_RUST_ORCHARD_INCREMENTAL_MERKLE_TREE_H - -#include "rust/streams.h" -#include "rust/orchard.h" - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define SINSEMILLA_DIGEST_LEN 32U - -/// Pointer to an Orchard incremental merkle tree frontier -struct OrchardMerkleFrontierPtr; -typedef struct OrchardMerkleFrontierPtr OrchardMerkleFrontierPtr; - -// Create an empty Orchard Merkle frontier. -// -// Memory allocated to the resulting value must be manually freed. -OrchardMerkleFrontierPtr* orchard_merkle_frontier_empty(); - -// Clones the given Orchard Merkle frontier and returns -// a pointer to the newly created tree. Both the original -// tree's memory and the newly allocated one need to be freed -// independently. -OrchardMerkleFrontierPtr* orchard_merkle_frontier_clone( - const OrchardMerkleFrontierPtr* tree_ptr); - -// Free the memory allocated for the given Orchard Merkle frontier. -void orchard_merkle_frontier_free( - OrchardMerkleFrontierPtr* tree_ptr); - -// Parses an Orchard Merkle frontier from a stream. If parsing -// fails, this will return the null pointer. -// -// Memory allocated to the resulting value must be manually freed. -OrchardMerkleFrontierPtr* orchard_merkle_frontier_parse( - void* stream, - read_callback_t read_cb); - -// Serializes an Orchard Merkle frontier to a stream. -// -// Returns `false` if an error occurs while writing to the stream. -bool orchard_merkle_frontier_serialize( - const OrchardMerkleFrontierPtr* tree_ptr, - void* stream, - write_callback_t write_cb); - -// Serializes an Orchard Merkle frontier to a stream using the -// zcash_primitives::merkle_tree::CommitmentTree sparse encoding. -// -// Returns `false` if an error occurs while writing to the stream. -bool orchard_merkle_frontier_serialize_legacy( - const OrchardMerkleFrontierPtr* tree_ptr, - void* stream, - write_callback_t write_cb); - -// For each action in the provided bundle, append its -// commitment to the frontier. -// -// Returns `true` if the append succeeds, `false` if the -// tree is full. -bool orchard_merkle_frontier_append_bundle( - OrchardMerkleFrontierPtr* tree_ptr, - const OrchardBundlePtr* bundle); - -// Computes the root of the provided orchard Merkle frontier -void orchard_merkle_frontier_root( - const OrchardMerkleFrontierPtr* tree_ptr, - unsigned char* digest_ret); - -// The total number of leaves that have been appended to obtain -// the current state of the frontier. Subtract 1 from this value -// to obtain the position of the most recently appended leaf. -uint64_t orchard_merkle_frontier_num_leaves( - const OrchardMerkleFrontierPtr* tree_ptr); - -// Estimate the amount of memory consumed by the merkle frontier. -size_t orchard_merkle_frontier_dynamic_mem_usage( - const OrchardMerkleFrontierPtr* tree_ptr); - -// Computes the empty leaf value for the incremental Merkle tree. -void orchard_merkle_tree_empty_root( - unsigned char* digest_ret); - -#ifdef __cplusplus -} -#endif - -#endif // ZCASH_RUST_INCLUDE_RUST_ORCHARD_INCREMENTAL_MERKLE_TREE_H diff --git a/depend/zcash/src/rust/include/rust/orchard/wallet.h b/depend/zcash/src/rust/include/rust/orchard/wallet.h index 8735f038b..d2892dab1 100644 --- a/depend/zcash/src/rust/include/rust/orchard/wallet.h +++ b/depend/zcash/src/rust/include/rust/orchard/wallet.h @@ -5,7 +5,6 @@ #ifndef ZCASH_RUST_INCLUDE_RUST_ORCHARD_WALLET_H #define ZCASH_RUST_INCLUDE_RUST_ORCHARD_WALLET_H -#include "rust/orchard/incremental_merkle_tree.h" #include "rust/orchard/keys.h" #include "rust/builder.h" @@ -19,6 +18,10 @@ extern "C" { struct OrchardWalletPtr; typedef struct OrchardWalletPtr OrchardWalletPtr; +/// Pointer to an Orchard incremental merkle tree frontier +struct OrchardMerkleFrontierPtr; +typedef struct OrchardMerkleFrontierPtr OrchardMerkleFrontierPtr; + /** * Constructs a new empty Orchard wallet and return a pointer to it. * Memory is allocated by Rust and must be manually freed using diff --git a/depend/zcash/src/rust/src/bridge.rs b/depend/zcash/src/rust/src/bridge.rs new file mode 100644 index 000000000..403465988 --- /dev/null +++ b/depend/zcash/src/rust/src/bridge.rs @@ -0,0 +1,327 @@ +/// FFI bridges between `zcashd`'s Rust and C++ code. +/// +/// These are all collected into a single file because we can't use the same Rust type +/// across multiple bridges until https://github.com/dtolnay/cxx/issues/496 is closed. +/// +/// The bridges that we leave separate are either standalone Rust code, or exporting C++ +/// types to Rust (because we _can_ use the same C++ type across multiple bridges). +// +// This file can't use a module comment (`//! comment`) because it causes compilation issues in zcash_script. +use crate::{ + bundlecache::init as bundlecache_init, + merkle_frontier::{new_orchard, orchard_empty_root, parse_orchard, Orchard, OrchardWallet}, + note_encryption::{ + try_sapling_note_decryption, try_sapling_output_recovery, DecryptedSaplingOutput, + }, + orchard_bundle::{ + none_orchard_bundle, orchard_bundle_from_raw_box, parse_orchard_bundle, Action, Bundle, + }, + orchard_ffi::{orchard_batch_validation_init, BatchValidator as OrchardBatchValidator}, + params::{network, Network}, + sapling::{ + finish_bundle_assembly, init_batch_validator as init_sapling_batch_validator, init_prover, + init_verifier, new_bundle_assembler, BatchValidator as SaplingBatchValidator, + Bundle as SaplingBundle, BundleAssembler as SaplingBundleAssembler, Prover, Verifier, + }, + streams::{ + from_auto_file, from_buffered_file, from_data, from_hash_writer, from_size_computer, + CppStream, + }, + wallet_scanner::{init_batch_scanner, BatchResult, BatchScanner}, +}; + +#[allow(clippy::needless_lifetimes)] +#[cxx::bridge] +pub(crate) mod ffi { + extern "C++" { + include!("hash.h"); + include!("streams.h"); + + #[cxx_name = "RustDataStream"] + type RustStream = crate::streams::ffi::RustStream; + type CAutoFile = crate::streams::ffi::CAutoFile; + type CBufferedFile = crate::streams::ffi::CBufferedFile; + type CHashWriter = crate::streams::ffi::CHashWriter; + type CSizeComputer = crate::streams::ffi::CSizeComputer; + } + #[namespace = "stream"] + extern "Rust" { + type CppStream<'a>; + + fn from_data(stream: Pin<&mut RustStream>) -> Box>; + fn from_auto_file(file: Pin<&mut CAutoFile>) -> Box>; + fn from_buffered_file(file: Pin<&mut CBufferedFile>) -> Box>; + fn from_hash_writer(writer: Pin<&mut CHashWriter>) -> Box>; + fn from_size_computer(sc: Pin<&mut CSizeComputer>) -> Box>; + } + + #[namespace = "consensus"] + extern "Rust" { + type Network; + + fn network( + network: &str, + overwinter: i32, + sapling: i32, + blossom: i32, + heartwood: i32, + canopy: i32, + nu5: i32, + ) -> Result>; + } + + #[namespace = "libzcash"] + unsafe extern "C++" { + include!("zcash/cache.h"); + + type BundleValidityCache; + + fn NewBundleValidityCache(kind: &str, bytes: usize) -> UniquePtr; + fn insert(self: Pin<&mut BundleValidityCache>, entry: [u8; 32]); + fn contains(&self, entry: &[u8; 32], erase: bool) -> bool; + } + #[namespace = "bundlecache"] + extern "Rust" { + #[rust_name = "bundlecache_init"] + fn init(cache_bytes: usize); + } + + #[namespace = "sapling"] + extern "Rust" { + #[rust_name = "SaplingBundle"] + type Bundle; + + #[rust_name = "SaplingBundleAssembler"] + type BundleAssembler; + + fn new_bundle_assembler() -> Box; + fn add_spend( + self: &mut SaplingBundleAssembler, + cv: &[u8; 32], + anchor: &[u8; 32], + nullifier: [u8; 32], + rk: &[u8; 32], + zkproof: [u8; 192], // GROTH_PROOF_SIZE + spend_auth_sig: &[u8; 64], + ) -> bool; + fn add_output( + self: &mut SaplingBundleAssembler, + cv: &[u8; 32], + cmu: &[u8; 32], + ephemeral_key: [u8; 32], + enc_ciphertext: [u8; 580], + out_ciphertext: [u8; 80], + zkproof: [u8; 192], // GROTH_PROOF_SIZE + ) -> bool; + fn finish_bundle_assembly( + assembler: Box, + value_balance: i64, + binding_sig: [u8; 64], + ) -> Box; + + type Prover; + + fn init_prover() -> Box; + #[allow(clippy::too_many_arguments)] + fn create_spend_proof( + self: &mut Prover, + ak: &[u8; 32], + nsk: &[u8; 32], + diversifier: &[u8; 11], + rcm: &[u8; 32], + ar: &[u8; 32], + value: u64, + anchor: &[u8; 32], + merkle_path: &[u8; 1065], // 1 + 33 * SAPLING_TREE_DEPTH + 8 + cv: &mut [u8; 32], + rk_out: &mut [u8; 32], + zkproof: &mut [u8; 192], // GROTH_PROOF_SIZE + ) -> bool; + fn create_output_proof( + self: &mut Prover, + esk: &[u8; 32], + payment_address: &[u8; 43], + rcm: &[u8; 32], + value: u64, + cv: &mut [u8; 32], + zkproof: &mut [u8; 192], // GROTH_PROOF_SIZE + ) -> bool; + fn binding_sig( + self: &mut Prover, + value_balance: i64, + sighash: &[u8; 32], + result: &mut [u8; 64], + ) -> bool; + + type Verifier; + + fn init_verifier() -> Box; + #[allow(clippy::too_many_arguments)] + fn check_spend( + self: &mut Verifier, + cv: &[u8; 32], + anchor: &[u8; 32], + nullifier: &[u8; 32], + rk: &[u8; 32], + zkproof: &[u8; 192], // GROTH_PROOF_SIZE + spend_auth_sig: &[u8; 64], + sighash_value: &[u8; 32], + ) -> bool; + fn check_output( + self: &mut Verifier, + cv: &[u8; 32], + cm: &[u8; 32], + ephemeral_key: &[u8; 32], + zkproof: &[u8; 192], // GROTH_PROOF_SIZE + ) -> bool; + fn final_check( + self: &Verifier, + value_balance: i64, + binding_sig: &[u8; 64], + sighash_value: &[u8; 32], + ) -> bool; + + #[cxx_name = "BatchValidator"] + type SaplingBatchValidator; + #[cxx_name = "init_batch_validator"] + fn init_sapling_batch_validator(cache_store: bool) -> Box; + fn check_bundle( + self: &mut SaplingBatchValidator, + bundle: Box, + sighash: [u8; 32], + ) -> bool; + fn validate(self: &mut SaplingBatchValidator) -> bool; + } + + unsafe extern "C++" { + include!("rust/orchard.h"); + type OrchardBundlePtr; + } + #[namespace = "orchard_bundle"] + extern "Rust" { + type Action; + type Bundle; + + fn cv(self: &Action) -> [u8; 32]; + fn nullifier(self: &Action) -> [u8; 32]; + fn rk(self: &Action) -> [u8; 32]; + fn cmx(self: &Action) -> [u8; 32]; + fn ephemeral_key(self: &Action) -> [u8; 32]; + fn enc_ciphertext(self: &Action) -> [u8; 580]; + fn out_ciphertext(self: &Action) -> [u8; 80]; + fn spend_auth_sig(self: &Action) -> [u8; 64]; + + #[rust_name = "none_orchard_bundle"] + fn none() -> Box; + #[rust_name = "orchard_bundle_from_raw_box"] + unsafe fn from_raw_box(bundle: *mut OrchardBundlePtr) -> Box; + fn box_clone(self: &Bundle) -> Box; + #[rust_name = "parse_orchard_bundle"] + fn parse(stream: &mut CppStream<'_>) -> Result>; + fn serialize(self: &Bundle, stream: &mut CppStream<'_>) -> Result<()>; + fn as_ptr(self: &Bundle) -> *const OrchardBundlePtr; + fn recursive_dynamic_usage(self: &Bundle) -> usize; + fn is_present(self: &Bundle) -> bool; + fn actions(self: &Bundle) -> Vec; + fn num_actions(self: &Bundle) -> usize; + fn enable_spends(self: &Bundle) -> bool; + fn enable_outputs(self: &Bundle) -> bool; + fn value_balance_zat(self: &Bundle) -> i64; + fn anchor(self: &Bundle) -> [u8; 32]; + fn proof(self: &Bundle) -> Vec; + fn binding_sig(self: &Bundle) -> [u8; 64]; + fn coinbase_outputs_are_valid(self: &Bundle) -> bool; + } + + #[namespace = "orchard"] + extern "Rust" { + #[cxx_name = "BatchValidator"] + type OrchardBatchValidator; + #[cxx_name = "init_batch_validator"] + fn orchard_batch_validation_init(cache_store: bool) -> Box; + fn add_bundle(self: &mut OrchardBatchValidator, bundle: Box, sighash: [u8; 32]); + fn validate(self: &mut OrchardBatchValidator) -> bool; + } + + #[namespace = "merkle_frontier"] + extern "Rust" { + type Orchard; + type OrchardWallet; + + fn orchard_empty_root() -> [u8; 32]; + fn new_orchard() -> Box; + fn box_clone(self: &Orchard) -> Box; + fn parse_orchard(reader: &mut CppStream<'_>) -> Result>; + fn serialize(self: &Orchard, writer: &mut CppStream<'_>) -> Result<()>; + fn serialize_legacy(self: &Orchard, writer: &mut CppStream<'_>) -> Result<()>; + fn dynamic_memory_usage(self: &Orchard) -> usize; + fn root(self: &Orchard) -> [u8; 32]; + fn size(self: &Orchard) -> u64; + fn append_bundle(self: &mut Orchard, bundle: &Bundle) -> bool; + unsafe fn init_wallet(self: &Orchard, wallet: *mut OrchardWallet) -> bool; + } + + #[namespace = "wallet"] + struct SaplingDecryptionResult { + txid: [u8; 32], + output: u32, + ivk: [u8; 32], + diversifier: [u8; 11], + pk_d: [u8; 32], + } + + #[namespace = "wallet"] + pub(crate) struct SaplingShieldedOutput { + cv: [u8; 32], + cmu: [u8; 32], + ephemeral_key: [u8; 32], + enc_ciphertext: [u8; 580], + out_ciphertext: [u8; 80], + } + + #[namespace = "wallet"] + extern "Rust" { + fn try_sapling_note_decryption( + network: &Network, + height: u32, + raw_ivk: &[u8; 32], + output: SaplingShieldedOutput, + ) -> Result>; + fn try_sapling_output_recovery( + network: &Network, + height: u32, + ovk: [u8; 32], + output: SaplingShieldedOutput, + ) -> Result>; + + type DecryptedSaplingOutput; + fn note_value(self: &DecryptedSaplingOutput) -> u64; + fn note_rseed(self: &DecryptedSaplingOutput) -> [u8; 32]; + fn zip_212_enabled(self: &DecryptedSaplingOutput) -> bool; + fn recipient_d(self: &DecryptedSaplingOutput) -> [u8; 11]; + fn recipient_pk_d(self: &DecryptedSaplingOutput) -> [u8; 32]; + fn memo(self: &DecryptedSaplingOutput) -> [u8; 512]; + + type BatchScanner; + type BatchResult; + + fn init_batch_scanner( + network: &Network, + sapling_ivks: &[[u8; 32]], + ) -> Result>; + fn add_transaction( + self: &mut BatchScanner, + block_tag: [u8; 32], + tx_bytes: &[u8], + height: u32, + ) -> Result<()>; + fn flush(self: &mut BatchScanner); + fn collect_results( + self: &mut BatchScanner, + block_tag: [u8; 32], + txid: [u8; 32], + ) -> Box; + + fn get_sapling(self: &BatchResult) -> Vec; + } +} diff --git a/depend/zcash/src/rust/src/builder_ffi.rs b/depend/zcash/src/rust/src/builder_ffi.rs index 437ad45f4..c24743d01 100644 --- a/depend/zcash/src/rust/src/builder_ffi.rs +++ b/depend/zcash/src/rust/src/builder_ffi.rs @@ -157,7 +157,8 @@ pub extern "C" fn orchard_unauthorized_bundle_prove_and_sign( let bundle = unsafe { Box::from_raw(bundle) }; let keys = unsafe { slice::from_raw_parts(keys, keys_len) }; let sighash = unsafe { sighash.as_ref() }.expect("sighash pointer may not be null."); - let pk = unsafe { ORCHARD_PK.as_ref() }.unwrap(); + let pk = unsafe { ORCHARD_PK.as_ref() } + .expect("Parameters not loaded: ORCHARD_PK should have been initialized"); let signing_keys = keys .iter() diff --git a/depend/zcash/src/rust/src/bundlecache.rs b/depend/zcash/src/rust/src/bundlecache.rs index e5873dc1b..f9cfd4acc 100644 --- a/depend/zcash/src/rust/src/bundlecache.rs +++ b/depend/zcash/src/rust/src/bundlecache.rs @@ -5,23 +5,7 @@ use std::{ use rand_core::{OsRng, RngCore}; -#[cxx::bridge] -mod ffi { - #[namespace = "libzcash"] - unsafe extern "C++" { - include!("zcash/cache.h"); - - type BundleValidityCache; - - fn NewBundleValidityCache(kind: &str, bytes: usize) -> UniquePtr; - fn insert(self: Pin<&mut BundleValidityCache>, entry: [u8; 32]); - fn contains(&self, entry: &[u8; 32], erase: bool) -> bool; - } - #[namespace = "bundlecache"] - extern "Rust" { - fn init(cache_bytes: usize); - } -} +use crate::bridge::ffi; pub(crate) struct CacheEntry([u8; 32]); @@ -114,7 +98,7 @@ static BUNDLE_CACHES_LOADED: Once = Once::new(); static mut SAPLING_BUNDLE_VALIDITY_CACHE: Option> = None; static mut ORCHARD_BUNDLE_VALIDITY_CACHE: Option> = None; -fn init(cache_bytes: usize) { +pub(crate) fn init(cache_bytes: usize) { BUNDLE_CACHES_LOADED.call_once(|| unsafe { SAPLING_BUNDLE_VALIDITY_CACHE = Some(RwLock::new(BundleValidityCache::new( "Sapling", diff --git a/depend/zcash/src/rust/src/ed25519.rs b/depend/zcash/src/rust/src/ed25519.rs index fc81bf1dd..a35515f99 100644 --- a/depend/zcash/src/rust/src/ed25519.rs +++ b/depend/zcash/src/rust/src/ed25519.rs @@ -3,65 +3,67 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php . use ed25519_zebra::{Signature, SigningKey, VerificationKey}; -use libc::{c_uchar, size_t}; use rand_core::OsRng; use std::convert::TryFrom; -use std::slice; -#[no_mangle] -pub extern "C" fn ed25519_generate_keypair(sk: *mut [u8; 32], vk: *mut [u8; 32]) { - let sk = unsafe { sk.as_mut() }.unwrap(); - let vk = unsafe { vk.as_mut() }.unwrap(); +#[cxx::bridge(namespace = "ed25519")] +mod ffi { + struct SigningKey { + bytes: [u8; 32], + } - let signing_key = SigningKey::new(OsRng); - - *sk = signing_key.into(); - *vk = VerificationKey::from(&signing_key).into(); -} + struct VerificationKey { + bytes: [u8; 32], + } -#[no_mangle] -pub extern "C" fn ed25519_sign( - sk: *const [u8; 32], - msg: *const c_uchar, - msg_len: size_t, - signature: *mut [u8; 64], -) -> bool { - let sk = SigningKey::from(*unsafe { sk.as_ref() }.unwrap()); + struct Signature { + bytes: [u8; 64], + } - let msg = unsafe { slice::from_raw_parts(msg, msg_len) }; + extern "Rust" { + fn generate_keypair(sk: &mut SigningKey, vk: &mut VerificationKey); + fn sign(sk: &SigningKey, msg: &[u8], signature: &mut Signature); + fn verify(vk: &VerificationKey, signature: &Signature, msg: &[u8]) -> bool; + } +} - let signature = unsafe { - match signature.as_mut() { - Some(sig) => sig, - None => return false, - } - }; +/// Generates a new Ed25519 keypair. +fn generate_keypair(sk: &mut ffi::SigningKey, vk: &mut ffi::VerificationKey) { + let signing_key = SigningKey::new(OsRng); - *signature = sk.sign(msg).into(); + sk.bytes = signing_key.into(); + vk.bytes = VerificationKey::from(&signing_key).into(); +} - true +/// Creates a signature on msg using the given signing key. +fn sign(sk: &ffi::SigningKey, msg: &[u8], signature: &mut ffi::Signature) { + let sk = SigningKey::from(sk.bytes); + signature.bytes = sk.sign(msg).into(); } -#[no_mangle] -pub extern "C" fn ed25519_verify( - vk: *const [u8; 32], - signature: *const [u8; 64], - msg: *const c_uchar, - msg_len: size_t, -) -> bool { - let vk = match VerificationKey::try_from(*unsafe { vk.as_ref() }.unwrap()) { +/// Verifies a purported `signature` on the given `msg`. +/// +/// # Zcash-specific consensus properties +/// +/// Ed25519 checks are described in §5.4.5 of the Zcash protocol specification +/// and in ZIP 215. The verification criteria for an (encoded) `signature` +/// (`R_bytes`, `s_bytes`) with (encoded) verification key `A_bytes` are: +/// +/// - `A_bytes` and `R_bytes` MUST be encodings of points `A` and `R` +/// respectively on the twisted Edwards form of Curve25519, and non-canonical +/// encodings MUST be accepted; +/// - `s_bytes` MUST represent an integer `s` less than `l`, the order of the +/// prime-order subgroup of Curve25519; +/// - the verification equation `[8][s]B = [8]R + [8][k]A` MUST be satisfied; +/// - the alternate verification equation `[s]B = R + [k]A`, allowed by RFC 8032, +/// MUST NOT be used. +fn verify(vk: &ffi::VerificationKey, signature: &ffi::Signature, msg: &[u8]) -> bool { + let vk = match VerificationKey::try_from(vk.bytes) { Ok(vk) => vk, Err(_) => return false, }; - let signature = Signature::from(*unsafe { - match signature.as_ref() { - Some(sig) => sig, - None => return false, - } - }); - - let msg = unsafe { slice::from_raw_parts(msg, msg_len) }; + let signature = Signature::from(signature.bytes); vk.verify(&signature, msg).is_ok() } diff --git a/depend/zcash/src/rust/src/history_ffi.rs b/depend/zcash/src/rust/src/history_ffi.rs index f132de9d3..4271d708e 100644 --- a/depend/zcash/src/rust/src/history_ffi.rs +++ b/depend/zcash/src/rust/src/history_ffi.rs @@ -145,7 +145,7 @@ fn librustzcash_mmr_append_inner( unsafe { *rt_ret = V::hash(root_node.data()); - for (idx, next_buf) in slice::from_raw_parts_mut(buf_ret, return_count as usize) + for (idx, next_buf) in slice::from_raw_parts_mut(buf_ret, return_count) .iter_mut() .enumerate() { diff --git a/depend/zcash/src/rust/src/incremental_merkle_tree.rs b/depend/zcash/src/rust/src/incremental_merkle_tree.rs index 0f51b9635..c7b228fd0 100644 --- a/depend/zcash/src/rust/src/incremental_merkle_tree.rs +++ b/depend/zcash/src/rust/src/incremental_merkle_tree.rs @@ -76,11 +76,11 @@ pub fn write_tree( tree: &BridgeTree, ) -> io::Result<()> { writer.write_u8(SER_V2)?; - Vector::write(&mut writer, tree.prior_bridges(), |mut w, b| { - write_bridge_v1(&mut w, b) + Vector::write(&mut writer, tree.prior_bridges(), |w, b| { + write_bridge_v1(w, b) })?; - Optional::write(&mut writer, tree.current_bridge().as_ref(), |mut w, b| { - write_bridge_v1(&mut w, b) + Optional::write(&mut writer, tree.current_bridge().as_ref(), |w, b| { + write_bridge_v1(w, b) })?; Vector::write_sized( &mut writer, diff --git a/depend/zcash/src/rust/src/incremental_merkle_tree_ffi.rs b/depend/zcash/src/rust/src/incremental_merkle_tree_ffi.rs deleted file mode 100644 index 5a70b6388..000000000 --- a/depend/zcash/src/rust/src/incremental_merkle_tree_ffi.rs +++ /dev/null @@ -1,186 +0,0 @@ -use incrementalmerkletree::{bridgetree, Altitude, Frontier, Hashable}; -use std::mem::size_of_val; -use std::ptr; - -use orchard::{bundle::Authorized, tree::MerkleHashOrchard}; -use tracing::error; -use zcash_primitives::{ - merkle_tree::{ - incremental::{read_frontier_v1, write_frontier_v1}, - CommitmentTree, - }, - transaction::components::Amount, -}; - -use crate::streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb}; - -pub const MERKLE_DEPTH: u8 = 32; - -// -// Operations on Merkle frontiers. -// - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_empty( -) -> *mut bridgetree::Frontier { - let empty_tree = bridgetree::Frontier::::empty(); - Box::into_raw(Box::new(empty_tree)) -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_clone( - tree: *const bridgetree::Frontier, -) -> *mut bridgetree::Frontier { - unsafe { tree.as_ref() } - .map(|tree| Box::into_raw(Box::new(tree.clone()))) - .unwrap_or(std::ptr::null_mut()) -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_free( - tree: *mut bridgetree::Frontier, -) { - if !tree.is_null() { - drop(unsafe { Box::from_raw(tree) }); - } -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_parse( - stream: Option, - read_cb: Option, -) -> *mut bridgetree::Frontier { - let reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap()); - - match read_frontier_v1(reader) { - Ok(parsed) => Box::into_raw(Box::new(parsed)), - Err(e) => { - error!("Failed to parse Orchard bundle: {}", e); - ptr::null_mut() - } - } -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_serialize( - frontier: *const bridgetree::Frontier, - stream: Option, - write_cb: Option, -) -> bool { - let frontier = unsafe { - frontier - .as_ref() - .expect("Orchard note commitment tree pointer may not be null.") - }; - - let writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); - match write_frontier_v1(writer, frontier) { - Ok(()) => true, - Err(e) => { - error!("{}", e); - false - } - } -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_serialize_legacy( - frontier: *const bridgetree::Frontier, - stream: Option, - write_cb: Option, -) -> bool { - let frontier = unsafe { - frontier - .as_ref() - .expect("Orchard note commitment tree pointer may not be null.") - }; - - let writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); - let commitment_tree = CommitmentTree::from_frontier(frontier); - match commitment_tree.write(writer) { - Ok(()) => true, - Err(e) => { - error!("{}", e); - false - } - } -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_append_bundle( - tree: *mut bridgetree::Frontier, - bundle: *const orchard::Bundle, -) -> bool { - let tree = unsafe { - tree.as_mut() - .expect("Orchard note commitment tree pointer may not be null.") - }; - if let Some(bundle) = unsafe { bundle.as_ref() } { - for action in bundle.actions().iter() { - if !tree.append(&MerkleHashOrchard::from_cmx(action.cmx())) { - error!("Orchard note commitment tree is full."); - return false; - } - } - } - - true -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_root( - tree: *const bridgetree::Frontier, - root_ret: *mut [u8; 32], -) { - let tree = unsafe { - tree.as_ref() - .expect("Orchard note commitment tree pointer may not be null.") - }; - - let root_ret = unsafe { - root_ret - .as_mut() - .expect("Cannot return to the null pointer.") - }; - - *root_ret = tree.root().to_bytes(); -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_num_leaves( - tree: *const bridgetree::Frontier, -) -> u64 { - let tree = unsafe { - tree.as_ref() - .expect("Orchard note commitment tree pointer may not be null.") - }; - - tree.position().map_or(0, |p| ::from(p) + 1) -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_frontier_dynamic_mem_usage( - tree: *const bridgetree::Frontier, -) -> usize { - let tree = unsafe { - tree.as_ref() - .expect("Orchard note commitment tree pointer may not be null.") - }; - - size_of_val(tree) + tree.dynamic_memory_usage() -} - -#[no_mangle] -pub extern "C" fn orchard_merkle_tree_empty_root(root_ret: *mut [u8; 32]) { - let root_ret = unsafe { - root_ret - .as_mut() - .expect("Cannot return to the null pointer.") - }; - - let altitude = Altitude::from(MERKLE_DEPTH); - - let digest = MerkleHashOrchard::empty_root(altitude).to_bytes(); - - *root_ret = digest; -} diff --git a/depend/zcash/src/rust/src/merkle_frontier.rs b/depend/zcash/src/rust/src/merkle_frontier.rs new file mode 100644 index 000000000..e21798da4 --- /dev/null +++ b/depend/zcash/src/rust/src/merkle_frontier.rs @@ -0,0 +1,121 @@ +use core::mem::size_of_val; + +use incrementalmerkletree::{bridgetree, Altitude, Frontier, Hashable}; +use orchard::tree::MerkleHashOrchard; +use tracing::error; +use zcash_primitives::merkle_tree::{ + incremental::{read_frontier_v1, write_frontier_v1}, + CommitmentTree, HashSer, +}; + +use crate::{orchard_bundle, streams::CppStream, wallet::Wallet}; + +pub const MERKLE_DEPTH: u8 = 32; + +type Inner = bridgetree::Frontier; + +/// An incremental Merkle frontier. +#[derive(Clone)] +pub(crate) struct MerkleFrontier(Inner); + +impl MerkleFrontier { + /// Returns a copy of the value. + pub(crate) fn box_clone(&self) -> Box { + Box::new(self.clone()) + } + + /// Attempts to parse a Merkle frontier from the given C++ stream. + pub(crate) fn parse(reader: &mut CppStream<'_>) -> Result, String> { + match read_frontier_v1(reader) { + Ok(parsed) => Ok(Box::new(MerkleFrontier(parsed))), + Err(e) => Err(format!("Failed to parse v5 Merkle frontier: {}", e)), + } + } + + /// Serializes the frontier to the given C++ stream. + pub(crate) fn serialize(&self, writer: &mut CppStream<'_>) -> Result<(), String> { + write_frontier_v1(writer, &self.0) + .map_err(|e| format!("Failed to serialize v5 Merkle frontier: {}", e)) + } + + /// Serializes the frontier to the given C++ stream in the legacy frontier encoding. + pub(crate) fn serialize_legacy(&self, writer: &mut CppStream<'_>) -> Result<(), String> { + let commitment_tree = CommitmentTree::from_frontier(&self.0); + commitment_tree.write(writer).map_err(|e| { + format!( + "Failed to serialize Merkle frontier in legacy format: {}", + e, + ) + }) + } + + /// Returns the amount of memory dynamically allocated for the frontier. + /// + /// Includes `self` because this type is stored on the heap when passed to C++. + pub(crate) fn dynamic_memory_usage(&self) -> usize { + size_of_val(&self.0) + self.0.dynamic_memory_usage() + } + + /// Obtains the current root of this Merkle frontier by hashing against empty nodes up + /// to the maximum height of the pruned tree that the frontier represents. + pub(crate) fn root(&self) -> [u8; 32] { + let mut root = [0; 32]; + self.0 + .root() + .write(&mut root[..]) + .expect("root is 32 bytes"); + root + } + + /// Returns the number of leaves appended to the frontier. + pub(crate) fn size(&self) -> u64 { + self.0.position().map_or(0, |p| ::from(p) + 1) + } +} + +/// Returns the root of an empty Orchard Merkle tree. +pub(crate) fn orchard_empty_root() -> [u8; 32] { + let altitude = Altitude::from(MERKLE_DEPTH); + MerkleHashOrchard::empty_root(altitude).to_bytes() +} + +/// An Orchard incremental Merkle frontier. +pub(crate) type Orchard = MerkleFrontier; + +/// Constructs a new empty Orchard Merkle frontier. +pub(crate) fn new_orchard() -> Box { + Box::new(MerkleFrontier(Inner::empty())) +} + +/// Attempts to parse an Orchard Merkle frontier from the given C++ stream. +pub(crate) fn parse_orchard(reader: &mut CppStream<'_>) -> Result, String> { + Orchard::parse(reader) +} + +pub(crate) struct OrchardWallet; + +impl Orchard { + /// Appends the note commitments in the given bundle to this frontier. + pub(crate) fn append_bundle(&mut self, bundle: &orchard_bundle::Bundle) -> bool { + if let Some(bundle) = bundle.inner() { + for action in bundle.actions().iter() { + if !self.0.append(&MerkleHashOrchard::from_cmx(action.cmx())) { + error!("Orchard note commitment tree is full."); + return false; + } + } + } + + true + } + + /// Overwrites the first bridge of the Orchard wallet's note commitment tree to have + /// `self` as its latest state. + /// + /// This will fail with an assertion error if any checkpoints exist in the tree. + /// + /// TODO: Remove once `crate::wallet` is migrated to `cxx`. + pub(crate) fn init_wallet(&self, wallet: *mut OrchardWallet) -> bool { + crate::wallet::orchard_wallet_init_from_frontier(wallet as *mut Wallet, &self.0) + } +} diff --git a/depend/zcash/src/rust/src/metrics_ffi.rs b/depend/zcash/src/rust/src/metrics_ffi.rs index 8cf995eb7..35306ef9d 100644 --- a/depend/zcash/src/rust/src/metrics_ffi.rs +++ b/depend/zcash/src/rust/src/metrics_ffi.rs @@ -210,7 +210,7 @@ pub extern "C" fn metrics_increment_counter(key: *mut FfiKey, value: u64) { pub extern "C" fn metrics_static_update_gauge(callsite: *const FfiCallsite, value: c_double) { if let Some(recorder) = try_recorder() { let callsite = unsafe { callsite.as_ref().unwrap() }; - recorder.register_gauge(&callsite.key).set(value as f64); + recorder.register_gauge(&callsite.key).set(value); } } @@ -219,7 +219,7 @@ pub extern "C" fn metrics_update_gauge(key: *mut FfiKey, value: c_double) { if let Some(recorder) = try_recorder() { if !key.is_null() { let key = unsafe { Box::from_raw(key) }; - recorder.register_gauge(&key.inner).set(value as f64); + recorder.register_gauge(&key.inner).set(value); } } } @@ -228,9 +228,7 @@ pub extern "C" fn metrics_update_gauge(key: *mut FfiKey, value: c_double) { pub extern "C" fn metrics_static_increment_gauge(callsite: *const FfiCallsite, value: c_double) { if let Some(recorder) = try_recorder() { let callsite = unsafe { callsite.as_ref().unwrap() }; - recorder - .register_gauge(&callsite.key) - .increment(value as f64); + recorder.register_gauge(&callsite.key).increment(value); } } @@ -239,7 +237,7 @@ pub extern "C" fn metrics_increment_gauge(key: *mut FfiKey, value: c_double) { if let Some(recorder) = try_recorder() { if !key.is_null() { let key = unsafe { Box::from_raw(key) }; - recorder.register_gauge(&key.inner).increment(value as f64); + recorder.register_gauge(&key.inner).increment(value); } } } @@ -248,9 +246,7 @@ pub extern "C" fn metrics_increment_gauge(key: *mut FfiKey, value: c_double) { pub extern "C" fn metrics_static_decrement_gauge(callsite: *const FfiCallsite, value: c_double) { if let Some(recorder) = try_recorder() { let callsite = unsafe { callsite.as_ref().unwrap() }; - recorder - .register_gauge(&callsite.key) - .decrement(value as f64); + recorder.register_gauge(&callsite.key).decrement(value); } } @@ -259,7 +255,7 @@ pub extern "C" fn metrics_decrement_gauge(key: *mut FfiKey, value: c_double) { if let Some(recorder) = try_recorder() { if !key.is_null() { let key = unsafe { Box::from_raw(key) }; - recorder.register_gauge(&key.inner).decrement(value as f64); + recorder.register_gauge(&key.inner).decrement(value); } } } @@ -268,9 +264,7 @@ pub extern "C" fn metrics_decrement_gauge(key: *mut FfiKey, value: c_double) { pub extern "C" fn metrics_static_record_histogram(callsite: *const FfiCallsite, value: c_double) { if let Some(recorder) = try_recorder() { let callsite = unsafe { callsite.as_ref().unwrap() }; - recorder - .register_histogram(&callsite.key) - .record(value as f64); + recorder.register_histogram(&callsite.key).record(value); } } @@ -279,7 +273,7 @@ pub extern "C" fn metrics_record_histogram(key: *mut FfiKey, value: c_double) { if let Some(recorder) = try_recorder() { if !key.is_null() { let key = unsafe { Box::from_raw(key) }; - recorder.register_histogram(&key.inner).record(value as f64); + recorder.register_histogram(&key.inner).record(value); } } } diff --git a/depend/zcash/src/rust/src/note_encryption.rs b/depend/zcash/src/rust/src/note_encryption.rs new file mode 100644 index 000000000..069fc712c --- /dev/null +++ b/depend/zcash/src/rust/src/note_encryption.rs @@ -0,0 +1,145 @@ +use std::convert::TryInto; + +use zcash_note_encryption::{ + try_output_recovery_with_ovk, Domain, EphemeralKeyBytes, ShieldedOutput, ENC_CIPHERTEXT_SIZE, +}; +use zcash_primitives::{ + consensus::BlockHeight, + keys::OutgoingViewingKey, + memo::MemoBytes, + sapling::{ + self, + note_encryption::{PreparedIncomingViewingKey, SaplingDomain}, + value::ValueCommitment, + SaplingIvk, + }, +}; + +use crate::{bridge::ffi::SaplingShieldedOutput, params::Network}; + +/// Trial decryption of the full note plaintext by the recipient. +/// +/// Attempts to decrypt and validate the given shielded output using the given `raw_ivk`. +/// If successful, the corresponding note and memo are returned, along with the address to +/// which the note was sent. +/// +/// Implements section 4.19.2 of the +/// [Zcash Protocol Specification](https://zips.z.cash/protocol/protocol.pdf#decryptivk). +pub(crate) fn try_sapling_note_decryption( + network: &Network, + height: u32, + raw_ivk: &[u8; 32], + output: SaplingShieldedOutput, +) -> Result, &'static str> { + let ivk = parse_and_prepare_sapling_ivk(raw_ivk) + .ok_or("Invalid Sapling ivk passed to wallet::try_sapling_note_decryption()")?; + + let (note, recipient, memo) = sapling::note_encryption::try_sapling_note_decryption( + network, + BlockHeight::from_u32(height), + &ivk, + &output, + ) + .ok_or("Decryption failed")?; + + Ok(Box::new(DecryptedSaplingOutput { + note, + recipient, + memo, + })) +} + +/// Recovery of the full note plaintext by the sender. +/// +/// Attempts to decrypt and validate the given shielded output using the given `ovk`. +/// If successful, the corresponding note and memo are returned, along with the address to +/// which the note was sent. +/// +/// Implements [Zcash Protocol Specification section 4.19.3][decryptovk]. +/// +/// [decryptovk]: https://zips.z.cash/protocol/protocol.pdf#decryptovk +pub(crate) fn try_sapling_output_recovery( + network: &Network, + height: u32, + ovk: [u8; 32], + output: SaplingShieldedOutput, +) -> Result, &'static str> { + let domain = SaplingDomain::for_height(*network, BlockHeight::from_u32(height)); + + let cv = Option::from(ValueCommitment::from_bytes_not_small_order(&output.cv)) + .ok_or("Invalid output.cv passed to wallet::try_sapling_note_decryption()")?; + + let (note, recipient, memo) = try_output_recovery_with_ovk( + &domain, + &OutgoingViewingKey(ovk), + &output, + &cv, + &output.out_ciphertext, + ) + .ok_or("Decryption failed")?; + + Ok(Box::new(DecryptedSaplingOutput { + note, + recipient, + memo, + })) +} + +/// Parses and validates a Sapling incoming viewing key, and prepares it for decryption. +pub(crate) fn parse_and_prepare_sapling_ivk( + raw_ivk: &[u8; 32], +) -> Option { + jubjub::Fr::from_bytes(raw_ivk) + .map(|scalar_ivk| PreparedIncomingViewingKey::new(&SaplingIvk(scalar_ivk))) + .into() +} + +impl ShieldedOutput, ENC_CIPHERTEXT_SIZE> for SaplingShieldedOutput { + fn ephemeral_key(&self) -> EphemeralKeyBytes { + EphemeralKeyBytes(self.ephemeral_key) + } + + fn cmstar_bytes(&self) -> as Domain>::ExtractedCommitmentBytes { + self.cmu + } + + fn enc_ciphertext(&self) -> &[u8; ENC_CIPHERTEXT_SIZE] { + &self.enc_ciphertext + } +} + +/// A Sapling output that we successfully decrypted with an `ivk`. +pub(crate) struct DecryptedSaplingOutput { + note: sapling::Note, + recipient: sapling::PaymentAddress, + memo: MemoBytes, +} + +impl DecryptedSaplingOutput { + pub(crate) fn note_value(&self) -> u64 { + self.note.value().inner() + } + + pub(crate) fn note_rseed(&self) -> [u8; 32] { + match self.note.rseed() { + sapling::Rseed::BeforeZip212(rcm) => rcm.to_bytes(), + sapling::Rseed::AfterZip212(rseed) => *rseed, + } + } + + pub(crate) fn zip_212_enabled(&self) -> bool { + matches!(self.note.rseed(), sapling::Rseed::AfterZip212(_)) + } + + pub(crate) fn recipient_d(&self) -> [u8; 11] { + self.recipient.diversifier().0 + } + + pub(crate) fn recipient_pk_d(&self) -> [u8; 32] { + self.recipient.to_bytes()[11..].try_into().unwrap() + } + + pub(crate) fn memo(&self) -> [u8; 512] { + *self.memo.as_array() + } +} diff --git a/depend/zcash/src/rust/src/orchard_bundle.rs b/depend/zcash/src/rust/src/orchard_bundle.rs index f91d9888b..1ab0ace7d 100644 --- a/depend/zcash/src/rust/src/orchard_bundle.rs +++ b/depend/zcash/src/rust/src/orchard_bundle.rs @@ -1,83 +1,131 @@ +use std::{mem, ptr}; + +use memuse::DynamicUsage; use orchard::{ bundle::Authorized, + keys::OutgoingViewingKey, + note_encryption::OrchardDomain, primitives::redpallas::{Signature, SpendAuth}, }; -use zcash_primitives::transaction::components::Amount; - -#[cxx::bridge(namespace = "orchard_bundle")] -mod ffi { - extern "Rust" { - type Action; - type Bundle; - type OrchardBundle; - - fn cv(self: &Action) -> [u8; 32]; - fn nullifier(self: &Action) -> [u8; 32]; - fn rk(self: &Action) -> [u8; 32]; - fn cmx(self: &Action) -> [u8; 32]; - fn ephemeral_key(self: &Action) -> [u8; 32]; - fn enc_ciphertext(self: &Action) -> [u8; 580]; - fn out_ciphertext(self: &Action) -> [u8; 80]; - fn spend_auth_sig(self: &Action) -> [u8; 64]; - - unsafe fn from_tx_bundle(bundle: *const OrchardBundle) -> Box; - fn actions(self: &Bundle) -> Vec; - fn num_actions(self: &Bundle) -> usize; - fn enable_spends(self: &Bundle) -> bool; - fn enable_outputs(self: &Bundle) -> bool; - fn value_balance_zat(self: &Bundle) -> i64; - fn anchor(self: &Bundle) -> [u8; 32]; - fn proof(self: &Bundle) -> Vec; - fn binding_sig(self: &Bundle) -> [u8; 64]; - } -} +use zcash_note_encryption::try_output_recovery_with_ovk; +use zcash_primitives::transaction::components::{orchard as orchard_serialization, Amount}; + +use crate::{bridge::ffi, streams::CppStream}; pub struct Action(orchard::Action>); impl Action { - fn cv(&self) -> [u8; 32] { + pub(crate) fn cv(&self) -> [u8; 32] { self.0.cv_net().to_bytes() } - fn nullifier(&self) -> [u8; 32] { + pub(crate) fn nullifier(&self) -> [u8; 32] { self.0.nullifier().to_bytes() } - fn rk(&self) -> [u8; 32] { + pub(crate) fn rk(&self) -> [u8; 32] { self.0.rk().into() } - fn cmx(&self) -> [u8; 32] { + pub(crate) fn cmx(&self) -> [u8; 32] { self.0.cmx().to_bytes() } - fn ephemeral_key(&self) -> [u8; 32] { + pub(crate) fn ephemeral_key(&self) -> [u8; 32] { self.0.encrypted_note().epk_bytes } - fn enc_ciphertext(&self) -> [u8; 580] { + pub(crate) fn enc_ciphertext(&self) -> [u8; 580] { self.0.encrypted_note().enc_ciphertext } - fn out_ciphertext(&self) -> [u8; 80] { + pub(crate) fn out_ciphertext(&self) -> [u8; 80] { self.0.encrypted_note().out_ciphertext } - fn spend_auth_sig(&self) -> [u8; 64] { + pub(crate) fn spend_auth_sig(&self) -> [u8; 64] { self.0.authorization().into() } } +#[derive(Clone)] pub struct Bundle(Option>); -pub struct OrchardBundle; -unsafe fn from_tx_bundle(bundle: *const OrchardBundle) -> Box { - Box::new(Bundle( - { (bundle as *const orchard::Bundle).as_ref() }.cloned(), - )) + +pub(crate) fn none_orchard_bundle() -> Box { + Box::new(Bundle(None)) +} + +pub(crate) unsafe fn orchard_bundle_from_raw_box( + bundle: *mut ffi::OrchardBundlePtr, +) -> Box { + Bundle::from_raw_box(bundle) +} + +/// Parses an authorized Orchard bundle from the given stream. +pub(crate) fn parse_orchard_bundle(reader: &mut CppStream<'_>) -> Result, String> { + Bundle::parse(reader) } impl Bundle { - fn actions(&self) -> Vec { + pub(crate) unsafe fn from_raw_box(bundle: *mut ffi::OrchardBundlePtr) -> Box { + Box::new(Bundle(if bundle.is_null() { + None + } else { + let bundle: *mut orchard::Bundle = bundle.cast(); + Some(*Box::from_raw(bundle)) + })) + } + + /// Returns a copy of the value. + pub(crate) fn box_clone(&self) -> Box { + Box::new(self.clone()) + } + + /// Parses an authorized Orchard bundle from the given stream. + pub(crate) fn parse(reader: &mut CppStream<'_>) -> Result, String> { + match orchard_serialization::read_v5_bundle(reader) { + Ok(parsed) => Ok(Box::new(Bundle(parsed))), + Err(e) => Err(format!("Failed to parse Orchard bundle: {}", e)), + } + } + + /// Serializes an authorized Orchard bundle to the given stream. + /// + /// If `bundle == None`, this serializes `nActionsOrchard = 0`. + pub(crate) fn serialize(&self, writer: &mut CppStream<'_>) -> Result<(), String> { + orchard_serialization::write_v5_bundle(self.inner(), writer) + .map_err(|e| format!("Failed to serialize Orchard bundle: {}", e)) + } + + pub(crate) fn inner(&self) -> Option<&orchard::Bundle> { + self.0.as_ref() + } + + pub(crate) fn as_ptr(&self) -> *const ffi::OrchardBundlePtr { + if let Some(bundle) = self.inner() { + let ret: *const orchard::Bundle = bundle; + ret.cast() + } else { + ptr::null() + } + } + + /// Returns the amount of dynamically-allocated memory used by this bundle. + pub(crate) fn recursive_dynamic_usage(&self) -> usize { + self.inner() + // Bundles are boxed on the heap, so we count their own size as well as the size + // of `Vec`s they allocate. + .map(|bundle| mem::size_of_val(bundle) + bundle.dynamic_usage()) + // If the transaction has no Orchard component, nothing is allocated for it. + .unwrap_or(0) + } + + /// Returns whether the Orchard bundle is present. + pub(crate) fn is_present(&self) -> bool { + self.0.is_some() + } + + pub(crate) fn actions(&self) -> Vec { self.0 .iter() .flat_map(|b| b.actions().iter()) @@ -86,42 +134,54 @@ impl Bundle { .collect() } - fn num_actions(&self) -> usize { - self.0.as_ref().map(|b| b.actions().len()).unwrap_or(0) + pub(crate) fn num_actions(&self) -> usize { + self.inner().map(|b| b.actions().len()).unwrap_or(0) } - fn enable_spends(&self) -> bool { - self.0 - .as_ref() + /// Returns whether the Orchard bundle is present and spends are enabled. + pub(crate) fn enable_spends(&self) -> bool { + self.inner() .map(|b| b.flags().spends_enabled()) .unwrap_or(false) } - fn enable_outputs(&self) -> bool { - self.0 - .as_ref() + /// Returns whether the Orchard bundle is present and outputs are enabled. + pub(crate) fn enable_outputs(&self) -> bool { + self.inner() .map(|b| b.flags().outputs_enabled()) .unwrap_or(false) } - fn value_balance_zat(&self) -> i64 { - self.0 - .as_ref() + /// Returns the value balance for this Orchard bundle. + /// + /// A transaction with no Orchard component has a value balance of zero. + pub(crate) fn value_balance_zat(&self) -> i64 { + self.inner() .map(|b| b.value_balance().into()) + // From section 7.1 of the Zcash prototol spec: + // If valueBalanceOrchard is not present, then v^balanceOrchard is defined to be 0. .unwrap_or(0) } - fn anchor(&self) -> [u8; 32] { - self.0 - .as_ref() + /// Returns the anchor for the bundle. + /// + /// # Panics + /// + /// Panics if the bundle is not present. + pub(crate) fn anchor(&self) -> [u8; 32] { + self.inner() .expect("Bundle actions should have been checked to be non-empty") .anchor() .to_bytes() } - fn proof(&self) -> Vec { - self.0 - .as_ref() + /// Returns the proof for the bundle. + /// + /// # Panics + /// + /// Panics if the bundle is not present. + pub(crate) fn proof(&self) -> Vec { + self.inner() .expect("Bundle actions should have been checked to be non-empty") .authorization() .proof() @@ -129,12 +189,45 @@ impl Bundle { .to_vec() } - fn binding_sig(&self) -> [u8; 64] { - self.0 - .as_ref() + /// Returns the binding signature for the bundle. + /// + /// # Panics + /// + /// Panics if the bundle is not present. + pub(crate) fn binding_sig(&self) -> [u8; 64] { + self.inner() .expect("Bundle actions should have been checked to be non-empty") .authorization() .binding_signature() .into() } + + /// Returns whether all actions contained in the Orchard bundle can be decrypted with + /// the all-zeros OVK. + /// + /// Returns `true` if no Orchard actions are present. + /// + /// This should only be called on an Orchard bundle that is an element of a coinbase + /// transaction. + pub(crate) fn coinbase_outputs_are_valid(&self) -> bool { + if let Some(bundle) = self.inner() { + for act in bundle.actions() { + if try_output_recovery_with_ovk( + &OrchardDomain::for_action(act), + &OutgoingViewingKey::from([0u8; 32]), + act, + act.cv_net(), + &act.encrypted_note().out_ciphertext, + ) + .is_none() + { + return false; + } + } + } + + // Either there are no Orchard actions, or all of the outputs + // are decryptable with the all-zeros OVK. + true + } } diff --git a/depend/zcash/src/rust/src/orchard_ffi.rs b/depend/zcash/src/rust/src/orchard_ffi.rs index 1c198e6ba..7af27527d 100644 --- a/depend/zcash/src/rust/src/orchard_ffi.rs +++ b/depend/zcash/src/rust/src/orchard_ffi.rs @@ -1,301 +1,98 @@ -use std::{convert::TryInto, mem, ptr}; +use std::convert::TryInto; -use libc::size_t; -use memuse::DynamicUsage; -use orchard::{ - bundle::Authorized, keys::OutgoingViewingKey, note_encryption::OrchardDomain, Bundle, -}; use rand_core::OsRng; use tracing::{debug, error}; -use zcash_note_encryption::try_output_recovery_with_ovk; -use zcash_primitives::transaction::components::{orchard as orchard_serialization, Amount}; use crate::{ bundlecache::{orchard_bundle_validity_cache, orchard_bundle_validity_cache_mut, CacheEntries}, - streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb}, + orchard_bundle::Bundle, }; -#[no_mangle] -pub extern "C" fn orchard_bundle_clone( - bundle: *const Bundle, -) -> *mut Bundle { - unsafe { bundle.as_ref() } - .map(|bundle| Box::into_raw(Box::new(bundle.clone()))) - .unwrap_or(std::ptr::null_mut()) -} - -#[no_mangle] -pub extern "C" fn orchard_bundle_free(bundle: *mut Bundle) { - if !bundle.is_null() { - drop(unsafe { Box::from_raw(bundle) }); - } -} - -#[no_mangle] -pub extern "C" fn orchard_bundle_recursive_dynamic_usage( - bundle: *const Bundle, -) -> size_t { - unsafe { bundle.as_ref() } - // Bundles are boxed on the heap, so we count their own size as well as the size - // of `Vec`s they allocate. - .map(|bundle| mem::size_of_val(bundle) + bundle.dynamic_usage()) - // If the transaction has no Orchard component, nothing is allocated for it. - .unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn orchard_bundle_parse( - stream: Option, - read_cb: Option, - bundle_ret: *mut *mut Bundle, -) -> bool { - let reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap()); - - match orchard_serialization::read_v5_bundle(reader) { - Ok(parsed) => { - unsafe { - *bundle_ret = if let Some(bundle) = parsed { - Box::into_raw(Box::new(bundle)) - } else { - ptr::null_mut::>() - }; - }; - true - } - Err(e) => { - error!("Failed to parse Orchard bundle: {}", e); - false - } - } -} - -#[no_mangle] -pub extern "C" fn orchard_bundle_serialize( - bundle: *const Bundle, - stream: Option, - write_cb: Option, -) -> bool { - let bundle = unsafe { bundle.as_ref() }; - let writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap()); - - match orchard_serialization::write_v5_bundle(bundle, writer) { - Ok(()) => true, - Err(e) => { - error!("{}", e); - false - } - } -} - -#[no_mangle] - -pub extern "C" fn orchard_bundle_value_balance(bundle: *const Bundle) -> i64 { - unsafe { bundle.as_ref() } - .map(|bundle| (*bundle.value_balance()).into()) - // From section 7.1 of the Zcash prototol spec: - // If valueBalanceOrchard is not present, then v^balanceOrchard is defined to be 0. - .unwrap_or(0) -} - -#[no_mangle] -pub extern "C" fn orchard_bundle_actions_len(bundle: *const Bundle) -> usize { - if let Some(bundle) = unsafe { bundle.as_ref() } { - bundle.actions().len() - } else { - 0 - } -} - -#[no_mangle] -pub extern "C" fn orchard_bundle_nullifiers( - bundle: *const Bundle, - nullifiers_ret: *mut [u8; 32], - nullifiers_len: usize, -) -> bool { - if let Some(bundle) = unsafe { bundle.as_ref() } { - if nullifiers_len == bundle.actions().len() { - let res = unsafe { - assert!(!nullifiers_ret.is_null()); - std::slice::from_raw_parts_mut(nullifiers_ret, nullifiers_len) - }; - - for (action, nf_ret) in bundle.actions().iter().zip(res.iter_mut()) { - *nf_ret = action.nullifier().to_bytes(); - } - true - } else { - false - } - } else { - nullifiers_len == 0 - } -} - -#[no_mangle] -pub extern "C" fn orchard_bundle_anchor( - bundle: *const Bundle, - anchor_ret: *mut [u8; 32], -) -> bool { - if let Some((bundle, ret)) = unsafe { bundle.as_ref() }.zip(unsafe { anchor_ret.as_mut() }) { - ret.copy_from_slice(&bundle.anchor().to_bytes()); - - true - } else { - false - } -} - -pub struct BatchValidator { +struct BatchValidatorInner { validator: orchard::bundle::BatchValidator, queued_entries: CacheEntries, } +pub(crate) struct BatchValidator(Option); + /// Creates an Orchard bundle batch validation context. -/// -/// Please free this when you're done. -#[no_mangle] -pub extern "C" fn orchard_batch_validation_init(cache_store: bool) -> *mut BatchValidator { - let ctx = Box::new(BatchValidator { +pub(crate) fn orchard_batch_validation_init(cache_store: bool) -> Box { + Box::new(BatchValidator(Some(BatchValidatorInner { validator: orchard::bundle::BatchValidator::new(), queued_entries: CacheEntries::new(cache_store), - }); - Box::into_raw(ctx) -} - -/// Frees an Orchard bundle batch validation context returned from -/// [`orchard_batch_validation_init`]. -#[no_mangle] -pub extern "C" fn orchard_batch_validation_free(ctx: *mut BatchValidator) { - if !ctx.is_null() { - drop(unsafe { Box::from_raw(ctx) }); - } -} - -/// Adds an Orchard bundle to this batch. -#[no_mangle] -pub extern "C" fn orchard_batch_add_bundle( - batch: *mut BatchValidator, - bundle: *const Bundle, - sighash: *const [u8; 32], -) { - let batch = unsafe { batch.as_mut() }; - let bundle = unsafe { bundle.as_ref() }; - let sighash = unsafe { sighash.as_ref() }; - - match (batch, bundle, sighash) { - (Some(batch), Some(bundle), Some(sighash)) => { - let cache = orchard_bundle_validity_cache(); - - // Compute the cache entry for this bundle. - let cache_entry = { - let bundle_commitment = bundle.commitment(); - let bundle_authorizing_commitment = bundle.authorizing_commitment(); - cache.compute_entry( - bundle_commitment.0.as_bytes().try_into().unwrap(), - bundle_authorizing_commitment - .0 - .as_bytes() - .try_into() - .unwrap(), - sighash, - ) - }; + }))) +} + +impl BatchValidator { + /// Adds an Orchard bundle to this batch. + pub(crate) fn add_bundle(&mut self, bundle: Box, sighash: [u8; 32]) { + let batch = self.0.as_mut(); + let bundle = bundle.inner(); + + match (batch, bundle) { + (Some(batch), Some(bundle)) => { + let cache = orchard_bundle_validity_cache(); + + // Compute the cache entry for this bundle. + let cache_entry = { + let bundle_commitment = bundle.commitment(); + let bundle_authorizing_commitment = bundle.authorizing_commitment(); + cache.compute_entry( + bundle_commitment.0.as_bytes().try_into().unwrap(), + bundle_authorizing_commitment + .0 + .as_bytes() + .try_into() + .unwrap(), + &sighash, + ) + }; - // Check if this bundle's validation result exists in the cache. - if !cache.contains(cache_entry, &mut batch.queued_entries) { - // The bundle has been added to `inner.queued_entries` because it was not - // in the cache. We now add its authorization to the validation batch. - batch.validator.add_bundle(bundle, *sighash); + // Check if this bundle's validation result exists in the cache. + if !cache.contains(cache_entry, &mut batch.queued_entries) { + // The bundle has been added to `inner.queued_entries` because it was not + // in the cache. We now add its authorization to the validation batch. + batch.validator.add_bundle(bundle, sighash); + } } + (Some(_), None) => debug!("Tx has no Orchard component"), + (None, _) => error!("orchard::BatchValidator has already been used"), } - (_, _, None) => error!("orchard_batch_add_bundle() called without sighash!"), - (Some(_), None, Some(_)) => debug!("Tx has no Orchard component"), - (None, Some(_), _) => debug!("Orchard BatchValidator not provided, assuming disabled."), - (None, None, _) => (), // Boring, don't bother logging. } -} -/// Validates this batch. -/// -/// - Returns `true` if `batch` is null. -/// - Returns `false` if any item in the batch is invalid. -/// -/// The batch validation context is freed by this function. -/// -/// ## Consensus rules -/// -/// [§4.6](https://zips.z.cash/protocol/protocol.pdf#actiondesc): -/// - Canonical element encodings are enforced by [`orchard_bundle_parse`]. -/// - SpendAuthSig^Orchard validity is enforced here. -/// - Proof validity is enforced here. -/// -/// [§7.1](https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus): -/// - `bindingSigOrchard` validity is enforced here. -#[no_mangle] -pub extern "C" fn orchard_batch_validate(batch: *mut BatchValidator) -> bool { - if !batch.is_null() { - let batch = unsafe { Box::from_raw(batch) }; - let vk = - unsafe { crate::ORCHARD_VK.as_ref() }.expect("ORCHARD_VK should have been initialized"); - if batch.validator.validate(vk, OsRng) { - // `BatchValidator::validate()` is only called if every - // `BatchValidator::check_bundle()` returned `true`, so at this point - // every bundle that was added to `inner.queued_entries` has valid - // authorization. - orchard_bundle_validity_cache_mut().insert(batch.queued_entries); - true + /// Validates this batch. + /// + /// - Returns `true` if `batch` is null. + /// - Returns `false` if any item in the batch is invalid. + /// + /// The batch validation context is freed by this function. + /// + /// ## Consensus rules + /// + /// [§4.6](https://zips.z.cash/protocol/protocol.pdf#actiondesc): + /// - Canonical element encodings are enforced by [`orchard_bundle_parse`]. + /// - SpendAuthSig^Orchard validity is enforced here. + /// - Proof validity is enforced here. + /// + /// [§7.1](https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus): + /// - `bindingSigOrchard` validity is enforced here. + pub(crate) fn validate(&mut self) -> bool { + if let Some(inner) = self.0.take() { + let vk = unsafe { crate::ORCHARD_VK.as_ref() } + .expect("Parameters not loaded: ORCHARD_VK should have been initialized"); + if inner.validator.validate(vk, OsRng) { + // `BatchValidator::validate()` is only called if every + // `BatchValidator::check_bundle()` returned `true`, so at this point + // every bundle that was added to `inner.queued_entries` has valid + // authorization. + orchard_bundle_validity_cache_mut().insert(inner.queued_entries); + true + } else { + false + } } else { + error!("orchard::BatchValidator has already been used"); false } - } else { - // The orchard::BatchValidator C++ class uses null to represent a disabled batch - // validator. - debug!("Orchard BatchValidator not provided, assuming disabled."); - true } } - -#[no_mangle] -pub extern "C" fn orchard_bundle_outputs_enabled( - bundle: *const Bundle, -) -> bool { - let bundle = unsafe { bundle.as_ref() }; - bundle.map(|b| b.flags().outputs_enabled()).unwrap_or(false) -} - -#[no_mangle] -pub extern "C" fn orchard_bundle_spends_enabled(bundle: *const Bundle) -> bool { - let bundle = unsafe { bundle.as_ref() }; - bundle.map(|b| b.flags().spends_enabled()).unwrap_or(false) -} - -/// Returns whether all actions contained in the Orchard bundle -/// can be decrypted with the all-zeros OVK. Returns `true` -/// if no Orchard actions are present. -/// -/// This should only be called on an Orchard bundle that is -/// an element of a coinbase transaction. -#[no_mangle] -pub extern "C" fn orchard_bundle_coinbase_outputs_are_valid( - bundle: *const Bundle, -) -> bool { - if let Some(bundle) = unsafe { bundle.as_ref() } { - for act in bundle.actions() { - if try_output_recovery_with_ovk( - &OrchardDomain::for_action(act), - &OutgoingViewingKey::from([0u8; 32]), - act, - act.cv_net(), - &act.encrypted_note().out_ciphertext, - ) - .is_none() - { - return false; - } - } - } - - // Either there are no Orchard actions, or all of the outputs - // are decryptable with the all-zeros OVK. - true -} diff --git a/depend/zcash/src/rust/src/params.rs b/depend/zcash/src/rust/src/params.rs new file mode 100644 index 000000000..1a408eb5c --- /dev/null +++ b/depend/zcash/src/rust/src/params.rs @@ -0,0 +1,146 @@ +use memuse::DynamicUsage; +use zcash_primitives::{ + consensus::{self, BlockHeight}, + constants, +}; + +/// Chain parameters for the networks supported by `zcashd`. +#[derive(Clone, Copy)] +pub(crate) enum Network { + Consensus(consensus::Network), + RegTest { + overwinter: Option, + sapling: Option, + blossom: Option, + heartwood: Option, + canopy: Option, + nu5: Option, + }, +} + +impl DynamicUsage for Network { + fn dynamic_usage(&self) -> usize { + match self { + Network::Consensus(params) => params.dynamic_usage(), + // We know that `Option` allocates no memory. + Network::RegTest { .. } => 0, + } + } + + fn dynamic_usage_bounds(&self) -> (usize, Option) { + match self { + Network::Consensus(params) => params.dynamic_usage_bounds(), + // We know that `Option` allocates no memory. + Network::RegTest { .. } => (0, Some(0)), + } + } +} + +/// Constructs a `Network` from the given network string. +/// +/// The heights are only for constructing a regtest network, and are ignored otherwise. +pub(crate) fn network( + network: &str, + overwinter: i32, + sapling: i32, + blossom: i32, + heartwood: i32, + canopy: i32, + nu5: i32, +) -> Result, &'static str> { + let i32_to_optional_height = |n: i32| { + if n.is_negative() { + None + } else { + Some(BlockHeight::from_u32(n.unsigned_abs())) + } + }; + + let params = match network { + "main" => Network::Consensus(consensus::Network::MainNetwork), + "test" => Network::Consensus(consensus::Network::TestNetwork), + "regtest" => Network::RegTest { + overwinter: i32_to_optional_height(overwinter), + sapling: i32_to_optional_height(sapling), + blossom: i32_to_optional_height(blossom), + heartwood: i32_to_optional_height(heartwood), + canopy: i32_to_optional_height(canopy), + nu5: i32_to_optional_height(nu5), + }, + _ => return Err("Unsupported network kind"), + }; + + Ok(Box::new(params)) +} + +impl consensus::Parameters for Network { + fn activation_height(&self, nu: consensus::NetworkUpgrade) -> Option { + match self { + Self::Consensus(params) => params.activation_height(nu), + Self::RegTest { + overwinter, + sapling, + blossom, + heartwood, + canopy, + nu5, + } => match nu { + consensus::NetworkUpgrade::Overwinter => *overwinter, + consensus::NetworkUpgrade::Sapling => *sapling, + consensus::NetworkUpgrade::Blossom => *blossom, + consensus::NetworkUpgrade::Heartwood => *heartwood, + consensus::NetworkUpgrade::Canopy => *canopy, + consensus::NetworkUpgrade::Nu5 => *nu5, + }, + } + } + + fn coin_type(&self) -> u32 { + match self { + Self::Consensus(params) => params.coin_type(), + Self::RegTest { .. } => constants::regtest::COIN_TYPE, + } + } + + fn address_network(&self) -> Option { + match self { + Self::Consensus(params) => params.address_network(), + Self::RegTest { .. } => Some(zcash_address::Network::Regtest), + } + } + + fn hrp_sapling_extended_spending_key(&self) -> &str { + match self { + Self::Consensus(params) => params.hrp_sapling_extended_spending_key(), + Self::RegTest { .. } => constants::regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY, + } + } + + fn hrp_sapling_extended_full_viewing_key(&self) -> &str { + match self { + Self::Consensus(params) => params.hrp_sapling_extended_full_viewing_key(), + Self::RegTest { .. } => constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + } + } + + fn hrp_sapling_payment_address(&self) -> &str { + match self { + Self::Consensus(params) => params.hrp_sapling_payment_address(), + Self::RegTest { .. } => constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS, + } + } + + fn b58_pubkey_address_prefix(&self) -> [u8; 2] { + match self { + Self::Consensus(params) => params.b58_pubkey_address_prefix(), + Self::RegTest { .. } => constants::regtest::B58_PUBKEY_ADDRESS_PREFIX, + } + } + + fn b58_script_address_prefix(&self) -> [u8; 2] { + match self { + Self::Consensus(params) => params.b58_script_address_prefix(), + Self::RegTest { .. } => constants::regtest::B58_SCRIPT_ADDRESS_PREFIX, + } + } +} diff --git a/depend/zcash/src/rust/src/rustzcash.rs b/depend/zcash/src/rust/src/rustzcash.rs index 39906f5cb..8f3762be5 100644 --- a/depend/zcash/src/rust/src/rustzcash.rs +++ b/depend/zcash/src/rust/src/rustzcash.rs @@ -23,6 +23,7 @@ use bellman::groth16::{self, Parameters, PreparedVerifyingKey}; use blake2s_simd::Params as Blake2sParams; use bls12_381::Bls12; use group::{cofactor::CofactorGroup, GroupEncoding}; +use incrementalmerkletree::Hashable; use libc::{c_uchar, size_t}; use rand_core::{OsRng, RngCore}; use std::fs::File; @@ -43,11 +44,19 @@ use std::ffi::OsString; #[cfg(target_os = "windows")] use std::os::windows::ffi::OsStringExt; +use zcash_note_encryption::{Domain, EphemeralKeyBytes}; use zcash_primitives::{ + consensus::Network, constants::{CRH_IVK_PERSONALIZATION, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR}, + merkle_tree::HashSer, sapling::{ - keys::FullViewingKey, merkle_hash, note_encryption::sapling_ka_agree, redjubjub, spend_sig, - Diversifier, Note, NullifierDerivingKey, Rseed, + keys::FullViewingKey, + merkle_hash, + note::{ExtractedNoteCommitment, NoteCommitment}, + note_encryption::{PreparedIncomingViewingKey, SaplingDomain}, + redjubjub, spend_sig, + value::NoteValue, + Diversifier, Node, Note, NullifierDerivingKey, PaymentAddress, Rseed, SaplingIvk, }, zip32::{self, sapling_address, sapling_derive_internal_fvk, sapling_find_address}, }; @@ -61,17 +70,22 @@ mod streams_ffi; mod tracing_ffi; mod zcashd_orchard; +mod bridge; + mod address_ffi; mod builder_ffi; mod bundlecache; mod history_ffi; mod incremental_merkle_tree; -mod incremental_merkle_tree_ffi; mod init_ffi; +mod merkle_frontier; +mod note_encryption; mod orchard_bundle; mod orchard_ffi; mod orchard_keys_ffi; +mod params; mod sapling; +mod streams; mod transaction_ffi; mod unified_keys_ffi; mod wallet; @@ -207,12 +221,12 @@ pub extern "C" fn librustzcash_init_zksnark_params( /// `result` must be a valid pointer to 32 bytes which will be written. #[no_mangle] pub extern "C" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) { - let tmp = Note::uncommitted().to_bytes(); - // Should be okay, caller is responsible for ensuring the pointer // is a valid pointer to 32 bytes that can be mutated. let result = unsafe { &mut *result }; - *result = tmp; + Node::empty_leaf() + .write(&mut result[..]) + .expect("Sapling leaves are 32 bytes"); } /// Computes a merkle tree hash for a given depth. The `depth` parameter should @@ -355,37 +369,29 @@ pub extern "C" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { // Private utility function to get Note from C parameters fn priv_get_note( - zip216_enabled: bool, diversifier: *const [c_uchar; 11], pk_d: *const [c_uchar; 32], value: u64, rcm: *const [c_uchar; 32], ) -> Result { - let diversifier = Diversifier(unsafe { *diversifier }); - let g_d = diversifier.g_d().ok_or(())?; - - let pk_d = de_ct(if zip216_enabled { - jubjub::ExtendedPoint::from_bytes(unsafe { &*pk_d }) - } else { - jubjub::AffinePoint::from_bytes_pre_zip216_compatibility(unsafe { *pk_d }).map(|p| p.into()) - }) - .ok_or(())?; - - let pk_d = de_ct(pk_d.into_subgroup()).ok_or(())?; + let recipient_bytes = { + let mut tmp = [0; 43]; + tmp[..11].copy_from_slice(unsafe { &*diversifier }); + tmp[11..].copy_from_slice(unsafe { &*pk_d }); + tmp + }; + let recipient = PaymentAddress::from_bytes(&recipient_bytes).ok_or(())?; // Deserialize randomness // If this is after ZIP 212, the caller has calculated rcm, and we don't need to call // Note::derive_esk, so we just pretend the note was using this rcm all along. let rseed = Rseed::BeforeZip212(de_ct(jubjub::Scalar::from_bytes(unsafe { &*rcm })).ok_or(())?); - let note = Note { - value, - g_d, - pk_d, + Ok(Note::from_parts( + recipient, + NoteValue::from_raw(value), rseed, - }; - - Ok(note) + )) } /// Compute a Sapling nullifier. @@ -406,7 +412,8 @@ pub extern "C" fn librustzcash_sapling_compute_nf( ) -> bool { // ZIP 216: Nullifier derivation is not consensus-critical // (nullifiers are revealed, not calculated by consensus). - let note = match priv_get_note(true, diversifier, pk_d, value, rcm) { + // In any case, ZIP 216 is now enabled retroactively. + let note = match priv_get_note(diversifier, pk_d, value, rcm) { Ok(p) => p, Err(_) => return false, }; @@ -436,57 +443,74 @@ pub extern "C" fn librustzcash_sapling_compute_nf( /// Returns false if `diversifier` or `pk_d` is not valid. #[no_mangle] pub extern "C" fn librustzcash_sapling_compute_cmu( - zip216_enabled: bool, diversifier: *const [c_uchar; 11], pk_d: *const [c_uchar; 32], value: u64, rcm: *const [c_uchar; 32], result: *mut [c_uchar; 32], ) -> bool { - let note = match priv_get_note(zip216_enabled, diversifier, pk_d, value, rcm) { - Ok(p) => p, - Err(_) => return false, + let get_cm = || -> Result { + let diversifier = Diversifier(unsafe { *diversifier }); + let g_d = diversifier.g_d().ok_or(())?; + + let pk_d = de_ct(jubjub::ExtendedPoint::from_bytes(unsafe { &*pk_d })).ok_or(())?; + let pk_d = de_ct(pk_d.into_subgroup()).ok_or(())?; + + let rcm = de_ct(jubjub::Scalar::from_bytes(unsafe { &*rcm })).ok_or(())?; + + Ok(NoteCommitment::temporary_zcashd_derive( + g_d.to_bytes(), + pk_d.to_bytes(), + NoteValue::from_raw(value), + rcm, + )) + }; + + let cmu = match get_cm() { + Ok(cm) => ExtractedNoteCommitment::from(cm), + Err(()) => return false, }; let result = unsafe { &mut *result }; - *result = note.cmu().to_bytes(); + *result = cmu.to_bytes(); true } -/// Computes \[sk\] \[8\] P for some 32-byte point P, and 32-byte Fs. +/// Computes KDF^Sapling(KA^Agree(sk, P), ephemeral_key). /// -/// If P or sk are invalid, returns false. Otherwise, the result is written to -/// the 32-byte `result` buffer. +/// `p` and `sk` must point to 32-byte buffers. If `p` does not represent a compressed +/// Jubjub point or `sk` does not represent a canonical Jubjub scalar, this function +/// returns `false`. Otherwise, it writes the result to the 32-byte `result` buffer and +/// returns `true`. #[no_mangle] -pub extern "C" fn librustzcash_sapling_ka_agree( - zip216_enabled: bool, +pub extern "C" fn librustzcash_sapling_ka_derive_symmetric_key( p: *const [c_uchar; 32], sk: *const [c_uchar; 32], + ephemeral_key: *const [c_uchar; 32], result: *mut [c_uchar; 32], ) -> bool { - // Deserialize p - let p = match de_ct(if zip216_enabled { - jubjub::ExtendedPoint::from_bytes(unsafe { &*p }) - } else { - jubjub::AffinePoint::from_bytes_pre_zip216_compatibility(unsafe { *p }).map(|p| p.into()) - }) { - Some(p) => p, + // Deserialize p (representing either epk or pk_d; we can handle them identically). + let epk = match SaplingDomain::::epk(&EphemeralKeyBytes(unsafe { *p })) { + Some(p) => SaplingDomain::::prepare_epk(p), None => return false, }; - // Deserialize sk - let sk = match de_ct(jubjub::Scalar::from_bytes(unsafe { &*sk })) { - Some(p) => p, + // Deserialize sk (either ivk or esk; we can handle them identically). + let ivk = match de_ct(jubjub::Scalar::from_bytes(unsafe { &*sk })) { + Some(p) => PreparedIncomingViewingKey::new(&SaplingIvk(p)), None => return false, }; // Compute key agreement - let ka = sapling_ka_agree(&sk, &p); + let secret = SaplingDomain::::ka_agree_dec(&ivk, &epk); // Produce result let result = unsafe { &mut *result }; - *result = ka.to_bytes(); + result.clone_from_slice( + SaplingDomain::::kdf(secret, &EphemeralKeyBytes(unsafe { *ephemeral_key })) + .as_bytes(), + ); true } @@ -564,12 +588,11 @@ pub extern "C" fn librustzcash_sprout_prove( vpub_new: u64, ) { // Load parameters from disk - let sprout_fs = File::open( - unsafe { &SPROUT_GROTH16_PARAMS_PATH } - .as_ref() - .expect("parameters should have been initialized"), - ) - .expect("couldn't load Sprout groth16 parameters file"); + let sprout_fs = + File::open(unsafe { &SPROUT_GROTH16_PARAMS_PATH }.as_ref().expect( + "Parameters not loaded: SPROUT_GROTH16_PARAMS_PATH should have been initialized", + )) + .expect("couldn't load Sprout groth16 parameters file"); let mut sprout_fs = BufReader::with_capacity(1024 * 1024, sprout_fs); @@ -635,7 +658,8 @@ pub extern "C" fn librustzcash_sprout_verify( unsafe { &*cm2 }, vpub_old, vpub_new, - unsafe { SPROUT_GROTH16_VK.as_ref() }.expect("parameters should have been initialized"), + unsafe { SPROUT_GROTH16_VK.as_ref() } + .expect("Parameters not loaded: SPROUT_GROTH16_VK should have been initialized"), ) } diff --git a/depend/zcash/src/rust/src/sapling.rs b/depend/zcash/src/rust/src/sapling.rs index 59536d060..95bc38552 100644 --- a/depend/zcash/src/rust/src/sapling.rs +++ b/depend/zcash/src/rust/src/sapling.rs @@ -12,7 +12,9 @@ use zcash_note_encryption::EphemeralKeyBytes; use zcash_primitives::{ merkle_tree::MerklePath, sapling::{ + note::ExtractedNoteCommitment, redjubjub::{self, Signature}, + value::ValueCommitment, Diversifier, Nullifier, PaymentAddress, ProofGenerationKey, Rseed, }, transaction::{ @@ -34,106 +36,7 @@ use crate::bundlecache::{ sapling_bundle_validity_cache, sapling_bundle_validity_cache_mut, CacheEntries, }; -#[cxx::bridge(namespace = "sapling")] -mod ffi { - extern "Rust" { - type Bundle; - type BundleAssembler; - fn new_bundle_assembler() -> Box; - fn add_spend( - self: &mut BundleAssembler, - cv: &[u8; 32], - anchor: &[u8; 32], - nullifier: [u8; 32], - rk: &[u8; 32], - zkproof: [u8; 192], // GROTH_PROOF_SIZE - spend_auth_sig: &[u8; 64], - ) -> bool; - fn add_output( - self: &mut BundleAssembler, - cv: &[u8; 32], - cmu: &[u8; 32], - ephemeral_key: [u8; 32], - enc_ciphertext: [u8; 580], - out_ciphertext: [u8; 80], - zkproof: [u8; 192], // GROTH_PROOF_SIZE - ) -> bool; - fn finish_bundle_assembly( - assembler: Box, - value_balance: i64, - binding_sig: [u8; 64], - ) -> Box; - - type Prover; - - fn init_prover() -> Box; - #[allow(clippy::too_many_arguments)] - fn create_spend_proof( - self: &mut Prover, - ak: &[u8; 32], - nsk: &[u8; 32], - diversifier: &[u8; 11], - rcm: &[u8; 32], - ar: &[u8; 32], - value: u64, - anchor: &[u8; 32], - merkle_path: &[u8; 1065], // 1 + 33 * SAPLING_TREE_DEPTH + 8 - cv: &mut [u8; 32], - rk_out: &mut [u8; 32], - zkproof: &mut [u8; 192], // GROTH_PROOF_SIZE - ) -> bool; - fn create_output_proof( - self: &mut Prover, - esk: &[u8; 32], - payment_address: &[u8; 43], - rcm: &[u8; 32], - value: u64, - cv: &mut [u8; 32], - zkproof: &mut [u8; 192], // GROTH_PROOF_SIZE - ) -> bool; - fn binding_sig( - self: &mut Prover, - value_balance: i64, - sighash: &[u8; 32], - result: &mut [u8; 64], - ) -> bool; - - type Verifier; - - fn init_verifier() -> Box; - #[allow(clippy::too_many_arguments)] - fn check_spend( - self: &mut Verifier, - cv: &[u8; 32], - anchor: &[u8; 32], - nullifier: &[u8; 32], - rk: &[u8; 32], - zkproof: &[u8; 192], // GROTH_PROOF_SIZE - spend_auth_sig: &[u8; 64], - sighash_value: &[u8; 32], - ) -> bool; - fn check_output( - self: &mut Verifier, - cv: &[u8; 32], - cm: &[u8; 32], - ephemeral_key: &[u8; 32], - zkproof: &[u8; 192], // GROTH_PROOF_SIZE - ) -> bool; - fn final_check( - self: &Verifier, - value_balance: i64, - binding_sig: &[u8; 64], - sighash_value: &[u8; 32], - ) -> bool; - - type BatchValidator; - fn init_batch_validator(cache_store: bool) -> Box; - fn check_bundle(self: &mut BatchValidator, bundle: Box, sighash: [u8; 32]) -> bool; - fn validate(self: &mut BatchValidator) -> bool; - } -} - -struct Bundle(sapling::Bundle); +pub(crate) struct Bundle(sapling::Bundle); impl Bundle { fn commitment>(&self, digester: D) -> D::SaplingDigest { @@ -141,12 +44,12 @@ impl Bundle { } } -struct BundleAssembler { +pub(crate) struct BundleAssembler { shielded_spends: Vec>, shielded_outputs: Vec>, // GROTH_PROOF_SIZE } -fn new_bundle_assembler() -> Box { +pub(crate) fn new_bundle_assembler() -> Box { Box::new(BundleAssembler { shielded_spends: vec![], shielded_outputs: vec![], @@ -154,7 +57,7 @@ fn new_bundle_assembler() -> Box { } impl BundleAssembler { - fn add_spend( + pub(crate) fn add_spend( self: &mut BundleAssembler, cv: &[u8; 32], anchor: &[u8; 32], @@ -164,7 +67,7 @@ impl BundleAssembler { spend_auth_sig: &[u8; 64], ) -> bool { // Deserialize the value commitment - let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) { + let cv = match Option::from(ValueCommitment::from_bytes_not_small_order(cv)) { Some(p) => p, None => return false, }; @@ -188,19 +91,20 @@ impl BundleAssembler { Err(_) => return false, }; - self.shielded_spends.push(sapling::SpendDescription { - cv, - anchor, - nullifier: Nullifier(nullifier), - rk, - zkproof, - spend_auth_sig, - }); + self.shielded_spends + .push(sapling::SpendDescription::temporary_zcashd_from_parts( + cv, + anchor, + Nullifier(nullifier), + rk, + zkproof, + spend_auth_sig, + )); true } - fn add_output( + pub(crate) fn add_output( self: &mut BundleAssembler, cv: &[u8; 32], cm: &[u8; 32], @@ -210,33 +114,33 @@ impl BundleAssembler { zkproof: [u8; 192], // GROTH_PROOF_SIZE ) -> bool { // Deserialize the value commitment - let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) { + let cv = match Option::from(ValueCommitment::from_bytes_not_small_order(cv)) { Some(p) => p, None => return false, }; - // Deserialize the commitment, which should be an element - // of Fr. - let cmu = match de_ct(bls12_381::Scalar::from_bytes(cm)) { + // Deserialize the extracted note commitment. + let cmu = match Option::from(ExtractedNoteCommitment::from_bytes(cm)) { Some(a) => a, None => return false, }; - self.shielded_outputs.push(sapling::OutputDescription { - cv, - cmu, - ephemeral_key: EphemeralKeyBytes(ephemeral_key), - enc_ciphertext, - out_ciphertext, - zkproof, - }); + self.shielded_outputs + .push(sapling::OutputDescription::temporary_zcashd_from_parts( + cv, + cmu, + EphemeralKeyBytes(ephemeral_key), + enc_ciphertext, + out_ciphertext, + zkproof, + )); true } } #[allow(clippy::boxed_local)] -fn finish_bundle_assembly( +pub(crate) fn finish_bundle_assembly( assembler: Box, value_balance: i64, binding_sig: [u8; 64], @@ -244,23 +148,23 @@ fn finish_bundle_assembly( let value_balance = Amount::from_i64(value_balance).expect("parsed elsewhere"); let binding_sig = redjubjub::Signature::read(&binding_sig[..]).expect("parsed elsewhere"); - Box::new(Bundle(sapling::Bundle { - shielded_spends: assembler.shielded_spends, - shielded_outputs: assembler.shielded_outputs, + Box::new(Bundle(sapling::Bundle::temporary_zcashd_from_parts( + assembler.shielded_spends, + assembler.shielded_outputs, value_balance, - authorization: sapling::Authorized { binding_sig }, - })) + sapling::Authorized { binding_sig }, + ))) } -struct Prover(SaplingProvingContext); +pub(crate) struct Prover(SaplingProvingContext); -fn init_prover() -> Box { +pub(crate) fn init_prover() -> Box { Box::new(Prover(SaplingProvingContext::new())) } impl Prover { #[allow(clippy::too_many_arguments)] - fn create_spend_proof( + pub(crate) fn create_spend_proof( &mut self, ak: &[u8; 32], nsk: &[u8; 32], @@ -335,8 +239,12 @@ impl Prover { value, anchor, merkle_path, - unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(), - &prepare_verifying_key(unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap()), + unsafe { SAPLING_SPEND_PARAMS.as_ref() }.expect( + "Parameters not loaded: SAPLING_SPEND_PARAMS should have been initialized", + ), + &prepare_verifying_key(unsafe { SAPLING_SPEND_VK.as_ref() }.expect( + "Parameters not loaded: SAPLING_SPEND_VK should have been initialized", + )), ) .expect("proving should not fail"); @@ -354,7 +262,8 @@ impl Prover { true } - fn create_output_proof( + + pub(crate) fn create_output_proof( &mut self, esk: &[u8; 32], payment_address: &[u8; 43], @@ -387,7 +296,9 @@ impl Prover { payment_address, rcm, value, - unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(), + unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.expect( + "Parameters not loaded: SAPLING_OUTPUT_PARAMS should have been initialized", + ), ); // Write the proof out to the caller @@ -400,7 +311,8 @@ impl Prover { true } - fn binding_sig( + + pub(crate) fn binding_sig( &mut self, value_balance: i64, sighash: &[u8; 32], @@ -425,9 +337,9 @@ impl Prover { } } -struct Verifier(SaplingVerificationContext); +pub(crate) struct Verifier(SaplingVerificationContext); -fn init_verifier() -> Box { +pub(crate) fn init_verifier() -> Box { // We consider ZIP 216 active all of the time because blocks prior to NU5 // activation (on mainnet and testnet) did not contain Sapling transactions // that violated its canonicity rule. @@ -436,7 +348,7 @@ fn init_verifier() -> Box { impl Verifier { #[allow(clippy::too_many_arguments)] - fn check_spend( + pub(crate) fn check_spend( &mut self, cv: &[u8; 32], anchor: &[u8; 32], @@ -447,7 +359,7 @@ impl Verifier { sighash_value: &[u8; 32], ) -> bool { // Deserialize the value commitment - let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) { + let cv = match Option::from(ValueCommitment::from_bytes_not_small_order(cv)) { Some(p) => p, None => return false, }; @@ -478,17 +390,21 @@ impl Verifier { }; self.0.check_spend( - cv, + &cv, anchor, nullifier, rk, sighash_value, spend_auth_sig, zkproof, - &prepare_verifying_key(unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap()), + &prepare_verifying_key( + unsafe { SAPLING_SPEND_VK.as_ref() } + .expect("Parameters not loaded: SAPLING_SPEND_VK should have been initialized"), + ), ) } - fn check_output( + + pub(crate) fn check_output( &mut self, cv: &[u8; 32], cm: &[u8; 32], @@ -496,14 +412,13 @@ impl Verifier { zkproof: &[u8; GROTH_PROOF_SIZE], ) -> bool { // Deserialize the value commitment - let cv = match de_ct(jubjub::ExtendedPoint::from_bytes(cv)) { + let cv = match Option::from(ValueCommitment::from_bytes_not_small_order(cv)) { Some(p) => p, None => return false, }; - // Deserialize the commitment, which should be an element - // of Fr. - let cm = match de_ct(bls12_381::Scalar::from_bytes(cm)) { + // Deserialize the extracted note commitment. + let cmu = match Option::from(ExtractedNoteCommitment::from_bytes(cm)) { Some(a) => a, None => return false, }; @@ -521,14 +436,19 @@ impl Verifier { }; self.0.check_output( - cv, - cm, + &cv, + cmu, epk, zkproof, - &prepare_verifying_key(unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap()), + &prepare_verifying_key( + unsafe { SAPLING_OUTPUT_VK.as_ref() }.expect( + "Parameters not loaded: SAPLING_OUTPUT_VK should have been initialized", + ), + ), ) } - fn final_check( + + pub(crate) fn final_check( &self, value_balance: i64, binding_sig: &[u8; 64], @@ -555,9 +475,9 @@ struct BatchValidatorInner { queued_entries: CacheEntries, } -struct BatchValidator(Option); +pub(crate) struct BatchValidator(Option); -fn init_batch_validator(cache_store: bool) -> Box { +pub(crate) fn init_batch_validator(cache_store: bool) -> Box { Box::new(BatchValidator(Some(BatchValidatorInner { validator: sapling_proofs::BatchValidator::new(), queued_entries: CacheEntries::new(cache_store), @@ -579,7 +499,7 @@ impl BatchValidator { /// the global bundle validity cache, it will have been removed (and this method will /// return `true`). #[allow(clippy::boxed_local)] - fn check_bundle(&mut self, bundle: Box, sighash: [u8; 32]) -> bool { + pub(crate) fn check_bundle(&mut self, bundle: Box, sighash: [u8; 32]) -> bool { if let Some(inner) = &mut self.0 { let cache = sapling_bundle_validity_cache(); @@ -622,11 +542,14 @@ impl BatchValidator { /// If this batch was configured to cache the results, then if this method returns /// `true` every bundle added to the batch will have also been added to the global /// bundle validity cache. - fn validate(&mut self) -> bool { + pub(crate) fn validate(&mut self) -> bool { if let Some(inner) = self.0.take() { if inner.validator.validate( - unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), - unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(), + unsafe { SAPLING_SPEND_VK.as_ref() } + .expect("Parameters not loaded: SAPLING_SPEND_VK should have been initialized"), + unsafe { SAPLING_OUTPUT_VK.as_ref() }.expect( + "Parameters not loaded: SAPLING_OUTPUT_VK should have been initialized", + ), OsRng, ) { // `Self::validate()` is only called if every `Self::check_bundle()` diff --git a/depend/zcash/src/rust/src/streams.rs b/depend/zcash/src/rust/src/streams.rs new file mode 100644 index 000000000..98c7d22d3 --- /dev/null +++ b/depend/zcash/src/rust/src/streams.rs @@ -0,0 +1,122 @@ +use std::io; +use std::pin::Pin; + +#[cxx::bridge] +pub(crate) mod ffi { + extern "C++" { + include!("hash.h"); + include!("streams.h"); + + #[cxx_name = "RustDataStream"] + type RustStream; + unsafe fn read_u8(self: Pin<&mut RustStream>, pch: *mut u8, nSize: usize) -> Result<()>; + unsafe fn write_u8(self: Pin<&mut RustStream>, pch: *const u8, nSize: usize) -> Result<()>; + + type CAutoFile; + unsafe fn read_u8(self: Pin<&mut CAutoFile>, pch: *mut u8, nSize: usize) -> Result<()>; + unsafe fn write_u8(self: Pin<&mut CAutoFile>, pch: *const u8, nSize: usize) -> Result<()>; + + type CBufferedFile; + unsafe fn read_u8(self: Pin<&mut CBufferedFile>, pch: *mut u8, nSize: usize) -> Result<()>; + + type CHashWriter; + unsafe fn write_u8(self: Pin<&mut CHashWriter>, pch: *const u8, nSize: usize) + -> Result<()>; + + type CSizeComputer; + unsafe fn write_u8( + self: Pin<&mut CSizeComputer>, + pch: *const u8, + nSize: usize, + ) -> Result<()>; + } + + impl UniquePtr {} + impl UniquePtr {} + impl UniquePtr {} + impl UniquePtr {} + impl UniquePtr {} +} + +pub(crate) fn from_data(stream: Pin<&mut ffi::RustStream>) -> Box> { + Box::new(CppStream::Data(stream)) +} + +pub(crate) fn from_auto_file(file: Pin<&mut ffi::CAutoFile>) -> Box> { + Box::new(CppStream::AutoFile(file)) +} + +pub(crate) fn from_buffered_file(file: Pin<&mut ffi::CBufferedFile>) -> Box> { + Box::new(CppStream::BufferedFile(file)) +} + +pub(crate) fn from_hash_writer(writer: Pin<&mut ffi::CHashWriter>) -> Box> { + Box::new(CppStream::Hash(writer)) +} + +pub(crate) fn from_size_computer(sc: Pin<&mut ffi::CSizeComputer>) -> Box> { + Box::new(CppStream::Size(sc)) +} + +pub(crate) enum CppStream<'a> { + Data(Pin<&'a mut ffi::RustStream>), + AutoFile(Pin<&'a mut ffi::CAutoFile>), + BufferedFile(Pin<&'a mut ffi::CBufferedFile>), + Hash(Pin<&'a mut ffi::CHashWriter>), + Size(Pin<&'a mut ffi::CSizeComputer>), +} + +impl<'a> io::Read for CppStream<'a> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let pch = buf.as_mut_ptr(); + let len = buf.len(); + match self { + CppStream::Data(inner) => unsafe { inner.as_mut().read_u8(pch, len) } + .map(|()| buf.len()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)), + CppStream::AutoFile(inner) => unsafe { inner.as_mut().read_u8(pch, len) } + .map(|()| buf.len()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)), + CppStream::BufferedFile(inner) => unsafe { inner.as_mut().read_u8(pch, len) } + .map(|()| buf.len()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)), + CppStream::Hash(_) => Err(io::Error::new( + io::ErrorKind::Unsupported, + "Cannot read from CHashWriter", + )), + CppStream::Size(_) => Err(io::Error::new( + io::ErrorKind::Unsupported, + "Cannot read from CSizeComputer", + )), + } + } +} + +impl<'a> io::Write for CppStream<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + let pch = buf.as_ptr(); + let len = buf.len(); + match self { + CppStream::Data(inner) => unsafe { inner.as_mut().write_u8(pch, len) } + .map(|()| buf.len()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)), + CppStream::AutoFile(inner) => unsafe { inner.as_mut().write_u8(pch, len) } + .map(|()| buf.len()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)), + CppStream::BufferedFile(_) => Err(io::Error::new( + io::ErrorKind::Unsupported, + "Cannot write to CBufferedFile", + )), + CppStream::Hash(inner) => unsafe { inner.as_mut().write_u8(pch, len) } + .map(|()| buf.len()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)), + CppStream::Size(inner) => unsafe { inner.as_mut().write_u8(pch, len) } + .map(|()| buf.len()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)), + } + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/depend/zcash/src/rust/src/tests/key_agreement.rs b/depend/zcash/src/rust/src/tests/key_agreement.rs index ab31ea14c..7185b8a7e 100644 --- a/depend/zcash/src/rust/src/tests/key_agreement.rs +++ b/depend/zcash/src/rust/src/tests/key_agreement.rs @@ -1,9 +1,11 @@ -use group::{Group, GroupEncoding}; +use std::convert::TryInto; + +use group::Group; use rand_core::{OsRng, RngCore}; use zcash_primitives::sapling::{Diversifier, NullifierDerivingKey, ViewingKey}; use crate::{ - librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree, + librustzcash_sapling_generate_r, librustzcash_sapling_ka_derive_symmetric_key, librustzcash_sapling_ka_derivepublic, }; @@ -39,15 +41,8 @@ fn test_key_agreement() { // we randomly generated let mut shared_secret_sender = [0u8; 32]; - // Serialize pk_d for the call to librustzcash_sapling_ka_agree - let addr_pk_d = addr.pk_d().to_bytes(); - - assert!(librustzcash_sapling_ka_agree( - true, - &addr_pk_d, - &esk, - &mut shared_secret_sender - )); + // Serialize pk_d for the call to librustzcash_sapling_ka_derive_symmetric_key + let addr_pk_d = addr.to_bytes()[11..].try_into().unwrap(); // Create epk for the recipient, placed in the transaction. Computed // using the diversifier and esk. @@ -58,12 +53,19 @@ fn test_key_agreement() { &mut epk )); + assert!(librustzcash_sapling_ka_derive_symmetric_key( + &addr_pk_d, + &esk, + &epk, + &mut shared_secret_sender + )); + // Create sharedSecret with ephemeral key let mut shared_secret_recipient = [0u8; 32]; - assert!(librustzcash_sapling_ka_agree( - true, + assert!(librustzcash_sapling_ka_derive_symmetric_key( &epk, &ivk_serialized, + &epk, &mut shared_secret_recipient )); diff --git a/depend/zcash/src/rust/src/tests/key_components.rs b/depend/zcash/src/rust/src/tests/key_components.rs index df43b2611..d3dc23aeb 100644 --- a/depend/zcash/src/rust/src/tests/key_components.rs +++ b/depend/zcash/src/rust/src/tests/key_components.rs @@ -685,7 +685,7 @@ fn key_components() { assert!(librustzcash_check_diversifier(&tv.default_d)); let addr = fvk.to_payment_address(diversifier).unwrap(); - assert_eq!(&addr.pk_d().to_bytes(), &tv.default_pk_d); + assert_eq!(&addr.to_bytes()[11..], &tv.default_pk_d); { let mut default_pk_d = [0u8; 32]; librustzcash_ivk_to_pkd(&tv.ivk, &tv.default_d, &mut default_pk_d); @@ -693,9 +693,7 @@ fn key_components() { } let note_r = jubjub::Scalar::from_bytes(&tv.note_r).unwrap(); - let note = addr - .create_note(tv.note_v, Rseed::BeforeZip212(note_r)) - .unwrap(); + let note = addr.create_note(tv.note_v, Rseed::BeforeZip212(note_r)); assert_eq!(¬e.cmu().to_bytes(), &tv.note_cm); assert_eq!(note.nf(&fvk.nk, tv.note_pos), Nullifier(tv.note_nf)); diff --git a/depend/zcash/src/rust/src/tests/notes.rs b/depend/zcash/src/rust/src/tests/notes.rs index 271e77bdf..1441e5b69 100644 --- a/depend/zcash/src/rust/src/tests/notes.rs +++ b/depend/zcash/src/rust/src/tests/notes.rs @@ -649,7 +649,6 @@ fn notes() { // Compute commitment and compare with test vector let mut result = [0u8; 32]; assert!(librustzcash_sapling_compute_cmu( - true, &tv.default_d, &tv.default_pk_d, tv.note_v, diff --git a/depend/zcash/src/rust/src/transaction_ffi.rs b/depend/zcash/src/rust/src/transaction_ffi.rs index 517567a73..bd8c92efb 100644 --- a/depend/zcash/src/rust/src/transaction_ffi.rs +++ b/depend/zcash/src/rust/src/transaction_ffi.rs @@ -10,7 +10,7 @@ use zcash_primitives::{ consensus::BranchId, legacy::Script, transaction::{ - components::{orchard as orchard_serialization, sapling, transparent, Amount}, + components::{sapling, transparent, Amount}, sighash::{SignableInput, TransparentAuthorizingContext}, sighash_v5::v5_signature_hash, txid::TxIdDigester, @@ -102,44 +102,6 @@ impl transparent::MapAuth for MapTrans } } -// TODO: Move these trait impls into `zcash_primitives` so they can be on `()`. -struct IdentityMap; - -impl sapling::MapAuth for IdentityMap { - fn map_proof( - &self, - p: ::Proof, - ) -> ::Proof { - p - } - - fn map_auth_sig( - &self, - s: ::AuthSig, - ) -> ::AuthSig { - s - } - - fn map_authorization(&self, a: sapling::Authorized) -> sapling::Authorized { - a - } -} - -impl orchard_serialization::MapAuth - for IdentityMap -{ - fn map_spend_auth( - &self, - s: ::SpendAuth, - ) -> ::SpendAuth { - s - } - - fn map_authorization(&self, a: orchard::bundle::Authorized) -> orchard::bundle::Authorized { - a - } -} - pub(crate) struct PrecomputedAuth; impl Authorization for PrecomputedAuth { @@ -240,9 +202,7 @@ pub extern "C" fn zcash_transaction_precomputed_init( }, }; - let tx = tx - .into_data() - .map_authorization(f_transparent, IdentityMap, IdentityMap); + let tx = tx.into_data().map_authorization(f_transparent, (), ()); let txid_parts = tx.digest(TxIdDigester); Box::into_raw(Box::new(PrecomputedTxParts { tx, txid_parts })) diff --git a/depend/zcash/src/rust/src/wallet.rs b/depend/zcash/src/rust/src/wallet.rs index 25d2d6fb5..67ed40c1f 100644 --- a/depend/zcash/src/rust/src/wallet.rs +++ b/depend/zcash/src/rust/src/wallet.rs @@ -26,7 +26,7 @@ use orchard::{ use crate::{ builder_ffi::OrchardSpendInfo, incremental_merkle_tree::{read_tree, write_tree}, - incremental_merkle_tree_ffi::MERKLE_DEPTH, + merkle_frontier::MERKLE_DEPTH, streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb}, zcashd_orchard::OrderedAddress, }; diff --git a/depend/zcash/src/rust/src/wallet_scanner.rs b/depend/zcash/src/rust/src/wallet_scanner.rs index ddc77c3b5..8e4fc9f88 100644 --- a/depend/zcash/src/rust/src/wallet_scanner.rs +++ b/depend/zcash/src/rust/src/wallet_scanner.rs @@ -1,5 +1,6 @@ use core::fmt; use std::collections::HashMap; +use std::convert::TryInto; use std::io; use std::mem; use std::sync::{ @@ -8,69 +9,19 @@ use std::sync::{ }; use crossbeam_channel as channel; -use group::GroupEncoding; use memuse::DynamicUsage; use zcash_note_encryption::{batch, BatchDomain, Domain, ShieldedOutput, ENC_CIPHERTEXT_SIZE}; use zcash_primitives::{ block::BlockHash, - consensus, constants, - sapling::{ - note_encryption::{PreparedIncomingViewingKey, SaplingDomain}, - SaplingIvk, - }, + consensus, + sapling::note_encryption::SaplingDomain, transaction::{ components::{sapling::GrothProofBytes, OutputDescription}, Transaction, TxId, }, }; -#[cxx::bridge] -mod ffi { - #[namespace = "wallet"] - struct SaplingDecryptionResult { - txid: [u8; 32], - output: u32, - ivk: [u8; 32], - diversifier: [u8; 11], - pk_d: [u8; 32], - } - - #[namespace = "wallet"] - extern "Rust" { - type Network; - type BatchScanner; - type BatchResult; - - fn network( - network: &str, - overwinter: i32, - sapling: i32, - blossom: i32, - heartwood: i32, - canopy: i32, - nu5: i32, - ) -> Result>; - - fn init_batch_scanner( - network: &Network, - sapling_ivks: &[[u8; 32]], - ) -> Result>; - fn add_transaction( - self: &mut BatchScanner, - block_tag: [u8; 32], - tx_bytes: &[u8], - height: u32, - ) -> Result<()>; - fn flush(self: &mut BatchScanner); - fn collect_results( - self: &mut BatchScanner, - block_tag: [u8; 32], - txid: [u8; 32], - ) -> Box; - - fn get_sapling(self: &BatchResult) -> Vec; - } -} +use crate::{bridge::ffi, note_encryption::parse_and_prepare_sapling_ivk, params::Network}; /// The minimum number of outputs to trial decrypt in a batch. /// @@ -82,147 +33,6 @@ const METRIC_LABEL_KIND: &str = "kind"; const METRIC_SIZE_TXS: &str = "zcashd.wallet.batchscanner.size.transactions"; -/// Chain parameters for the networks supported by `zcashd`. -#[derive(Clone, Copy)] -pub enum Network { - Consensus(consensus::Network), - RegTest { - overwinter: Option, - sapling: Option, - blossom: Option, - heartwood: Option, - canopy: Option, - nu5: Option, - }, -} - -impl DynamicUsage for Network { - fn dynamic_usage(&self) -> usize { - match self { - Network::Consensus(params) => params.dynamic_usage(), - // We know that `Option` allocates no memory. - Network::RegTest { .. } => 0, - } - } - - fn dynamic_usage_bounds(&self) -> (usize, Option) { - match self { - Network::Consensus(params) => params.dynamic_usage_bounds(), - // We know that `Option` allocates no memory. - Network::RegTest { .. } => (0, Some(0)), - } - } -} - -/// Constructs a `Network` from the given network string. -/// -/// The heights are only for constructing a regtest network, and are ignored otherwise. -fn network( - network: &str, - overwinter: i32, - sapling: i32, - blossom: i32, - heartwood: i32, - canopy: i32, - nu5: i32, -) -> Result, &'static str> { - let i32_to_optional_height = |n: i32| { - if n.is_negative() { - None - } else { - Some(consensus::BlockHeight::from_u32(n.unsigned_abs())) - } - }; - - let params = match network { - "main" => Network::Consensus(consensus::Network::MainNetwork), - "test" => Network::Consensus(consensus::Network::TestNetwork), - "regtest" => Network::RegTest { - overwinter: i32_to_optional_height(overwinter), - sapling: i32_to_optional_height(sapling), - blossom: i32_to_optional_height(blossom), - heartwood: i32_to_optional_height(heartwood), - canopy: i32_to_optional_height(canopy), - nu5: i32_to_optional_height(nu5), - }, - _ => return Err("Unsupported network kind"), - }; - - Ok(Box::new(params)) -} - -impl consensus::Parameters for Network { - fn activation_height(&self, nu: consensus::NetworkUpgrade) -> Option { - match self { - Self::Consensus(params) => params.activation_height(nu), - Self::RegTest { - overwinter, - sapling, - blossom, - heartwood, - canopy, - nu5, - } => match nu { - consensus::NetworkUpgrade::Overwinter => *overwinter, - consensus::NetworkUpgrade::Sapling => *sapling, - consensus::NetworkUpgrade::Blossom => *blossom, - consensus::NetworkUpgrade::Heartwood => *heartwood, - consensus::NetworkUpgrade::Canopy => *canopy, - consensus::NetworkUpgrade::Nu5 => *nu5, - }, - } - } - - fn coin_type(&self) -> u32 { - match self { - Self::Consensus(params) => params.coin_type(), - Self::RegTest { .. } => constants::regtest::COIN_TYPE, - } - } - - fn address_network(&self) -> Option { - match self { - Self::Consensus(params) => params.address_network(), - Self::RegTest { .. } => Some(zcash_address::Network::Regtest), - } - } - - fn hrp_sapling_extended_spending_key(&self) -> &str { - match self { - Self::Consensus(params) => params.hrp_sapling_extended_spending_key(), - Self::RegTest { .. } => constants::regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY, - } - } - - fn hrp_sapling_extended_full_viewing_key(&self) -> &str { - match self { - Self::Consensus(params) => params.hrp_sapling_extended_full_viewing_key(), - Self::RegTest { .. } => constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, - } - } - - fn hrp_sapling_payment_address(&self) -> &str { - match self { - Self::Consensus(params) => params.hrp_sapling_payment_address(), - Self::RegTest { .. } => constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS, - } - } - - fn b58_pubkey_address_prefix(&self) -> [u8; 2] { - match self { - Self::Consensus(params) => params.b58_pubkey_address_prefix(), - Self::RegTest { .. } => constants::regtest::B58_PUBKEY_ADDRESS_PREFIX, - } - } - - fn b58_script_address_prefix(&self) -> [u8; 2] { - match self { - Self::Consensus(params) => params.b58_script_address_prefix(), - Self::RegTest { .. } => constants::regtest::B58_SCRIPT_ADDRESS_PREFIX, - } - } -} - trait OutputDomain: BatchDomain { // The kind of output, for metrics labelling. const KIND: &'static str; @@ -743,7 +553,7 @@ type SaplingRunner = BatchRunner<[u8; 32], SaplingDomain, OutputDescription, WithUsage>; /// A batch scanner for the `zcashd` wallet. -struct BatchScanner { +pub(crate) struct BatchScanner { params: Network, sapling_runner: Option, } @@ -758,7 +568,7 @@ impl DynamicUsage for BatchScanner { } } -fn init_batch_scanner( +pub(crate) fn init_batch_scanner( network: &Network, sapling_ivks: &[[u8; 32]], ) -> Result, &'static str> { @@ -768,10 +578,8 @@ fn init_batch_scanner( let ivks: Vec<(_, _)> = sapling_ivks .iter() .map(|raw_ivk| { - let ivk: Option<_> = jubjub::Fr::from_bytes(raw_ivk) - .map(|scalar_ivk| PreparedIncomingViewingKey::new(&SaplingIvk(scalar_ivk))) - .into(); - ivk.map(|prepared_ivk| (*raw_ivk, prepared_ivk)) + parse_and_prepare_sapling_ivk(raw_ivk) + .map(|prepared_ivk| (*raw_ivk, prepared_ivk)) .ok_or("Invalid Sapling ivk passed to wallet::init_batch_scanner()") }) .collect::>()?; @@ -794,7 +602,7 @@ impl BatchScanner { /// After adding the outputs, any accumulated batch of sufficient size is run on the /// global threadpool. Subsequent calls to `Self::add_transaction` will accumulate /// those output kinds into new batches. - fn add_transaction( + pub(crate) fn add_transaction( &mut self, block_tag: [u8; 32], tx_bytes: &[u8], @@ -815,7 +623,7 @@ impl BatchScanner { block_tag, txid, || SaplingDomain::for_height(params, height), - &bundle.shielded_outputs, + bundle.shielded_outputs(), ); } @@ -828,7 +636,7 @@ impl BatchScanner { /// Runs the currently accumulated batches on the global threadpool. /// /// Subsequent calls to `Self::add_transaction` will be accumulated into new batches. - fn flush(&mut self) { + pub(crate) fn flush(&mut self) { if let Some(runner) = &mut self.sapling_runner { runner.flush(); } @@ -841,7 +649,11 @@ impl BatchScanner { /// mempool change). /// /// TODO: Return the `HashMap`s directly once `cxx` supports it. - fn collect_results(&mut self, block_tag: [u8; 32], txid: [u8; 32]) -> Box { + pub(crate) fn collect_results( + &mut self, + block_tag: [u8; 32], + txid: [u8; 32], + ) -> Box { let block_tag = BlockHash(block_tag); let txid = TxId::from_bytes(txid); @@ -858,12 +670,12 @@ impl BatchScanner { } } -struct BatchResult { +pub(crate) struct BatchResult { sapling: HashMap<(TxId, usize), DecryptedNote<[u8; 32], SaplingDomain>>, } impl BatchResult { - fn get_sapling(&self) -> Vec { + pub(crate) fn get_sapling(&self) -> Vec { self.sapling .iter() .map( @@ -872,7 +684,9 @@ impl BatchResult { output: *output as u32, ivk: decrypted_note.ivk_tag, diversifier: decrypted_note.recipient.diversifier().0, - pk_d: decrypted_note.recipient.pk_d().to_bytes(), + pk_d: decrypted_note.recipient.to_bytes()[11..] + .try_into() + .unwrap(), }, ) .collect() diff --git a/depend/zcash/src/script/zcash_script.cpp b/depend/zcash/src/script/zcash_script.cpp index e34d24549..f91b81b9b 100644 --- a/depend/zcash/src/script/zcash_script.cpp +++ b/depend/zcash/src/script/zcash_script.cpp @@ -13,50 +13,6 @@ #include "version.h" namespace { - -/** A class that deserializes a single CTransaction one time. */ -class TxInputStream -{ -public: - TxInputStream(int nTypeIn, int nVersionIn, const unsigned char *txTo, size_t txToLen) : - m_type(nTypeIn), - m_version(nVersionIn), - m_data(txTo), - m_remaining(txToLen) - {} - - void read(char* pch, size_t nSize) - { - if (nSize > m_remaining) - throw std::ios_base::failure(std::string(__func__) + ": end of data"); - - if (pch == NULL) - throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer"); - - if (m_data == NULL) - throw std::ios_base::failure(std::string(__func__) + ": bad source buffer"); - - memcpy(pch, m_data, nSize); - m_remaining -= nSize; - m_data += nSize; - } - - template - TxInputStream& operator>>(T& obj) - { - ::Unserialize(*this, obj); - return *this; - } - - int GetVersion() const { return m_version; } - int GetType() const { return m_type; } -private: - const int m_type; - const int m_version; - const unsigned char* m_data; - size_t m_remaining; -}; - inline int set_error(zcash_script_error* ret, zcash_script_error serror) { if (ret) @@ -104,7 +60,8 @@ void* zcash_script_new_precomputed_tx( zcash_script_error* err) { try { - TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); + const char* txToEnd = (const char *)(txTo + txToLen); + RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; stream >> tx; if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) { @@ -137,7 +94,8 @@ void* zcash_script_new_precomputed_tx_v5( { CTransaction tx; try { - TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); + const char* txToEnd = (const char *)(txTo + txToLen); + RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION); stream >> tx; if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) { set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH); @@ -202,7 +160,8 @@ int zcash_script_verify( zcash_script_error* err) { try { - TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); + const char* txToEnd = (const char *)(txTo + txToLen); + RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; stream >> tx; if (nIn >= tx.vin.size()) @@ -242,7 +201,8 @@ int zcash_script_verify_v5( { CTransaction tx; try { - TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); + const char* txToEnd = (const char *)(txTo + txToLen); + RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION); stream >> tx; if (nIn >= tx.vin.size()) return set_error(err, zcash_script_ERR_TX_INDEX); @@ -305,7 +265,8 @@ unsigned int zcash_script_legacy_sigop_count( zcash_script_error* err) { try { - TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); + const char* txToEnd = (const char *)(txTo + txToLen); + RustDataStream stream((const char *)txTo, txToEnd, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; stream >> tx; if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) { diff --git a/depend/zcash/src/serialize.h b/depend/zcash/src/serialize.h index e8010d7f4..11db3714a 100644 --- a/depend/zcash/src/serialize.h +++ b/depend/zcash/src/serialize.h @@ -26,7 +26,7 @@ #include -#include +#include #include "prevector.h" @@ -588,22 +588,22 @@ template void Serialize(Stream& os, const std::uniq template void Unserialize(Stream& os, std::unique_ptr& p); /** - * Ed25519SigningKey + * ed25519::SigningKey */ -template void Serialize(Stream& os, const Ed25519SigningKey& item); -template void Unserialize(Stream& is, Ed25519SigningKey& item); +template void Serialize(Stream& os, const ed25519::SigningKey& item); +template void Unserialize(Stream& is, ed25519::SigningKey& item); /** - * Ed25519VerificationKey + * ed25519::VerificationKey */ -template void Serialize(Stream& os, const Ed25519VerificationKey& item); -template void Unserialize(Stream& is, Ed25519VerificationKey& item); +template void Serialize(Stream& os, const ed25519::VerificationKey& item); +template void Unserialize(Stream& is, ed25519::VerificationKey& item); /** - * Ed25519Signature + * ed25519::Signature */ -template void Serialize(Stream& os, const Ed25519Signature& item); -template void Unserialize(Stream& is, Ed25519Signature& item); +template void Serialize(Stream& os, const ed25519::Signature& item); +template void Unserialize(Stream& is, ed25519::Signature& item); @@ -976,52 +976,52 @@ void Unserialize(Stream& is, std::shared_ptr& p) /** - * Ed25519SigningKey + * ed25519::SigningKey */ template -void Serialize(Stream& os, const Ed25519SigningKey& sk) +void Serialize(Stream& os, const ed25519::SigningKey& sk) { - os.write((char*)sk.bytes, ED25519_SIGNING_KEY_LEN); + Serialize(os, sk.bytes); } template -void Unserialize(Stream& is, Ed25519SigningKey& sk) +void Unserialize(Stream& is, ed25519::SigningKey& sk) { - is.read((char*)sk.bytes, ED25519_SIGNING_KEY_LEN); + Unserialize(is, sk.bytes); } /** - * Ed25519VerificationKey + * ed25519::VerificationKey */ template -void Serialize(Stream& os, const Ed25519VerificationKey& vk) +void Serialize(Stream& os, const ed25519::VerificationKey& vk) { - os.write((char*)vk.bytes, ED25519_VERIFICATION_KEY_LEN); + Serialize(os, vk.bytes); } template -void Unserialize(Stream& is, Ed25519VerificationKey& vk) +void Unserialize(Stream& is, ed25519::VerificationKey& vk) { - is.read((char*)vk.bytes, ED25519_VERIFICATION_KEY_LEN); + Unserialize(is, vk.bytes); } /** - * Ed25519Signature + * ed25519::Signature */ template -void Serialize(Stream& os, const Ed25519Signature& sig) +void Serialize(Stream& os, const ed25519::Signature& sig) { - os.write((char*)sig.bytes, ED25519_SIGNATURE_LEN); + Serialize(os, sig.bytes); } template -void Unserialize(Stream& is, Ed25519Signature& sig) +void Unserialize(Stream& is, ed25519::Signature& sig) { - is.read((char*)sig.bytes, ED25519_SIGNATURE_LEN); + Unserialize(is, sig.bytes); } @@ -1079,6 +1079,11 @@ class CSizeComputer public: CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {} + void write_u8(const unsigned char* pch, size_t nSize) + { + write(reinterpret_cast(pch), nSize); + } + void write(const char *psz, size_t _nSize) { this->nSize += _nSize; diff --git a/depend/zcash/src/streams.h b/depend/zcash/src/streams.h index 99956126b..d8f3f0d9a 100644 --- a/depend/zcash/src/streams.h +++ b/depend/zcash/src/streams.h @@ -317,6 +317,11 @@ class CBaseDataStream void SetVersion(int n) { nVersion = n; } int GetVersion() const { return nVersion; } + void read_u8(unsigned char* pch, size_t nSize) + { + read(reinterpret_cast(pch), nSize); + } + void read(char* pch, size_t nSize) { if (nSize == 0) return; @@ -360,6 +365,11 @@ class CBaseDataStream nReadPos = nReadPosNext; } + void write_u8(const unsigned char* pch, size_t nSize) + { + write(reinterpret_cast(pch), nSize); + } + void write(const char* pch, size_t nSize) { // Write to the end of the buffer @@ -424,6 +434,13 @@ class CDataStream : public CBaseDataStream }; +/** + * Concrete instantiation of a data stream, enabling them to be passed into Rust code. + * + * TODO: Rename this to RustStream once the non-`cxx` usages have been migrated to `cxx`. + */ +typedef CBaseDataStream RustDataStream; + @@ -492,6 +509,11 @@ class CAutoFile int GetType() const { return nType; } int GetVersion() const { return nVersion; } + void read_u8(unsigned char* pch, size_t nSize) + { + read(reinterpret_cast(pch), nSize); + } + void read(char* pch, size_t nSize) { if (!file) @@ -513,6 +535,11 @@ class CAutoFile } } + void write_u8(const unsigned char* pch, size_t nSize) + { + write(reinterpret_cast(pch), nSize); + } + void write(const char* pch, size_t nSize) { if (!file) @@ -614,6 +641,11 @@ class CBufferedFile return src == NULL || (nReadPos == nSrcPos && feof(src)); } + void read_u8(unsigned char* pch, size_t nSize) + { + read(reinterpret_cast(pch), nSize); + } + // read a number of bytes void read(char *pch, size_t nSize) { if (nSize == 0) return; diff --git a/depend/zcash/src/streams_rust.cpp b/depend/zcash/src/streams_rust.cpp new file mode 100644 index 000000000..9b0eef45d --- /dev/null +++ b/depend/zcash/src/streams_rust.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2023 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "streams_rust.h" + +rust::Box ToRustStream(RustDataStream& stream) { + return stream::from_data(stream); +} + +rust::Box ToRustStream(CAutoFile& file) { + return stream::from_auto_file(file); +} + +rust::Box ToRustStream(CBufferedFile& file) { + return stream::from_buffered_file(file); +} + +rust::Box ToRustStream(CHashWriter& writer) { + return stream::from_hash_writer(writer); +} + +rust::Box ToRustStream(CSizeComputer& sc) { + return stream::from_size_computer(sc); +} diff --git a/depend/zcash/src/streams_rust.h b/depend/zcash/src/streams_rust.h new file mode 100644 index 000000000..a255b976a --- /dev/null +++ b/depend/zcash/src/streams_rust.h @@ -0,0 +1,21 @@ +// Copyright (c) 2023 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_STREAMS_RUST_H +#define ZCASH_STREAMS_RUST_H + +#include "hash.h" +#include "serialize.h" +#include "streams.h" + +#include +#include + +rust::Box ToRustStream(RustDataStream& stream); +rust::Box ToRustStream(CAutoFile& file); +rust::Box ToRustStream(CBufferedFile& file); +rust::Box ToRustStream(CHashWriter& writer); +rust::Box ToRustStream(CSizeComputer& sc); + +#endif // ZCASH_STREAMS_RUST_H diff --git a/depend/zcash/src/test/addrman_tests.cpp b/depend/zcash/src/test/addrman_tests.cpp index ae96d4f2e..0808fe406 100644 --- a/depend/zcash/src/test/addrman_tests.cpp +++ b/depend/zcash/src/test/addrman_tests.cpp @@ -31,6 +31,7 @@ class CAddrManTest : public CAddrMan int RandomInt(int nMax) { + assert(nMax >= 0); state = (CHashWriter(SER_GETHASH, 0) << state).GetHash().GetCheapHash(); return (unsigned int)(state % nMax); } diff --git a/depend/zcash/src/test/bloom_tests.cpp b/depend/zcash/src/test/bloom_tests.cpp index ae375a1e7..4e4f93dfa 100644 --- a/depend/zcash/src/test/bloom_tests.cpp +++ b/depend/zcash/src/test/bloom_tests.cpp @@ -538,4 +538,30 @@ BOOST_AUTO_TEST_CASE(rolling_bloom) } } +BOOST_AUTO_TEST_CASE(rolling_bloom_reset) +{ + struct TestRollingBloomFilter : CRollingBloomFilter + { + TestRollingBloomFilter() : CRollingBloomFilter(100, 0.01) {} + bool is_data_empty() const { return CRollingBloomFilter::is_data_empty(); } + }; + + TestRollingBloomFilter rb; + BOOST_CHECK(rb.is_data_empty()); + + std::vector d = RandomData(); + rb.insert(d); + BOOST_CHECK(!rb.is_data_empty()); + BOOST_CHECK(rb.contains(d)); + + // reset() should ensure minimal memory usage. + rb.reset(); + BOOST_CHECK(rb.is_data_empty()); + BOOST_CHECK(!rb.contains(d)); + + rb.insert(d); + BOOST_CHECK(!rb.is_data_empty()); + BOOST_CHECK(rb.contains(d)); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/depend/zcash/src/test/coins_tests.cpp b/depend/zcash/src/test/coins_tests.cpp index 42e98308d..10d6bf17d 100644 --- a/depend/zcash/src/test/coins_tests.cpp +++ b/depend/zcash/src/test/coins_tests.cpp @@ -45,6 +45,7 @@ class CCoinsViewTest : public CCoinsView hashBestSaplingAnchor_ = SaplingMerkleTree::empty_root(); hashBestOrchardAnchor_ = OrchardMerkleFrontier::empty_root(); } + ~CCoinsViewTest() {} bool GetSproutAnchorAt(const uint256& rt, SproutMerkleTree &tree) const { if (rt == SproutMerkleTree::empty_root()) { @@ -192,6 +193,10 @@ class CCoinsViewTest : public CCoinsView } } + HistoryIndex GetHistoryLength(uint32_t epochId) const { return 0; } + HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const { return HistoryNode(); } + uint256 GetHistoryRoot(uint32_t epochId) const { return uint256(); } + bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, const uint256& hashSproutAnchor, @@ -243,6 +248,7 @@ class CCoinsViewCacheTest : public CCoinsViewCache { public: CCoinsViewCacheTest(CCoinsView* base) : CCoinsViewCache(base) {} + ~CCoinsViewCacheTest() {} void SelfTest() const { @@ -279,7 +285,7 @@ class TxWithNullifiers JSDescription jsd; jsd.nullifiers[0] = sproutNullifier; mutableTx.vJoinSplit.emplace_back(jsd); - + saplingNullifier = InsecureRand256(); SpendDescription sd; sd.nullifier = saplingNullifier; diff --git a/depend/zcash/src/test/mempool_tests.cpp b/depend/zcash/src/test/mempool_tests.cpp index e38a34ddb..66ab933ad 100644 --- a/depend/zcash/src/test/mempool_tests.cpp +++ b/depend/zcash/src/test/mempool_tests.cpp @@ -105,13 +105,13 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) removed.clear(); } -template +template void CheckSort(CTxMemPool &pool, std::vector &sortedOrder) { BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size()); - typename CTxMemPool::indexed_transaction_set::nth_index::type::iterator it = pool.mapTx.get().begin(); + typename CTxMemPool::indexed_transaction_set::index::type::iterator it = pool.mapTx.get().begin(); int count=0; - for (; it != pool.mapTx.get().end(); ++it, ++count) { + for (; it != pool.mapTx.get().end(); ++it, ++count) { BOOST_CHECK_EQUAL(it->GetTx().GetHash().ToString(), sortedOrder[count]); } } @@ -127,28 +127,28 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).Priority(10.0).FromTx(tx1)); + pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1)); /* highest fee */ CMutableTransaction tx2 = CMutableTransaction(); tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx2.vout[0].nValue = 2 * COIN; - pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).Priority(9.0).FromTx(tx2)); + pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).FromTx(tx2)); /* lowest fee */ CMutableTransaction tx3 = CMutableTransaction(); tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx3.vout[0].nValue = 5 * COIN; - pool.addUnchecked(tx3.GetHash(), entry.Fee(0LL).Priority(100.0).FromTx(tx3)); + pool.addUnchecked(tx3.GetHash(), entry.Fee(0LL).FromTx(tx3)); /* 2nd highest fee */ CMutableTransaction tx4 = CMutableTransaction(); tx4.vout.resize(1); tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx4.vout[0].nValue = 6 * COIN; - pool.addUnchecked(tx4.GetHash(), entry.Fee(15000LL).Priority(1.0).FromTx(tx4)); + pool.addUnchecked(tx4.GetHash(), entry.Fee(15000LL).FromTx(tx4)); /* equal fee rate to tx1, but newer */ CMutableTransaction tx5 = CMutableTransaction(); @@ -156,7 +156,6 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx5.vout[0].nValue = 11 * COIN; entry.nTime = 1; - entry.dPriority = 10.0; pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5)); BOOST_CHECK_EQUAL(pool.size(), 5); @@ -167,7 +166,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder[2] = tx1.GetHash().ToString(); // 10000 sortedOrder[3] = tx4.GetHash().ToString(); // 15000 sortedOrder[4] = tx2.GetHash().ToString(); // 20000 - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); /* low fee but with high fee child */ /* tx6 -> tx7 -> tx8, tx9 -> tx10 */ @@ -179,7 +178,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) BOOST_CHECK_EQUAL(pool.size(), 6); // Check that at this point, tx6 is sorted low sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString()); - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); CTxMemPool::setEntries setAncestors; setAncestors.insert(pool.mapTx.find(tx6.GetHash())); @@ -205,7 +204,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.erase(sortedOrder.begin()); sortedOrder.push_back(tx6.GetHash().ToString()); sortedOrder.push_back(tx7.GetHash().ToString()); - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); /* low fee child of tx7 */ CMutableTransaction tx8 = CMutableTransaction(); @@ -220,7 +219,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // Now tx8 should be sorted low, but tx6/tx both high sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString()); - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); /* low fee child of tx7 */ CMutableTransaction tx9 = CMutableTransaction(); @@ -235,7 +234,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // tx9 should be sorted low BOOST_CHECK_EQUAL(pool.size(), 9); sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString()); - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); std::vector snapshotOrder = sortedOrder; @@ -277,7 +276,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString()); sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString()); sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6 - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); // there should be 10 transactions in the mempool BOOST_CHECK_EQUAL(pool.size(), 10); @@ -285,7 +284,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // Now try removing tx10 and verify the sort order returns to normal std::list removed; pool.remove(pool.mapTx.find(tx10.GetHash())->GetTx(), removed, true); - CheckSort<1>(pool, snapshotOrder); + CheckSort(pool, snapshotOrder); pool.remove(pool.mapTx.find(tx9.GetHash())->GetTx(), removed, true); pool.remove(pool.mapTx.find(tx8.GetHash())->GetTx(), removed, true); @@ -317,7 +316,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.push_back(tx3.GetHash().ToString()); sortedOrder.push_back(tx6.GetHash().ToString()); } - CheckSort<2>(pool, sortedOrder); + CheckSort(pool, sortedOrder); } BOOST_AUTO_TEST_CASE(RemoveWithoutBranchId) { diff --git a/depend/zcash/src/test/miner_tests.cpp b/depend/zcash/src/test/miner_tests.cpp index 9b028d42b..1141e7c80 100644 --- a/depend/zcash/src/test/miner_tests.cpp +++ b/depend/zcash/src/test/miner_tests.cpp @@ -12,14 +12,19 @@ #include "uint256.h" #include "util/system.h" #include "crypto/equihash.h" -//#include "pow/tromp/equi_miner.h" - +#include "pow/tromp/equi.h" #include "test/test_bitcoin.h" +#include "zip317.h" + +#include +#include #include BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup) +// Precomputed nonces and Equihash solutions. These were CPU-mined by the code in MineBlockForTest. +// CreateNewBlock_validity will recreate blocks from these via ProcessNewBlock, which would fail if they were incorrect. static struct { const char *nonce_hex; @@ -135,6 +140,96 @@ struct { {"0000000000000000000000000000000000000000000000000000000000000e67", "006104bfc0d7d12fed0162241b28c22d415cb1cfce0298bccfdba64f6dd1ea60337cc2e4d07a2a2608811c6349d7c3fa24f5de5a3741e4673a334a157bc7fa446497645065ba7bf319e6084ab828ca589bf6e27505865af28b79e631d46d08bc72f7dee29844d9c4012ee92dbb696f5d137d70b668bff7a3b6a0021cbcf10a915468d66e6b379f96321d2d7e5d4a3e9457c03e0ed89d84b09ee9e9902e31d885a573c961d61ca35004f87f677c1c0bc5f488d05331602f80d9b57d42e906ab11d9be0fdc48b8f1968a87ebea39d083394e481675f2a85e35a76fdd1de94005578d7f693a1fd931472a4cda98ea6f19c532d61a6ecba7a5b7377b48ac08c9c485b7d187e37809d46f80d0f9d17b21fc57782e7716962acf2b6d67f246bfcfdf0aae04efd86fb019cd9e44c9133548ef9251a62eb596009bfeff55e01fa46402d88b00789d0173d96c73fed1aaf737ddbc00b9d2a4b0c56406c5c7371a98ef1fe39ba07d0f511c3457dfa65c99893954b45c89c76d493dbc106dfe1ee42ce1dd9f15c79636c50bfe76a99f1b909993451f39549b5e885c95e7ed75e570377be338d25bf783010541dc774b2b7baac350cc925bc2885f926dc6293e9dba3d86d3a8a1ff5a76c75d60892aedc1bd07e907fb5c3eec0af4e501bac201fa4364355e1daf0bef135d45bc07518f3b92dbe4abed79bb7de16eff7ffd05cd3cbbb283b05220f970d1cda05ea280dc3b684f0def76a6f31c62f39e6e340d2f7a74ca45621f32f41c791c8d5a13bce4a12dc77d7f40d502613454282b2a7b7c717ce58ad96444e327f1be4fd68e2915f30f069fa966b8629621a0ea11abd773a0589476ddefbc15d35bb95e6929f3af87317065100c4ce68327c09910467371ee469069fbfd773223ebd0c2a8a2b7c2c11893dc9cd8d091f0e476b4bd443021595daa9cd40b0283a1da707171afc89bf90a414d19b7a116fecac676aad68670af80f5b539eacbb3e14656edab7aed7910e572e131dba74ded3f34256be26e79ab133620c543cd346925515c433c4586b0e65d98df20607c8fc1104d709b7e9c8125e925a574aeae31b271245e4f6c7d311d9cdc318609960e595d15fd9eb75e357d03be1440d9d42f4994094dd387b02d458e56cd8c3eadf638360a1e2d53321d268eab48c07814ab4acf7dbfee02d774daf574feb9c0bce41c63cfe532eb4c7ece86657034e580bb9b47ebf6d9c528e5e0268ef3553bfc25283d6c9916cddf7129f341006f412f50759cea1f3d4bc67d8d5ba8a2e69f863f5add092a5cecbcc6d60bd61b8980affb1f99cbc145b0c19c98a79d8c7fc45cc9aef7dd2ce9bfba74d8dd6dccf18f26409e883317291cc6e7eae04fa97341f1cd640dedef071e10671e32746226230cd132f7a5f612634529c75b7b938d0566f3f07305d276f2e859100851c42ae052fefb4725d679933bb2e36baa6aa3cc7d1f6626c88f19cf7d13b49d05f686b8f37b82b44cb5bd315e0cb4fbb4bb18ecb40e0a0a8993ceaf329fd72e6ff9339cd77f870728a418d00340d554d7015a1022dbe49bd567b4473305afd1d0ac781199a7e58f2041bd9f1184fb60480d34924e1a793625d7ca514fea1f015a391e5dda8017155b7c80a94b9ba6863523682d2873bbc09e6cb0056ec4d7370db33ed7366511fead2d3b2a825990e922c1cd3180eba683eead129e264a09d92845edb40a2bb5db26a624b12f84e4d60d3ad954c1daefb622ab2d61ab41644e36bffc14a497a8d99bc14fc77072250b0d81157f95aeb0d52c133bf7a87fca50c138f61e37eecc99dcd121bc98b1d843624f90778e7a9ed64c18ca1a88450e5c76a82751948a291caeb301585e78263ddfed46da579fa16574a37776c8ad31d6dc2a66"}, {"0000000000000000000000000000000000000000000000000000000000001a1e", "02c15514adacf021e7dc629d773d4eb9b9d4fb7e2f0bd2407da31e4923d48236140e69808da546b6e5ed0cc7515e51102a70df67f67cdc3721130fe67db65e300bcae2168cad7ecd25447c0949243d3eca0aec9b079344297cc4dcef96a1f1dc14e7a4059990f4e583092ad5c6d947c1c09a582435065637e1296eec816d12102b2758094c0665a0b469d176335e4639b368d420611dbf82d13dd5e1b1624b7868c5060459d8cdf80619f75625f4561df9a3e7765dc388a2503bbcf3e01d962c8042f9de6bd39a6538ece99fe6dc399bfa390e8c8d9cce6452ebe7ba1945b0756ee39e4f1e636f5605cea4f4f2ba91f0a2f748827ad725d5177bfb5127b53c7827d385a12d4432a0a8a244bd62d8ee88d028009364b3d94fcd17e6f3f112b15c757a6d9e627937afffbeca1284d3f891a703cd57907289853bd917494d324df71a6e3b2518473233b998e232fab930850783928ff2946959551d8174fbc01ef357c05acdd40a564fe99b4b87fd79a0f247b0bffd1da7a635bb6720f9947f9b26866da04a96fa00b956d6255056d01627cbd41f88d7636b757e72e530c1f4855720d7b6370f2e89749bc85394b74ef46d446aae767aa298e0304c989eea99608f3f8802666778bbbf0f403edf9b8c1ddff3b0abdb2f914b4ff3157ebb9244c83910d2ff26e3f7bd2b4a41278da2e35914f3339eb60eb673d3094e114395921ec7f999b37e1132719b6bec3c4c20364b2635f8d68160f2b9c462482c7d569303df78c80b9c0bd14049a15985ea3aa5a6578a3b5c6b9f73b214f48ad797171f492bad5211b164a5561fdbdf0d8211dc5722a48b4de16de122ffc1f0fa5657489889862c63a3b82b4c7af7f30097eeaffd955659eefa7d2e1447b37fbf659367815b92dd28aa31cd569810dd7f429f2a486ddae7d5c131a667b2c5fca7bd757fa6a802dc0a288dc3a7d92391f496186a32132bc879c0c4042f87432b1cdbe569da105d325097d821c193e3b106bea48a55164bd7d2de0c08bf7d3f2772961cf3b31c5f07a61de7c1d3738cb3eab256992e568cbaa24103d892044f61f23dd296c23936c873c51e383d1b82144dc3e97e8b9f3b916f916590b29c93c9bcde6db01a557312778f562d5dd16877d1f6355a30b29c0ec52cd2ae11171e93db29f57896b6567d6ab74d7ceea804dfd8a0910a5ee2df875427853c2da123307bb0dd59181dfedd9783e4e85a86365b58260a9fb67d644b08dad713428f6306a0ed4973c9f9b9b6658d1fd4693b5da341dd5194d5942ea677b93be4d9c73c548e38092341a411ce321f986e553ff6745dc2464816b8422c935c0f40633a5771d7b3edddc1648e2b453308200d79af05b952d17aa994a1d1d8fbbdf68ded95051324de11b9ff90bd2e9af81625a8d1cd5285bd1e6f0e039a5d2e5b43aa49197ea123b3481c60fa99d43185098683ee4620e871fd1b9118c6998eb4bb9d1dea490b8222218f5792fce4f516a849c15a6ed5c5bc564d122f34c37c22439b4d450223a340729b42c7fe162a08fc2af542dbe8f3492330c8b2f107de7d2ddf172a0deb5df3a94a081968b5b5a321f3a3a5ac94d46dcb12e6f7b889a7dcf96f1e48562be67dca745f5f1d8f2c36fca37ee8cea56414933c655e91a595e3940cee08215963559728f4dbbd32900d1fc23a62fa75dfff13db343b85c73a6aba40d7d3ecc9a44e434d179bfa2636a7cb3b8adfdca3f76a96d3f3baaaf1aaba183c3deec644305f6f7d1fdcc798e36fb2d675aa376a4f12c6aa342214dc1bb979698e71e9a6b67a709e34822f6454c866dcfa7b60f91833346643c2dd12bf0b0f1575133e6905a5e0b28773c9697a47cea5c7d7c1e62bf283b4f6243523e887b566e56a1372f297bb3e33"}, {"00000000000000000000000000000000000000000000000000000000000022de", "007f388bed6b91756ea3e0866716ef6e9485fae6160195c7cda5c1e43f96ee359e105bcf4e8c293690420939124f04a0196363910421187811575929db40500b0bfdd1e8964aa334b801e3339a336d585a30852f1dc294a2d3d36f9ecc747458f3d41b4572415496df2a9fb1f882156cdabf9f65e681f38019865d6d47482277e24c9b8973eb34a41254faae4c5e2caa9dde5925ec118f3d8fa767ae00f434645957154367afe72000c59c79182c8faddd24424b9ebbb09ccd651b00540c96b9c7eec648a28a1d72c2e575d0f2250078511a011598db8e0788edf0ddc15ae24b62f63d6f93f71a2743a3c43ece55471a9802a76f31561a6f365c3647029bfa736395883afc0632bc25d4a8661b25d5aa0310f3c3fd3a183e75d359d6de3e5910b5dfbb74b7660af906917dc42b12e3e484aae1dbd20eaee037ef301572b7fc24d85b4aff9c82b27dcd421cee1639230d0188fec59f0dd4c0ced69c1ad07abd23692b1bd30735af942df597dcf6f403a36371bc416cf3e29a58570f586b05c357dc49515689788ad9581b8887dd913a41dc35e1ac9c9f9f4ea534eb6b36cc8af0299b6d3905750425da0366bdc59a7824477d7946b6f35c4ec90b8e61790fa74a4fa92396ea856661027828d40abb11dbe36bba516fe8ec8913106677285a4790d8034d1d1bf9fd87990889ddffc369b954a3d1c172be7e1812226c2b100cbe82c42bf4423456b6cb2bac3b4828135cb54f7a933a01f7f4a2057ad92136ba8e19fec313b412d43c089a71f06fd1625329b78d49ac92c59e4080932ddb1645910fd874dfb1f358e214231f62041acc41fd2c4e7b7127b3042459e1457f6b307fce9825aa4d2b942277f52665f2a77dd107b4f16cb3280f20c7551ff6cd855f97a6144131f69bab5648fb4b81261eefbf629094e8bcc4e36077f46d51a647da51fc01dca9a9ac12e2f7e2e2b1c9229dae099e95370177143d3b38ab661f19758494a01b32f0c27155b45a872a867dc50f9d76473695e9e2c4f9357f5ba6bb6c455d985f4e2c21486fde6576c6a8ceda6e010a7dc2b504130f429ac33376781ee4af5bbe8d768005bc4cb5092b15c4f296a8bd8c54a298eecd790a5161755a8605cc46bf890b8ff93d508501842b78c7261e5deeb1096891c528a300e57bf2f0aa9e8af2623cdf16bba20427704120484b6af8be26e4983d2685c783ce85d0174f84598719c6beefcc3603a94d4aa62750725df50671d7f9903ec255f779643ebd2fd8122fae3319e61928dcdaa44880d6a483140de63d2d7d7dc9dd449e0ee00d908e0f2164fc054198641e8fb0d74279c9b4117884b9335028a9f50c7223d3c03675ecf73329e52603f77f20cffba99356e51a365b75825f7db56d77542784f3c2663c493a2e564d73f753e9d6ebb0c2f2027a2330a7117c67a20507474fc47282a02cb572de17bbc7a335959316f74a05e3687cfb5227bc5b1b7f084f50902760e77740d420df9a495521c09b911e5f199a8343918b8386fc74f22552a76524a22c8c70ff06084e7fefe9b3ab98e004fadf35eb5f60483f287851712d90ebdd6b512877170d3b7fb34f16813917ae3b5ed54ede6081bdd7cc646fb336658121fd8fbafc52959b48d13375dfa4ce8616c157533a05ee1dc1120f215c348b54357d68adb4da7f5f48d55c005b3e7a23d05746e44d968f7601d4dbdff702861030d6e3a4140e6e1a29978be541f713f8e2cc9aa1ac32fecc941ee4aa4c41bc7f91ea5328ff87cbf35a8de17d1d3d1ad6d4384b0df52d10b3984d62e1678e86dd150ee425490bc727ee7107fda0f5d2433ab1c5d407be9d123fad5c201355601d926d3923787be86a4aa5be0b8d5750171ad658f8e97798b5dcaed46345a9af70c441"}, + {"00000000000000000000000000000000000000000000000000000000000018c5", "003f7160ba1eb3d1992200815707240d1f0ff76d040a3f7993f664545b9a3735cf4fd9262e3a577f56e713dc6b114e209751db2523f13c6de6b1545c5065d3350a0b8b4050abd2cc77760db670fb7a923b999ab803730ceefb4ed7955953b3efddb07c71cfd0de19bd356b9f3f2b5259f9d79d36a3b2dc50ce1f88d3d5623e34ef76ee546cfd827484a16d5330852bc2b90cef44a3bf1b8c6c309bc259292fa157582e7be6bd687a00a23c9a549ce20b19f1c14d9bc2f31703afdde89318372c9a1d882933350e343078a978cf25433d89c70582e1fc6b2bce256123a3582cda5bf21be7bf0aa806bc47dbd6343697d94432bc6f75981e7e871a4e9c0b726ebcad1675f8e1f285c5e365cb79d30e917a5d26bacbfcaf288c69f68d4475dc4482e9c31dda254410c8de06a00ca3993e9011973c3f900e0b917764a413e30b22a61b4a2941cef7b6d8f53ce25928beec6d022733c532e379c924ff02f63ff66110e92529202d267a53e515cd545aefe6f59cf059bdc346c71ecc9b0e6d6130acb0aea1c70441560eb6731f6a68deaa3441c9a6ec4d6495e19014057d8fba02ce52131cd01a0237aaba41a075d58b18e19e49358ec20efa51d43709606dfa45e0a019f94c6118c8d8fc205ca8638f0f051eac8562e7d2efaf5433103856b91d383fb7723e16f385aa7ccc09932d7643a48038d3cdeec4de7e90035b2f4c4c9574e9e35a506ea39d4b38479915bf6b0365ea0683b018ddb16f01f251e5a475f7593dc6002e65d74013ad6117a33133d4fd3c83850fa2e8cf1836db06c8b5d189c182f0853ba455669308f559ec140572f93a97e24d6f57cbb1b43dfa8bee3700f2f89c0ec079c5411aad0d44a711c2dada6a6d035faa9883118741b3055fdce93d12078e05620fb6487ef2d6f41abe5e7d70f66c79ebfce5471762348a716e3852f20094e75ecdd2a37ae291003b571d9508d0f1fbf57b1bb7871e98d9fe1b889b852503deeee95c3afa06a419fd834431c984506bf454dc31fbb7b19c8ad3be5f2f870df9a7d8cb030b43f38dae543015106a5faef5062cc7db6a1bc065091260ac5b1b85d48935ae29d8072e2beceb129510f97bc46e0d58b605e78e52dad908d91d678e9f2527776971fbb6eedc297aeb8f150573f4efc5a36d942fa1a6c7983e76e7a20e6e3913b200fded49f88fb5a888f9754705df0606699173acec199f93cca550053294a68286e03b3c9dab6c8e3f2620ca17e0980f754183ac621e0a9c71dccd4d38ab0c264ec3d79b22608b2f55c7feea420adae96d786d650a404e9b834874f8ed57d4f8df5aedc6cf041bf853779147b323ababc3f0daf840305741ee306d3fdd031704c13b8e0e6e28d46ec270e5ffe2f7146edfb5ad762e56e520a5fcadacf92cf955739f43be15dfcb170243f0ef7d861b29a9c521e5ef70d1b71946fcca2538036ea92ddb221b95acd4dd9bd55dbdab184e51350ab40e6d6a2203932fb0dbe818f54a4eff13bf08431aee5fb10d5cde7130d7b7cfcb6e4c2eedebde28b607213347bad53676f1317496f2a5f61178fbef07600b693ae7285075ad949b932d6fd57571b3880fd82c25611323022f3c79a29c5ab08e638053603bfc341345a5b656951ceb3b7bccb70fedf47e5e1e1ddd4c5d0286875afcc4cdd31bd1c4e7424036f96e7a9d62ad107743441a6369af57fdd61197d3163f471f5b1c7205d8c6f908d43db9d8d512fed1bd041a2b16dfb98716eb442589074df11641d330585366aaf5b51bed51033c590eceda8e3bfed790572132977482c246708e2d73b28ba01d3707fbf27949bc5e9baed5a41ae80406eed93198cae9c548b9316768ba7a91652551080321167c92a996d6e35030938410e42f6a79bdd44512"}, + {"00000000000000000000000000000000000000000000000000000000000015fc", "0093b709f841c816ac7701bbebe791d1fff91a026711cfe407e5221b99e13c33ac627e5e70f6092eb6bc0dc5aaa45709b12ae824f36398d3739a1a0a3f4155128e9b9ecc880ef33cfbf153306897a937675eedac03c889b5944812f35479209710b1e2696492ec173e1488dd92095cc3a56504a6aba45c4c9e94a47c0b431eb7e6062212daa4ccfba35c8bd7d8b3c91adf985431347d9e3d78322bc9c6f7877b45c0dde8f7da60060646ea3e3ae8cc958026117c616b89c51ae9ad38d90cf6f0a7a223573fb5c525ee8bcdd90eb00e37858a16fbdb33785af8e4dc4902a4db76e33637de572b1a1ba7bd3086d7fc07590d335b74df832eeee877a8db0a6f95b2ee836f7ce270044acec24805ad19fdf55218969d5fe3aa9b1df15d125fe3d167ab387a1f1eb510626b4e5115cfffb463516f26cda0e93ab6cf26ed12dc57e1852ca62985fc64ba95749b7f32501e2f8801cc1fe1882ae7ddf2b1e5173af0643e48b21a2a9d0ddc21ff539f37ad7974129564eea5338d90de37d20938096185d63f93ac9fc7dd7a574ea75b773ed92f3051c702a2970edb952a140c1d22344f0e273cbc5b05b1ad4e7ad9195d7eb533b0ffb5a28ebff158ec7f148efdb38f6503558b10d225a77a6882d4d79c61ea10410625d258a3e75310915fe9b16ff8f3287cf3b24174c6e6c15821ead165259e0c74513e466898b0c90d2db777ed87f6f4ddb8877c1ae3c40a0ed916a57e3cd23edadea3c9ef61069557bfb640524482fed5260e82e482fd5fa849c329ac091163ba9f04af9aea292a2f8bc078921939ff6b844b4f462281ff8dd609dc1ce2a1b6e95e1e33934c1c2765640b5380663dd360557196884c276ec5f26ab75321d00942d9b75d3c973ab072fda0195f04d30607936c46fe72970b5ec34e40eb3718673217e5e7b5d51ee233f9897df13d51890099b21834c6de99e40873ffd8f9759549c7ca6e0b2df796f0a18ea132c41a34bc51baaa2f2c8e5ed6300c8030967f5571d70be3815a402798f18e5c4e81d22467bb9ca10dc9ad1916e35e65e3bd0d15f3302c5a0187a9aa41e5b25f6eb01221f8e75d00a829b37c0010f661e7bdb6140dd4cf81f2fbd9ba19bbf8b835f00bd946f836fe39a5ff7a52e7785dbe4625e43efb4015bbfefc9a22dfe32a01c4c52a670bf2e8a61e119e09939867e6c91673d15834d68ae379d9f60697d8dc1724f3fcb3f54861c11b66582b62766f7f587f42e32ab3f3406ecafea0dfef62dfd4ea06118a7ffcd6184bfcfae3db14e5c3ee53e8221b5db7360e4593a1310a19a65e0cb31a9ba97cc1c738c0c85d418b7180e04d7bc4c80b5c0da5a4d889979373d44f98275d481c209d5194a70c92cb384f1265ecf82afe8c11753ba736abb454b99b548ef48ef832f7e4ba9b700e9dc72400eaa9da89209f956f9b02060ec57f75349209b3ce2c706c92660e699b3514e361d0d89af8e2606f9e6d0b9932698dbd10c1f861a23d23ae0b849b20b647261d8f090c0d516bdea9bd058d9fe95799cacd5f74a909020374506abf597651e661d9ce0501f03efcfd632b71c31155dd3111a7a62cc2127e4f4f4fddbc065e0cb28cb42f4601d9f0538b5d3fe6298f4fbddeda8813c4c9e1325a2097a0e2d5849eca22720e1bde58e503167a83b4c3178e6ba0a3367b2232fe767f75485429bdf2012650cb976a0843fa4e3d7cbd94a1b5e6f21ed8f62566381ec3c655b2608068da1d36b589ea6d2e2a3bb06f8f8b26a0cfd70dcddc72beedb25c17a1050695383d986e44df30b98e00e5cd06bfaf9bc31326073149a8634e6773b26416b334c66d31eb2c5a8d197465944e4ee4b153c9c36f0940e4f5e3055d25042b4e54448bce6332ffb3f3d305b00505956878a668"}, + {"00000000000000000000000000000000000000000000000000000000000004c6", "000a9d2b301bec736c7062af3cd59b8d15b113308a0735340c134602c2f93142357a31962e4f4176f948156a54ad5846426d853846c12dd2f3b235a45c20a822d263ffddac598ddb66b3cf2be0ecf51adfbca0b103cedf836a99c124e0800272a99b8835d248f7ae95130bef32af851e4beabd942896fdd2ea759e3e8e2b227c5c23d49c8edfd78d12fe7f53873773883cc70169594e0e81db687d707518b6886aa1b3449e1a73650367914e4e4af25354bac6c0d8f4ac3df0325af0182db7b40d229dffe3878853081b76e739e5c75d078144d0b3ba4ea0e34f9a7fc60029dfdceaacc27cad824f08cf2af1aabf0d9fbed6c9f2d752f5fe74ddc454134d9b4e7f86c04691098584f0338ae731d53e29e61b822e586fa07fa59582f21feb57935d95f6196edf16e2bdcce7d638f16a39d32425e0632760c9be24c31b8efe737a2babddc031c2ed8d301d6ebeb11af24f00f013de2d685dc3a54484dfdf52e9b982ba93ac9207579ff7de9347a98a72d5877c3fb31301ffbd3b680415dea438297e8fad48e30173395b8316699e685b16a69615934c18fef133d6b136e6a5edea3055956313a38d39872a6427bfb7eee4c4fa2a4bc6949ee5e0294b5d3a819d610dbd976340dbb7d591bdda7e2b6c2ef74da4604db34d2546a3aaca37117d60192fb5c633c5ba9286d0885c97fc3622e8d0c3899e1bd9a84404368faaa8287793f80f183de96eab9783f71fa10713429d49c29ee0a941c451745b9bf882c378d899be1a29836fc00b230a9167f2222529239ea6b0f60338288e0f4e1cab305f7715433e412c7d0a19a3f9566108df9bd18a1d2ac770fa50a2380b17f7985f5fe69d17fad98267663f59a658563fdce14f85fc5e7cdcfe34440c06cc5fa8d3fa43a83741e2c392618656c1493d0c8b7620634a57c130b81a4afce34e11811627990022bfab05579ceb4837f3315aab63f0cf9a5b053b11be411796704e3dd60871b163b0fc21957fb8757b0db46110c71f40f599cec55fa57e91c564377d389b0f3c9349282aa9d1e1afd79ca148217e3ad9d754ac0574986f62243e31abbcf5e00a7cd897c84fff8cfe0ab5a21d4ac2d0e8f244640bb15c2d09a3277edd3e2c483675174b6ae0960ab310c21a11f6735cbc744633a73aab97a05c5f975a85ef96368bfe1b3a3d98ad011e5449922cb193cade80566a65e0bd8d9b38eac20ec9dd05b0694f21b5a524a5c546e775f8d6599bf702c365ed54551752e3f1f3a1d1a768b74696bab82e2d7c728e524fd0aca1edb5116abb3d2d5eaade53510614ca404c2d4819d487866625c3da073e683d4b0914b88a5bd2e31e17ebd1f17951c469a462a11420801eab22130dda63cf72a993c8a8cc028ae1053f60d74bc4ef921866d2416032566d67723cadef6ff04ff60186d0ed79fd8445faaf5546ba35a175ad1a38db872de5f2a43ccb87c6e7d8f33a8fbfce7b04bc9d95b9058e2592c90f3cc080f951c3aeb980ca2180b4761430ccf9c0bbd4f5398843947cb5acb4a1f80fdca0bb0b55c55d80152c5d4ac6119d102d9ed0a1afd91f192324554564d1b8370fa1b4a9a959441a33cd357bf00fa34de54ba16ca3227db2f498a88f996641dd85ae2c09398c71abe827b682f7f9f07e0eba4efc1edb5902e04f3ca8acf577bf2930d3130cf794937273e7523453eae2245be4ff5b46074ff4eeeba6a76f3ef94f0aa30a742f0757d4ac16560f12d33795c66d3aae4d335063624e34f0b3c723944288ff4baa0de3f34a1a1209b20fb32e6a77d89f450632ab6da2603dd3460b1ae4a4addb5f3781e49794cb7a3fc8f2a6d019b16b14de795b141ed4dd36a8244cf4653b222c26db08ca186ac4db9049ab1f193403285bda76f9bc911d32d6"}, + {"00000000000000000000000000000000000000000000000000000000000003bd", "0085ec8ee88d6b5bc940a0401613c94e1699de789c0b1d6f9624a140c5d38ef8a4ef69646e7ba2bca77b089c7be5ed10885391d400d28cb0d4e17f1b7a030e14b803bb53138b5f536e327e0be0325d2b404cae6000c577102e486b825f280079e24e5ccc6cc58c1fd0117d5693dc0bc486af3475e9bb6e83e20ed13ff127130d031587152f67f9a8122deead07c114c1b3d6962c8e1cc93a52da5d6ae9f2d68b6bee660808b0f4170529aaee4a596b47712a82a8215c1de5571bd5ecca0c53e0d2b7cf7a83770b21475949af5667ed5ad66c0cc87f2a5692a84133a5f0d688cb7939ebf2d62c7e25a26ce74195b2bab2ff5397cf5049c5800bd8130b083b89d85b06ac711fdcf2c793f18381753a5130800c180a82a952ba4dd9d164c950f11e8a2d30f17f881d80aa5cc517d80177d055853ceed39593ab2ce81b24d82b4007d9ce355193742ffe7ee5ad3d43910620053e457583f456ffdc216583166cb7e1c9b311ba3a121db09bf0067766628c413360e2d1a97c2cd0be34200d191f869e277f3fe2c29c11ddf3acec25eceb933fc96440f0d5a28d3f9d84283443720eef3bde34ea0af413cfefa1c88d5875b2ae401894b6579132deae519d5db955e65d5197fa5af58fdaf60718e35f02610be95fd71df1427ff3bd9377e9e7ebb53e9e53b8603656fb0e630dfe13939636a4cfc5ccede16b3ed36008e990798aaf3ecfe0e0921c8c683a452651722fe01ec26526794c94f164c7923a314ca3c9b596985afb10bd4149d2690089916a736b7e29804518b45dc4da39af9765a8cec2ea972d85766fb0ab4dfde4b7081b12cc348c2810cdf3cb8c6313dcd11564ce4e87413a18dea344ed9b42d7cd1491cfc1123856282a379f281af0e9b0be77555deb81037120505ead2a01376f8c1cc6f79183a3102dc06a529dede17107814d9f1cae00ad762126817d827cc5661f69dff68599097af03c266cb6342de04a7b119f64476eba362ec9021ecc04090d017f1dc3f0084b7ff0b83f9653419dc8f895b61a854cb4bbd49b8f41a0d32f33bd0e8da481d254210d8c2db85a08c2c967c871728b5d32bcb577bcde1a3291e9bb026770c94f5e537610bcf517c2c99ee8d124d0ace4a84fcbefa8ead5cc904df67322ddbd953f514bc294bc5e9049d085562b9263e9569261fcb2c5037e1bf2684362075019220c2df5a002fd0a38ddc353ab4525a21bacf7d551d61048da9f19af9ef4ac0721b9fdc2f71938b7a28aa50ba7b060aa9ce61e734429d18b890fdf0639690fb54e34d5207593ed2def1a0d36acf59c86a8d4c35b945069423b312fd85783292a449e2b50e744739bfb26067158794de6d47b2d2f28fd0b67b9de012d0b4966c54b36f366d315fceb2b29dd47def86610f3b7345581cef4c32a349a5f39e5053fc49c4f1261fb9f44d2e32b378149f9cb303c6e143b4494135a2bfb41a462e52c330ddfd8ec7f6f502dee33f9130c2a53e617b5f1b45d2ab6e060fb886f4ff31b975e2637d35de2b5c53a4e1cc1e4cd312f6c1474445b1d536a8f0ebd25de68e31e69d9be7c554e1d93d98dcb90cf356ae8e7a0ba564d12199f964d891c8a3ce8f251e4a5dd8efa3e9ffe6b8ee8ac382243306e6d69bde30bb73d4de336eea76782bf73fc7ec90824eaeffac3333c58c5a51707f551d1620457773427bdcecb6d9cdcaf797092bfbd5428def1b11b36390c164aac4cc8acd721f1f13af80d71e12f912fb7cf3dfaab21e49ebc216adb1433fa47b9053c8298428c0976ee7e3f05f16688ff38099d4fe183b2587ddd6e1646fb66fca3066f1e6461b1dbec2491a90a76d19f195b195b69a063a54120a28161276e990a57d2996f4d20fb82e014d7a7c58de679e9d65439c9aaff0e9c"}, + {"00000000000000000000000000000000000000000000000000000000000021bb", "00633c18f9af2877f67402ff0b9a6e0d3a2e6b7a980b1ec7ce7ec90030f02062a36ead38a613fc11b1152620cd72340af723d835b3bfac5dc1d0fab5f4520b2d1a8c25ad4efa39da47468874c298fab3aedd1f5c0e8d658a054ba727bfe1383c40c5810698995e12e714fe4aad8d5881efefe4a2ad093a38de3878d1d906129ca67e85242585bc79c224b9d95a5ce1ffbc3b445f323b25bb6254ff5c66f8313b7bd37a6d641f306700e6c8a7bd4bd3bcaab420f39cf9bd12d6321e75000a03940c8649fb3278eae4e3f55a14570807bc0cc108a90d03845549a4e29f540120ce49a218257cb4f00e32494f509a2f4572b92211f06bc2a886e7e6cdf50170a71bc1af52d1c07987de17e8dfe6cfe69bdceb1be744e4d4da9b44e7b5278364e4a901f6bd9206230ecb934b74936371c27581f889b092897156552fe6764e0ec4f42e6a69c90308958e67f18e727ebe22e30147bec8c2a38521fa7603e32362bba99e823956a733a8cf39fe0cffd36bab148c3f6867fd6d8bd3762717db47f8c7613fa1508c22e08a2a75feb587f639c73ce5b31294d691d4de90a7cd8a460f730ae33c14ea0c3c17074a6cb95df2d487e17ef0e3da8277dd38b51146f53e41f097c7a66021685a8cfb2517e59b4dad1bbb494f2c50c6aae26175f15856851239d97996c73ccd9b2d4a3d5ac9f218164e17e61ff5c2e77e9714064f09a3ea1a5054f794612efdc2c47f6cf13d33a90871b9edacc7fbaced2005255cf14802b4687cf5bc12aa3a4d941b7efdf5aa645c3f3b7cd525161fe3b829ad14be031508a3fe2613a406b53ce21bc314f4b812f3bb56d063a9bd5a772a277c763bca98ca1a3d9391dcce77bd27f23bd8b8892a37d2d05323555c2f391d4001d39a1dac7d5d37524d5397069cb70034c12334006a81dd9b158cea4f135ec85ba6521aee3f4645010e26df522fc813d6e1190bfd513906580f9a6dd409d48578d51e9b3f6a9fd15608c3ac24adbea6cb2d0f943c47f805e14e95e8624fbc33c8d1edd53e17dc3b9263170a161b9ee7b704f488c25cd66e48591a4104086be29a854bd0573e014a9a516d3514d79765341763879584d5a507a6e6a1c918fc47a8eaa8678e3310a4134e431bd5a576c1f20cdec1a4c6c366562712388d9c4aa81af84d598b266566ee39c26fe5b571d703254e9e069b33d1c6a9e24183cb97c5d663948e890ed15767d31f9e875d0f81a47c4d7025999d70319303529433c0d46d00e911f0e4ea28dc0d3d29fe1d37296395c76ae0da3fe820265c2f37be0ec8fbd96096077abb4e30d29540d36959ef3cd974dae6431f31d94d5277873b5483533868c6ae2864e4125e181534ef23aa49f85fb16a038c4a72dc396783e8e3cbdd44d03c60dedbbfecf5538dd094f00d7ff4458c2858df610338e58587ec220fc88c3380eeaa9cfd751e54d792158f9dce0a45aa7e9ffd429eb168122282155db6a5161133e9949a9b113989139039bdd4f14c69d36b375876cc9b89a42b09f92b1789af3e702a1471d672ba0a31f6fd53d2183578e6e22efec6333acdae977ae3134d4cce3f555bb344f8836cc6e93d36dce9f9ea66118d196aaf8c3aee7a9185515079df99c4a6794d995269f2b9eaa75c55b214d7360de3777dfbaab0106e0451af4a5a49c6c69b94a1010057cb620a62d40a0e0bcd4f933d8bb115de6ba3362a57625e83d3168d4804ed665c866f80fd9df771c03c3a6517bd64be970c214eb9753a99c4bcfe53224b6a354bf6db9f9df8d3197f0db9e9de3307593114a050b6a8f63cbe5495682d925d8b78e3cd9dcf6574e386fa9a3d49a1dff33c19be83e2630fabe12ab25438f834aa02c4503f50c087362754613a1e13f355b8d4735006d23e9755a2f3"}, + {"000000000000000000000000000000000000000000000000000000000000144d", "0054d2026c93243f58f3d6d39ce1fa562bee7b8a2f10dfb9932d07aa7dbf4f55535ed2d306441e92ccf013c6b9036948ecaad99d235fcb51278d998bbd96a446260f4dd658a4abe379b5f478b267cacc5118abc3022b8a938f8754b94181f02d97094b856ab7d90b5a0ac1746e4d29af37a5e5020fa59cae931a7cbfc996151cba0d9c4d2a7eccd1319d616e7c674cc25e7f51344d024a7aacbbfff81d03c7582a9d60fdf69abd650e09a99d692c6e999603689533e485322e2334555a23c4373f188a8f7da86268912cf360d6fcd3590a8912a551bd844503deff9461c88d5fd795d949d1976d7087aee22323f7519b0d77096c62e465d433db3d5d1a6afbeeda2d2dad83e3222c43751509ae93b540131b283998379a674b6b8d03cdecd4952109857c04e91ce5594d4791af44be379b69c860d14b5eee1ed62942d6af400bdcbd79d618b7ed25f78206c383191448013854c144189b12f6b1345fa4dbc0af3b557fdff21172d91c3e8efaf67c5471ca232af0f2d2bc3c530403873905d9491caaacb39281891ef99939fab8cff1191e6f1ffef71415bce10324d7c740d2d3ab7ee62302a6676ed0dba93bb112414dd696c778cac34d10ee12ff813cd4ca4e5c54f1e33397472b81fd448ff37903958a85fc0402e794d8e2ace57e72ddf6b7f395d609d28573649c5417b3b5c8008fcc2eae52b17b28e5026809e008b36c31a70a402cc7c696a54a1231b6640795d5cdeb5610ab2df708d42fcaa5e327c99c27eb35f3dbcda0e8091158ab7782c754145a4a689f7c72452305a7d5606d1520f2f5c04d43396a5f60b8daba0735e5d3a78a9b5b23cc220d2475a9e58cb3d65a860a00206c059a3526e620922928596792c10e5c38f9102cafe583f97a91da585544247bc75d9c03dd16c622e2a1a5421186ffb86ca521ccdaf0a962d7bd277a00d6c3c9ffd865d98541a26f93fce444a8e43a1f13022621b3a1d4f5ffddcbd60107ca9785973dfe600d105f1bd7b8cea037c71ca14cd769d0519a01fb1b9b12bb3600efd36877d09f674380cceb1bcd545f839d0233db2ad9160103dc1680c6ff5205157b46b845903a839d29e798d0c93aad574cfb6342d2e4435f26e60ffdbf655cf0de83f37831c4788e8332ad6f9a437d1a1b91e02ed12bf3241ed4691d5678626698bfc7ed0110a3e6b9c587119f1ef3b52a43ef6b146e9c078a1b7e0650e50b243cce70f1ddade313654e97557f0509316e66556d73ed7e0fec61fb68f92718cb9b6b513bb77294a6543b094921b4ce512da861dcfd5803860449cba7ac05e462fcb0508a82b0f4665b259bc3010bf3a7327406dbc8efc1a174c8e8a9349dd873736d0e5fcc13f7d24763796ab479f0d8aad55d9a58981c4df76dcec694ec0d4cc528deb5d1e952b3287c145d04f24e2ecb428253ba0f516fde1b65008811c6e65306be0870e197e797467104cd9c4723a55cb990145505bc76563109f4bea392548c92da48634e4edd8dca1ae382cb9e5fcc9f2044d4e35135ab3ac782778c4e116fe6159334772de0cdc26ddab030b18da73619b61f09bf7f815c759fb08074c413b93b6f471f1b16f012588fdb790e6ec9ea0f518688eca984c026791cfa1c5f7b4d165887e4d45df1df96960f6d0a989410fb08b486dcd71e332d2ba5c253263733f8dc4a70a04522694f07994c32dd256f3335f64b1c60faa875033b1758ed30d82ca249c9d5f4948b445b666d981bab8d22230dc9394fa065564d523e07eda575efeb1e6b8816c4cbc0576d1f33e0d01256532e3d24dc70f7549540c33ca6346c1baf8fe986c89fb9afaa0f9cdbbf6d21118990e548e04dc92ec28329ddaef4b2daf101025f3d46b3a520064bfd6416e6acf8a1f2e72197561f"}, + {"00000000000000000000000000000000000000000000000000000000000009ec", "000a91acb6076445ce808057a00f402ea90f9e94c9061b52172b693de1a26cd5013a6c75f757bedeac460ecf73f0ed463ca28a0c61fca45c0155add5584d52a8824dd7cb34b9e3fb2d3adbb4f83303e2003f5da214e0cdc5688d402497f712e6f8d64f812a24780e6e2b917448431e7793790de4857f7572c5f292392d9c1b10f32c7fa15c1b1876925ca8511f19266d6c0b4a2c4f7537e68e38c5ef9914d0517fd7329bcd582c1a0246ea029fa1403d3719c0c35aae605ce9bc0dee812369a630ae8db4df453f132906bfbae5b065de9d2f16595c758f6dd11dc63fa385a4a94a962a95ba60d2217a64c4082ca80f8591d94408fff26eda6cf7cd6b04287cb227c7df1a4cf16156144c210ce4b51391da1045a2fff097317d68e011281294ec96891bd490180ef25546915590a2f8946427a2cd1219b0cefe8af711e90a73441eada13cf322b2e9ba46b5e87970e23001761d7b07a302e58a55722cc3d8beee39423dee0e076f83f0371b821fc71ab3926f50c087726d7c1c6c0832dbe55b93ebe3e6785a9e8d66494acb969a105b11f87ffa0f15cb85eced01506a0daf6994687465a5176741eb3155f264e08012133a50179d26b9af8e5b3092467c6f93a8cdd654a8ed6ffc9a52c6a1b746d31c637205b80c2b459fee153db55515e588e6af942f47e8244b1d5b6e5d5138050696eabc55617a2e50d205e831a6fcc6daa661e5d05eb05fdf8057438afa541ec3efc8c355f4f78d6e92d061d7154337adbcf5690972cf41315ac797b57812ca5cddba1514ff2adf150f577511a395ce14e35cf12b3896a30c5a5ac56f0b091d6d655f97b4c55b3b1477a9fd8f49ebc75d4c481085b416dfaa336582059522bac0f83568f43729d40f4692906b989de7294b4111ec1e246c63a88e43294a621ee5a313506fc75814c374746606106a7a71da00254c54e1d8f18d5dd771c1c04e0e2e8885b7884510c9aa925ba9cea37063e360383cebd6b108d5f422100fb3654e3d233bef83a4ca19df119651ffdb749514b1990ad5e08085c975269502f7f797bbdffe2fcb07dfc6762e9f553bc93364aa55514301a4ad9079ec0bde77e5aac6a812c7e6a633c1f982e64eaaf93010150ea1c3fd469f9b76e6543b2dd6cdb1403add383a2a9577584d0beda73c6dd3e8455de2c506b67fddd801ec9ec7ba4d3a850bfce1c6ddd4bf598d0037cde7105d3a1e0dd3ce4b6d8f15c6abdc8c926761b3aa800eddec289a4835aacac9d135fa3c1a78fadb73d228111e5b727bdb8033236644851f56c45691ce5fa9f4027ee8cc31dde37975f4121f2639c4a133a7bfaf4121728c012359eb1baf0404985c71a9b935cbf334720868978f4ec45bf52b15014ac0c096b89e2f85b0bb1166bf3fb8cf4deb6d256610f3e1b205f4b9db9b65004f18b5044eb14593f65423ca4ce402b1cd1b9812122d03aa699e4a197c7c86ad2875c225e9bdde73c2076c6c2325d3a1533aafd2ac2427b17cf10af57931390aff5975d9e5addcd3443c2ab3aa6635ce7c2540015e07446048d9dae4d311e535477856ae20d9bda601adc4b719c10cc95b788461f67473b9aa99adba9d05965435622b1a7bf37fb1a7b59f2b7cd07a32ce6f1756a24440e85cff4ca233bd28a33ca1245e2c6cb203e08391cf7a7b21ef65e36a97f887c66dea588c7d4d31e509fc224e9fde5e96c293c65b72898b59c63208f14b769997104bfdad7325e43b431ae79c1f1392421c97094395e958dbddf6c56b63b44636f91a82c90bf3f7a23324aaad2b61c10cc908c10488a43d7315383a5f10f859ca7361b5e573745f4f322201572bbb1a9397b22e0c2ed6c753734e975ba1d1fe427b6f203ac8370edb1dd351d531e61a2ab745267099d746e9"}, + {"00000000000000000000000000000000000000000000000000000000000002f3", "00954e2d9e8574317998e3c977b98aa975c81a66ef2a76cc2e14d866b2ee1595a669693195ae8ab4599c0e4ebf7901e59bc148b03671d55deb8e02bb5ca3f50f76df14d05bedf1c3937345bc3ef68e2d1f1b58950abd08e87ab9d6c9da31438be136983f6a7e1dd2900f64309f8b0699e4c6761553e173bdc1b759da525f17e4462992743aa1cb68d1944836b45183ab381cd945acae9c41648225f61697b08cc4079ea4b6fedc5205a3829d25c6033266a3c24bcc4247b7b6681f64d8063d183ec8c2da0f4422c26ec3d805b8b07fa6792647109e65aa6033b55139c512fb4b103a62553e5ea783beacb033ade27d7e7eeaeb45e26e637ace5d0243159b5ecbab88b6c95a9a551ffd50f4d77b703e0aac7cf47e7738e5e679a11409dc18d7526efa875f1cc318cb85de704b909a6722939fa327cec185e1f887b946238aa7a3ed0433920869fde2fcd83eb81a7f162d01cc284cb24365ffbbe8e18aa919508c90d1086cc82130024cf08c8025d1e482e382e03e821c0bbc16781376e3e14b8ab499eb3d95b948edc07a150e12babe482d2af26c1ba54b07adb7c3aeee48e5faa6fb8aa002bc522ee05d66cd1da1311ddd8eb10ee3861fe00c5b0392de7b9fb717f50c77d4c5cc8fba72157ed49f0559f97c5451159a938a168504c32a3e23ab57d40b0887b2286159fe372bc6da65f66882eeeab87c337904a5dddec64d9d377d15805be3d0016f9e1cdd04c50647e04cead64f77d9829345dec1d659a882aef59423528677529379d09ee7b8477acc646e15b2dcac8d55bf2ece0cf20d53e008b97b06fd573e8d8b9a967b06a1a03a7cda2ed1e130a0c3137c65a0d1e74913832f210b4647cd5c330e3b691b2360ae4714355b394e377d8af2aecf09e87d78d70e38e91a8ac741bf48765ab9a5dff525225dbcaf288c2b71902a264fda1a3901293dcbd4356aa7d367f18139c0420a6a325762eb041f2d84e608aacad596508244b6ee2ca2990884e104a0357b5d9af9e0f2e378b51b6fcaf702d8be8a24487195b43c17f798f95729a950f27006aadb9f05ee014880ac174d3e9ecbf2f07293a49d8927b92cc4140905cc07bfe96afdf2a664e28237f0f5546d9879260eb4439247cec2635eb94729bcf4da12a443d76b8417a25c25d6961b872e98c68bff72cb7f84c35f06b218011ba1eec82ea6b37713e9cd596fff139a3a0b5719117c6cb564ca9bbc8bd2570299a579c2293d73b62c867f7cb0d98384ec8142e27723613a14225b77d244a1b5431cd2a122ef0ab461c876e9f353d0befa9f1d683dbe8baa149b6e5445a203bf322de7de170e19275f25beb44d1e3ff79732ab34d0cb75fd9f99f0583ebb53746514e013ddc646eaf23f9286d9961a40386fb7e64a57230f2dfa6008a299d3cea7958afd5e3502e0f6fd114125785c3591e639edb605e4d376f17144103bf6bf64944546fba70075e2a85aa96cbbcd090994d16dd42aa31d876c93e1f168a88d81bf544dcf137324773bd5046ecfaf528074341870d9c80d9510069cc9fe830c7425fe0332fce01c7fadb7144e7ec41883863604c84af09ad5b65296d00f5ea1aa1560561a90ca7e1bb1e8c9dfa6430287cd6a6573247ba8c93b7b4ef105971749bc9254efd2f19ca18fe396bb9a05dec86c862774b55aa5e128af6c699997bfcf47390a5dad8cc863588f552774d4dd6317ca869ad9e0ec080510cae55f297afbe7b763c340703e5b7bf985933a4fcfafc61289fec98e3ac0e27ad8bab33af5d7bd0c7ce7950e06af509b8bb12a5e61c6a15d47b315af13af7e93e1df95c1e027f44d18bf0569302ad33e5f0e2e5b606de201576935d1f2f6419bbd7736d03679597f85b617e005adf54225a171bccacaa5cd352f06"}, + {"0000000000000000000000000000000000000000000000000000000000000c2b", "00a900d06ec75add44ea008b312d21bb030b19cf681372934a6059649389d881fb5f1cfafeeaa45a566d08328d798e1019f4e4b7851c2f4ac88737ea7f8c0253e7c44d04b368bfd57f955ad2403b6d67513978da03a674db3f41fda5e86a33bbcad8d7aaaae25a17cf338db2067514779cff6464475d2ab3226460fda27903b365a6d4c475c327c103da7e6db4565b47ff573316da7cdef6e2d3d354f9f52847654fcdb7386e6fe8088c1d7f87c91c037b5f567729499c4667e817c2120cf9721a5b556422d4fb65fdd2f3bee5daa51eaa8c0da3dd18671ed8d195e5a33f2cefade59a668f21b414d54291b9a9ea1558418426e1f4db2553dbaf9ca308cb38bf5625e45742df53c8a9d75b2dc835b0f659426cca1ae613014f61dd66868f79128627fe5dba880daa79ba814cd3bb1ab3c4aa7ebb28b1c0141038d70e7d0c0d891c0749bc5c311123c3ce0d24079f077501e176906a335565fe02322ff778908796b2dccc9e2ae57e11545890255bbbcbff7f7cd56709567af03210bf09605b4557c8d67ee4a4ad58a90234a2d4bfdf3ddbc76eae752f03b73fd997edde0306fc9b7ea7d906a5d2580f0554eec31c8340abdc94323a515d66782ae0dafec796d7b7c6097b91bdff08031c97793400159bfb8e4bd81629e01d91bab6b83175f1ffbed1611eaae4fc34240147a5bbd8bc91e34656f01598be9c02dfc167b0595b27b297667dc6e7f3069eb2b6dee90a1003c64d58d9379a1ad8595277a1323658d824630e8f36c2f0482d5a87e8c61557efe70fa3d75f059f30e452bdff36ddb1c777f67d97d8581a4ab7786e29068b98e1430affb4800c98e09afda8a363b91ca5b30b381b5ecfa444b16fe16826b94dc47a200edb8fc6259a59cb208afd329004598af9e161c678a03c05d465553b9092ef0ea1ae0b46c3cfd5183a4a4998a13300ed6bab192752bd84cc3093e38c9ce22faab417511d9b4e81c947a81bff80db3db771f2ab22ee3f8722074ab9036769ebb59d77d302942c0d8700759c801813a00f467f9e032d2a040596197669b31f39fe003107ee1285c0c684efe0f892d72a3ac0dd7a3d4eb9f72fcf2c4013a0446f857648ae5e5b54668a8cba794d2b6f06580ddfb7ab75ef935830caf825244172c5e233c07bce3a23fe298e52b33e187faee624725c6e45037d254c3f44fc3ac9af23fddfff6491b5fef41a730b8d8cad7d1d9e2df1d35a35a1fca07e99589b9a5f15eb97d26f235665a804e3f321e0f4095acd51fb2536383ff5bc1243978c43ec7fa9fc8e6fb4b7bf305e04cb7ca696cf82350797f16b853b5f807a9104944a1a6ebf75f8ee5edf879ed504f8a8b64a9c6d38454611b4f8951066587dec1e868ca1c80d626ec7978a413b06cf6b07e7c47fde42a5e270c342ba04733f8c9302a8fe99feaa062d63a517e2a1d75046525ddcef790e9bf4b261c8d9b47a049204dff3310593634efd3a2bf364edc5d9dbdd113892d778e6c1c354887e7069546cd6bef4298fabfb21f80f37e325da9205f52d6203382f15ccdcb18ffacea5715443e07da1225f0b7132677502470ed9bcb1f5e9be6974c03eff2d7f28180634f361d7aa7617ce906091a575912c2bce4604500c9bec5bce16057f8c49133417db295e444cfff74b02d4e6e3d953995be12422b98ee296e1407e7da16614b6c177a945eeade5faf53335cc38cae55a1b464209d192f24de606e1f81657badb4c23964c02bbbca66451a74803a21a7b90d419c9ab71692f02bcbe8f7a0ad16de85488af669233866d974f9d5b4b2fbd06810ea17e546c0b0338a871462d05fd8f819831f213f717ff01e7d351cd4ed35486272a4a256e35af7747bd4092eb49fad18e20935bc77037cab6d30c9c7ad31e"}, + {"0000000000000000000000000000000000000000000000000000000000000c47", "005fba4c169f301b881668ad46f4ed9238173321140060a8cc2d55ce81f7bec8197f6ed2e6511bdb721e07d396734cca9c0cbb268229833782b1b9950dd68c17819ead44b3f995a368b19c5c9a8345464af6b6d400f59b8341d9c5194032565aa44866f205919934c813dcc494b36503a7dd38f18099e7b83561ffb42b5024b2b6c03228a8e76996234e346937f516fe9413072e23497c37d022611eb7b6415065be5d995afbb79308118f7461cad969996922ee24f20965eb73b29ae6183f52caeaa2e34feb3ff268eab8e4daa9461a98e50a1f83d46753b37ddb40420276c0e0f1da25904b1f13a2e5c78eb674f1b9cbb4fe69361b6ef0467e319315e763544ac6a878cbd8f42a7cf91bba367d34db2827cf5187b48d711d26d3d38f14d025e1112d7534843f76b4a152334845a29268e77fd7221f55ad5eabba68743ef9ecdc372b04e808434d785de6bcbe5d4536011f463a2d8f84411e84d418394b8009621c140c69173e8e9301e222ed20333393cb3492613ba297898e10631f09c6c4fb97bc884690b6b6936f5e4f1f56b212a51afa0f0e2456a626a59c41af9cc65d7a98f11101e07582bd839020f66587c6f375073a101e1e6bf91a1876ed4561f0f940fe45a8c6fb7f45f3c21b90d818edf7239263a3c1fc03147895b32945e0411560eb36d1ee0a81576cb1597afbe85060d7575c5f9e3f5c02b149e7c4441c4663a5b2cf11edb5464b0cb4234004d4ccbdf6a63501e83b30f1ee1f0b6ccd4a887f3315a1aee4515662bf46ff63ee70e857a568575841982f8a94b9172cf90bd34b13933039c509d83cbb90ac055981cb9ce2ea631dea2122b31fb8f90110156abf3802c62457e711fdcac033a46cc69414fecefd8a83083951a25db3c981ed3c63a3cbb1119638343ab19309bade1de94e0e2d48bf5224fcc77f962b395d3377042f64976993acf0d5eec073215a7f3041c63631c91741bb7a69b0c553f80335c3054140da706eb9363f0daddd59e4663881f0e5f17e54572ed900fe147fce172aee4386647487ae8ee3ba6afa91ab1d353b07220a5a268b228e58496ad5b6f9dd6a7eb31351ffa8152fc94af48a9bae19249e15a6c23b3c9df3a63e1c8d278f04ad5d50ecc3d892868333e0025a7a9b3f031e2830e57a4c8a77c59066c43d767542c2e06f1cefc809112e3b70c5a040cdce80a71c0c79704152e9ac4c2f7a1f0bea6275cf16e75b6e46ecd6dba8839e27ad09dfecc54d543ad551bf3644b17d00f7055a99efaa1c012f9defd2bb86e0b2d482c9ac4e6a76db548a4d0dddf4918492ee83ca9de1b15547934cc0a2bd10b12158d9849f8f76c75134632ed230e99152ca19b6d61e57727aa9e660a18e6925289fadb3319d9bd062e041247a75a3e5a74fa9ce094c1661e96afe6dbb7be00a0cf86c5917efd9b80133e68b6ca089c1223c682f1ae41e73856300cdf250028d3ce0623d7357ccb3dc0d7514a4cf96fc06fef4e7b306d0bbea7dfe9ee3dd822627e30477b7e5ca18182ad96aaf5259405ddd1f151cb56b74603377c0a901c54ff296acbe5dd94efd51fc458080a3b06b5b5dc6dccf43d17624b9f2259e38c50e2eef13b1e98272665346f9f932340dfdc0f75efad366db5bb8031c7876c5fc6f62ead146b9e35317e17512355f89cf95c862afe1b4b021db9435d7693ed7841d0f74488bdb504586b67ade79c6b83ea6cf1d4308fcacb3bb11cc77128173e5c9edd7d230fd48254f9e14e64de7cd58ef339c74d96c59ec751664762aaa8a61ea614ba17d1737c3a6be5432e29fddb73f3448541265cf2ceec81a04c9f85f755b0b623a936a2d2b7e3ae9e22a6bac8265dd5dc474469ac135ef2569a8c65c29de8dd5c6d4ba6d71ff237f859f5dff1c"}, + {"0000000000000000000000000000000000000000000000000000000000000261", "00647416d0e4c42dfdce6024b99b39962c95df5eff028cda90e69fb3d9866b454f70356f2e7c801bb8bd1fb996279f23f0c770534950dcf6170266d07739cd2bf1858d8565c77db90e35e6edbb3c5b56a6fd85ca187bab1c1b2ba41deb7948713eeb6d96d7677a4d7025642a4890af7245cd71740d71568c9d547b6f5763440936323479cb47e2b0d5d6fa6ef8fefb8399817d7012f44670a987edf7b6d96da26b4846a4c67b09b90846219d8750ba15b2e44496f22962edfdba78122811fe5652f21854cff8d293fa47da64f5196a764aee08a13b30d81f21498f62f35a8c3d191b37097cbad1115d75cd9d910d757d26e42b44bbdf9da7e20ebea30bba86e4265df3272945aaa5b9eb371343b8bcf4723b3b269aa41ff49554ddb4d788de833245873c7b874287db55181fe889fdbf8b12a5e3203b31593f789944df23759193f82ea99a252641b9df0576905b5933008b044ede5c6847b3b9c3c23655dcb966de154c1f013ce4f22f57cd995f55f058cb678c9c47221cc881028063e480dabfa1bc51e2f308eb3287806f3f45420f74413cc28f04eeea3c12f815e8be7210a63a990100d916d61b890655f4cd75f54f70bdadbcc3b390a50f112a875b10c52f5bfac161808d770cf1c915daa204b47e282749a7b3ca05d14f10e3e48a8b147e062c11867256b44968458856b2676d67da1107ad39dbc603e289afb71d8dc73fa303b28ea5e512b804fd9c4440618fd587e9a6b5d2ee16a06d7ee76a42951f7e1b34460f334dcf7154c7aea715ca73ba120397f36e146a02d7cbeba14e0583ebd71492753ce61aa31fd45b10e7a2547dee75eb7ec4f548876c7c2f4f0a9d234f2c2891d685a95e3184e643fa292f0eaa743efe09321204e6d597d3817714acc6ce7df89eea3699fa2d23cb8d2ec0e4360b83b529cccfc26ef607a65d7e48d103ddc6947a2e2215beef52c7809c54d1ff5d3fc8c42673b51c8351bbeecc79561e48c616c6c5b53edd9916d7bb5a9679c2a5ef8f046fe04d75db50e11ad9be5fc8656947ef12a5d0f127f8906b14e6e5933797421ee9055c65677fc1a59d952aa658a16db7e75c674671ce573f24e791e370e1985f4dfa36ca4223350ac920120393815b499370eee655eab60b46808c395e0a353bf506361983d3acf1a76d67fe9ee720d57bd27f05ec19281bc9739798ccda835972b1d7527f9e56ca1b8f6adeb1120caafe61b42088a86af9ecfc381fde06067396569fb4f374dee187d2bdf251f3e3fedc9d22b2d6178caad3b77be1f6e7b47601b5e56b3043a2199a8129a75c112de08e61f56fedf6d0ae6cb0b2271a4d14a71a5b35292f9b57941e71125e442f9c464a1c91e4abd995f4f5c38404edc95682cd7736b121b0836a54a139a29a83eca9d86997fbc546fc1af9b28b0448717aff44a87357a952c17a9b34cd7b51d1fa03046b133086cb0dc507e6a28ecf4627bd2e7c8cabdc0c3febe86dd86cdb13bdd459aa77e999254baa2a373769120deddd245d2e2a941518a352a2d921db266f044e7ab70b23bc77f0a121819a58e33e29343d45e50f5f86f8852de6f36fd7160ddf4842b1a1ff3a30260500d416620821f6b0e6f2b67497d692bfbefc55666be62c4e092385c9367f282a7955b3bf95279f398004787f546746d6c0a9de210067159be67c923b8fbe1c7e3b6c3ba9bb2f5eae028226421108b1cee7c0651fe1c373498801c479d08815b3cbec0a39b7f935d62590c3d92b50f3258f7b1ad9497fd126c4bd3b47711f99e5aa44de46d7fc08f535c4f32aeaf7c59fc2ac39c32e11e314f8aaf467975466765d2e70f7b9c5c02ccf95ad2715bb4df30846bd9ed113e72d5a5c7ab55a14c3500ed6d5217a5586ae7bcad55f00db1854d1"}, + {"000000000000000000000000000000000000000000000000000000000000026e", "0124262befd15dcad7b1d044451814948ce9d8bb801a3972f72c4f0e16c8f106384138111d8e9b7954c0247ce7dc2e509d85655265a2ded827a2bcdc3c110b25986495a45a9a45be20c2d50c5e2b054eb654890b030edcc85e445025465dd699146ccd8357c8dcc0930733c7ac24860022a8ecb1837dc833c5dc9bfa322016e0af2666b2c4dfb6cea24aa2ec023a3e13947784185ec6ff531c246fb093e1b8aa7a577c746ebbe80f0165d42b88043359fa28505c532c6f5f46b67f679c055f9959fccbf6bde14f24d8fed8b539738d7e1e5804f0f0fecb25b6fb6a8fc1e9c74cadb49dd4091fb5339ec42d81115f7aaa6e648cc2b4e291620a7c769e07c5078bbfca4fb3dab3a93a4dd9335ed40bde211e0caeec1feb97c91ad40c337cadae30b7a02a7e62ca17fd0f0d598bd0eedd4c019e1795c0f5a35af799872b582ba90f14cb13257b242998a45e025274b62a260178f690f75176bfafc5004d62b700e32acfb9e43c0330777f398367c104ec712532635ed0ae62b7d154085d12ff595cc2c74f060186edb442c1aa5358e248349f4bdf4d288461530fa9480feaf27270735b7ca30bb10c8b448bf4bfd09b719649d35d487bc47534030fc4f89e9a586b66fcd6716d4af76d29febd75ab941814439dfa5d6febe2a972ab21253f314250ecafba20717ff99551458bd60e12c4dbc45c5e149cf13af812c391b2fc166ae90d94b12cb61ff48a1dc63667072f44942d549531654c81256be96a78e66683d5e22e1b3a3af854561cceb3b5766cff43f1a6c43f77a17e3b4e8c0cfbda76654b86f47bcde7f0c2f2d05b18ad1b4231863e9b35a1d43f07cf74ee6966e0839e37cf3037198e716176df5b6d16db03e303e25e19d3c8d0444365c09a61ed3d63e0353756df27d98854774b6d59dedde6159f1dad5ea36687b3c4542297533f120a015df16bfb76c1f3da4aa255385db0b36b1a9cbd953c6b8788afd52053530534a169b6cc95ba9abcc4b005a0ea0d4d5e616bfee532fe08d5ffeee4863fa630074ecba47d16762ff03120e82443de661542f2ce3e032be91d4b13efa0cf879239c5b34c5e7fc53ddaa3117ea58eb8d3353ebd1453c7f4ce9ad62c859e59722c08341796e37a297827330dd41c64c1391ef69b38378aef181e96e265994617b680d899264cb35633b8036611fe3fc823935aab97a75d58a4920e1d1ce1d32f1e5202f59830e7a54bf5d19ebe7647101efdb6961c245230c4b6e86bc37454b1eafc1366788d5c1aa420ff07e0ef54bf9d9e59b2fb78c98b33679b9c9d22050cf0d0f9874689a97e18d6b1e748e2abd97b3a5394f07d9d09263d6b9b629ab1bd6ea252d092da3ec80d348c4ae6aea5bda7f3946adff216f25fd11cc5e11bdc671d2b0f7dffd53ee6baf76681ba2eb0fa8f63027faa241d17e5accdef58812deeb9a67f41d5d9f91f86dc38351d66c7d11e425393c17c6191c05b98b120edc7fba41689cd6ff588205ed2d82ae1d0199a762ffa1ef15621a6d391ef83e5c0b37f1b269ebbe2c4042e8fe803e052af5dd490f234d717b8a985d7b59f08fee54ca1358519bdd7c7e3cb6d594ebf99ff67fb126d88e0d19e74a77a37f58d9e322f370a1d9de41b2576045e282d726be0b1a61b324b1d662f3abc605d046411d7b9d95535c703507a6698ac98f8703916153d86377d1c1b0c8f75b4852406fce90747b3fc3d670dbb210c280a76b0577121cf956b058e81b0f7e0464dcbcc13449811f8df58562dc8e40413dae69fa6080737ccfa9703923acceaa1e1542fc921a5af9eba332c79af12e94ce88ce7fa950fa93a2b254b3f9b58d8076b3a7b5c89dc29da8a10d13df5ede877f756ff170c65ca62e1a3c45331fe90e47487c94052fba7a508"}, + {"000000000000000000000000000000000000000000000000000000000000015d", "00bad6f1abd293f77025c045399721f8a2e5fe6d2b1cd055b5cdeb84cdd2053b17bce0838f088c7b6a520559812f53ceed9fe7e627d318c409df086d1e889127fd6eb38a5b0b71696dc90748f8e68ef65919cc1603253880feaaf3e9b30592b14448441774c21dba1116e2ca799746d910fefc239c3972c75aa86c3b262d0fb699a417c87fe9dfaeb3cc1cab62456114d3a98a3c9b3e4b2badc90db017e8e31cfa5b2e4b3d9850d1010692f279cfbf20f844a1eb8110471d33d83434412dfab1ee4355be6b9cdab5a4fb76ce8b3ebf9cf0cc14197e9edac539c3e8d694af5c7190f976a099c6e343d80cd9cc91df113ce7685b24560c8250e437a04301de129d5dd8ae02f848602fa6dce71e64c934d72e062db27e7d2cc327f03f46685e7d744228eedd81f915ab53d2da2842e3534764e1d0b4889d90e45477e916bc7ea7f6ba4ad7f61001ad18f0faf30c909dbd5000f2681fe58e37daa7f7702ed325e5a80cc3c0e3e70ae346dd2fcbf6d9c23b9620ee337292a79b1813ac1b26aad6b54ea4ab9eb9d2ddc3bf4bc4d9ae5fbe94471743018adb960f1564e5b9c74b770a20aa1a436a05cfcf4f6d8b85cbb87710bf2d95ce1b55839e6db539ecaac967588747c148654c0f36466187471da1da19e783d6e215916324e561c2d8276a110342f6c73c3006dd20aa38cce5df66a66cbcfcb9428ed33703d805b6ef240a930ff1f60071abcb67432a43583f9d922169ab756b6007e3fc43086663fd3146f3f7dcf6fc0a7652db938f03c36226313023b3fd1668cbfa333b303827373b14bd6cf0ad23e55f2c41f5777958ec19116de4f67b46feec842175d0f6573c9587778f6e151ae953c2074a7cc168f1d818af6103b74bbe1daa641930496a29213837c071b712a5d0bb0f055c18fe921d2b9634fc88b46935ff65fe403d0849e788354aad02e6059fbbe4e343364307e665e797ae8e5857a0a12bd986b5e69c5b53a32f391b9fcf8a26c8de78fde105a2726c3ea398b7caf4f7ab68d9053646a3167ff626ff8224b24c173ad5062558d3778ffdd58dd479db05f4b628e3045922fb139226a59ff1bb58067c10c4488453eb68197a06f88bc53c494f77b19d581026bc11de0551785eedf93fd69212db2b6d2084c6874211129e1f3cf65ebd552d1ee7b47563d83ac9cc1df78c08934791c7d1609ba2fdc59de5c58b7a50535945ea10c2c92dc6847ad0e3a9f3c71e40902a38be95c2281221c216d0c9f8ab7c0e7360022a10b942f314402817b6452d56220a79b99125ff0bb248c67c5f16e93f11a31c2cef9d627f53a2c37764ae269aad92fe6ef42fe5379e340c3c60a78cd6c38ace9226b4f53c09e91e80163d47cc87a0e1ab853d333a24458e2a6e0c162ffc265a666b8a6ff4a9e909c5d9e60691b39f329203246bd4919a59b0ece7e8ffd2f48342fa90bd00461500a23c2b90f4cd1ff35707424e5a19cf93d679860896e535c38b847af7d0b50d7166210152e9f179723cd9baf2141b588504d725382639e059b60fffcbac055004848e447b82b292358e8bfa2bf6a27d3740560b987153ab2c19cf6a2a13ff4c7b64010595b53bd807c3b2596d953fdbd82c05a30d48c2d628db13082b11bb15cd331913d5e04352e039a17461b3007d52e212e7266bf85a6c67e622c51bcad1bc5a5a5bd77fe1157219a5ef15c3250debc466ebbbd9ce6fdc9a183d1e47e325b8fa4c7df361b2bd785cf60cf50ff6aedd31e0d1fcbfdd83dd5b18e3a9bba584879cac9d52a3236cf746f96afdb5ea6752bc9ee5e58ba4d3fde66c3e03c4737fa75541f165b809ea4c73d78b94fff5732b3da301e83cee01f7d3141fb53d73393a3dac2b002c0d6522d7cf2e49265696a4e0d4fb66e47a58ff41"}, + {"0000000000000000000000000000000000000000000000000000000000001d3c", "004beccdbdc5053f2e04b6042ff2c6f396963e55c60eb85be0825b4409968cb2d6fd2bf2d52111b75ce40a1e45908d54c637297ab126383d0889c01e17e40d347fad9bfed8fa33e349c6e2da5a9b5a64ef5490c400949a85abce1acaf60e836827641441921c5b02be1048275638c73a6f996289e7916c4e0af19a1dfd6a01fe41d66fd2388d851957b27b484ac6919ddea75e25bb068652dcc21b6b8db588a04df616aba797346103a98677cc373a61c47dd061b7cd9bc01e4965325f196c22662ab407ddfa3007bc74d5ce62d449b713590d6c7b468bc493e0d08602409cebf67ca6f8d7223a2229fead7bda90dfbc95041c926cdf6544eeea886b1433e1e7e3160452da3ea37d7f55f57529fa3f6c3950cbdc381fa7c3efab0529259eedcf1e5b0e7716c2146bc14fc24f0b7314f91843a7f0e51ef8017da605663e13f34d7764ffe5347972087961b3c93b1e5dc600af49247108d6afb1dab18133bdab66ed485b9aa606c97d13dad29402f3615217fa42d3f1b167f1f3e2033cd41c2022488124ad82c48a510753c1969e3df706d2d5166ac7534bbf1fc47b8d77426207261dba8408535312b8291455c4fe52963f289825d9065e764b13385aae31e5735fbb90880735427143263b7fe959114de41ba7d202954847d4752bdce141e0c3f10b0d240d93f9d989a20b0cbc64f4763319c68c0fb9fd4c059c8d5cc98fd99f579090780f617541fd82514e7046981685486dd24b7d75b4e868ed684d48837308f910af25a351482b06cba5351e9dd970ce3a3dda3a3a1283759cbeb0a83fbbd3c3f0baac3129ce4515f2990abeb626099927390f9936fa82e3b8a3817dfdb1251025c2edf48785f3907751ac9843b6d4e0dbcc018d1986872a5da9e04395aaa6e9f73e09565d1ef989bb1caccb5ff968dfa75fe652dafec5ce35dddb4ef65b019f05abb84775ac9f88b041bb33e20465ddba478e0ea5de10f50b8743ddfc44c52e7b50794d7659e02819bb268c4d9bda82e9e8550a11b6e70a3cf53fb47719dee6a67420f4131d21621fd2e77c697639964ff903fc51aae8168320d33125df3173d2566a62de7a83200259da27d18f9b3a34a36d4bf037c571043bebb70ac12a4e964563a49128bb11ebee283af829da21e712eab4b6d6d316917ec9d571ef638aa1da4177a3050b15b2000adecc5fa18eb40b15dc1d41cd88f3ac6d261953298357bbb1d6fd13b5b5706a71ce8bbf78e312a5821381d048f3dc1a1abcf564cda78b181cf36e19c8c8f12468bed570e96550fec4f0a67526d8eb061846911d960da145e6b262a2b6c9bb9e392ed49d147991bcb339e89b99be9d2846ffc977425e93f6e2ae1c77ce7ba36c9533deb1040a0a5912ea3dcf18837327d632919fe9e3ed63a6084b4ef35086278c727ef3043e81a35a92a18f7d83d1f53dcecd94fb913405805b65cdb40f178ffda56756565d5b42c21f0813797215fdcdb5ba66af85f165c9217ed6e7ae689b76258e17b35d8f19a1bfc3d8a725216c4604e62ec514145509e9fc308af1ff23d833f139f77ffc7c8a620f158c5703f6c388d81b2de189893ab176f3f3070739867112e86e881190da8b6ce3b1b74d758c5d7cd8f5415a14bc67e163fd30f7f5b25407552c42368174bd5db20b8c8f6540155946e81ce280725d6364b70cb1d15511e9fd74059bb430e2e5730697b61e024b5d3aa368362c136f63b177dddd3003811ba8bf453c509e048b380556ad2e392a6bdf0c28f18170c213db3cdfc8431b02eb5ab6f9cb1fe401649041f4f4a69b01de15521be8a4033add7bbf483727de95c1fd43356fffeef928b5130c025bbdd50dec14fa322f8dd2e5489a57793505950ca70f08c4f8a8a374a8dff88b1c599f313c"}, + {"000000000000000000000000000000000000000000000000000000000000067d", "0012039cb9c4079c84406244afe15a40bb7d8629f008d61e2106d58d6eb70a75034c377df594fe9e91255571843883daa827284ba8160fc0b7b726859a68446db14685d91e15c1da9f57130c521055e20ff7a93a0e58aa9ef5fe5959ff01255847f663fb8cbc7e180144be0e58f3186222c464c7292a51d1d2689e947e1025bc95873e51c44a98b0f56d356d548a9ae9f721ee3b2aed1ae0d7a7d74e82c8a6b749baea6b1d39d0b8003047fa72c71339beb20195ffea4eea7e8ef86e5d10518b027266aa7f4ecef34ca69d5afd4fc19b9e320b6ee13b764cad71db74942a5db8b4164179d8a0e536368b06adab621bd662480390c8b8860ba31127f602468cb16bcd0ba3ffce72e10dc29dc0ef5fbd34213649b55cdf1f835fd67116d251dfa1eddc9e71f1e30b7bf78ed5965ac3a6e2ff15997cc74fca155f21601b9a3e854b6fc057aaa3e1e21df97fa9753a0e6f3d056200d89112f57f932e51b00421ad5a4cb7fd98ef24221758c31cf901799177ef514c825ec9a2fcd59e157e27e451ceb4d4f532440e822bd289a3e419e0101b5f4c3b42de8feb30c753846cef04a2a16b5f894f0c86346443a25eadad0c1832cf5ea4d267391505491721ee380c90fbf1e8fb224b472d100d4d23d242ee2116715b7ea41233c5f9f9c1c2fadcdecb4099d0504ec0fba04925919d6351d7a66a759cf1f70cd9da9f0ea3d2609924150bc208b15ec8ae467e6b6b974ed93d1a83b9da645b6bde6d65e9d762027e4eacdc63a71d71496d7e23873b9848d4fafaad60e567399c8dcd2e4e76d47e313e1fb2fcb53acc2e4f9a21eab5ab2a10a3a43b0590f748ec2027048c55fee640cc7abb0c12322fc3b34b7bb7509421e197eba250ae385a46bc218a769c5e4f745c8f1af5c5c7e685fa71f0fe001c5c65c334006160dfbcdd76d850f93765ba4d31ec3d050c2ef999d60ee6ff16b139116549c8cbf3580dfe09d13a56ddd10bc9d78f89ec175cebb32f0d1a05071a005bd7fb9c997734cf750138f4235f109f3ce7886ac99be520786927fdec3a4910f1fe074cb23f48d107c3dfaf3c1b5b8dbe1ea4bcc57f51e560d27e0afb0ad9809d34c982b7780513a8def61dd6abb8dd478d18e9dc4e69e11c179d1162ce6633dfa0d2541f3ae2496a3e72dfe356ed346735cac2ea7c2585e6f8e283050c8b5b3dc3fb55c24d818c5fdf8fead66add559510dc97f334d16dd3d613a2c60923e042a2d538d33e0f1a1fc9d605754b024ef2cedb61359184615343311d8da49de38ac888dcd6e4cd42fa6b22ff5db8500b1b957da33852f7a90b7152a642eea0c8de7951d7ce20d327e49cf08601f3ceb5ff33bb41b296731ad639251f11e50b929797b16ae38270caca3683f57d210e291156f0d79972cf445b786708ea728ea21ddb33670895d59f5d5eb6f72fad335d37a7a6d9f087fd204a33558b2a043323bbf14e5398c2a65784f897dc9c3608e6b11258f72271d38d368fc6fbfa9211389e137c0f1845a52a07f3fbd78bf1838f751492663619cd270f8768af3adbd0513ccb0383423ef29cfcbbabba681d398912391c9c13f882a26df47e512a68a73f89131b296f606a662bcbdf1464dbc44e709dae86cfe193226ba784ba285e83b4498a8f5e68a91af8733b52d90993eb78a5507018a196a0d6f2f97776099f589aa418f467489a515c50cfe8d33e325721d541b3b65d901207c769835b9e7fc773228267dea9b5be7f2faaf727eefa52a25519c5ccae1300219c94fea58a7cd4300b1523d1a32399affb5da2ab4b25ff64f1bef67de32cee39e9a719c4f303918a287fd92fbfacc91e026b14a280c2604e1a9db0b701d55a11ac26f4b2de245b53c7431ac3d5c722c802c6c9b56625e735575bb0b9"}, + {"0000000000000000000000000000000000000000000000000000000000000083", "00697c178fd5051eb7d1b3d5e274def57dccffec14195c8d23c65b37298b1a61efa0d1fa32d69f9def6f0990df33f7105ecd0cae66bfc7fc3cbb36693abdf0225b2bcbb2225c6fe84d65f0b246028adf8bd79a7f069b2af958c558f6c20560ed432cd622df3318065c27ba698cb50d5a7ee41a53190a349e690fbf9388da101b01d608a3899f66ea5144ee7e44591d441d09455287dc36367b4567f5e1b94d57738de2fab73815bd04e79d216519159dba6922159420c79514755325892654daaa9355d553e7ff874136fd80678732ffc10f07b54257f5e78e3d8d93f41034381e0a3db139c64308b9a1d6868bda29b3a148180de1f956abdd985d9c14165a259866deb1ff6894c7987e8d15580d35eb721a311cedb750c5c4ae07b58e0b505d42cba45e790e2d711acf4914fc0eae5f6368c0edfd56ac4f1fb4b52d74733b7d6b0591be9525ee9842f282744f15b0b400e7c6273f1e86f3bd82e1c985595b427091d90391806e3645af7f539ffc78187f396fed729e54f5dca611b4cad2116483ebf841a13d3069751ccffbcb6be24bfdd353552594c34d9bd5a0326347ee065ffca1890b330fb61016c56db55922841660597d2af01a9c013bdddc262495fe8be91bb45544ad19018a70b7e80218facf473a541cf2c75f442b966fc20dd88874f1a043b71ac427d49c6787f1b6e56f418ee1d7f693b993105095a420d14863b6a284703029e519acd6d04f562fa42bb2765c85e6fc1744109b38af9d76a7fe7a0d193a534c2b9fd33d00b627dccbc530d372861d1d4b3c46ce566856a8a3b7ce160ab07a0315c22f77070c35d4635f2c55c01ce7f565ae7d34c97238f399831e466e6e40a73030d1b848a4bd604d64c2ccf5999c2e38205a9a9a64f763a6a845379d5844f5f460db264b7f347737cfaff415ba8038239bdb89c6754a3ec6af00718c8aec4cb8a321f5a20ac76b9396697cb4d42c0a0716564b50df217de6f15888e5a5406e1286f1bd0a1e88f3a3e3ade3ef9270e5c6d33204f0e66c3e3c70f356095a5fb6d3c7c797572054824344203c8cf91bf0bbefc212fb38a5bf6284513390dd1b02df51ce366d35519e12b56fe89a837439dc21be73b85ff1144835ce1f2524ccbf6ae1782613ed7b4e7faeba83c1547d5cecd6a5f3294a88e56c1841b19169561c0632056be546085a66f9c640b868687924f77105df3c223a2ee20b5a1f8fd942f417eccc59d7be3afe3f5e1a4693b71d82e34c43dfa6750cedfc0ef5ada9cebe6c5d7b753b15f01cfbe16bc9512e54341e86f157747c05f0d30a71b936b9df8ac26b21f074ce36c77e23f408b4ddba3bcc5d36fd4eb1a4f7406ff6b9f39e88cb1f6951cb371e90291be2757d9a6a5dd1633d5e55b22ac49410ca4b865b01cab44cd4e150f69dae5c7dd3007e95e67c64561d9c2df4e326d9fc45ce15dfeecc10fb408cb3ed9bdb776692639d4677b516768ef9ce05b56610530b1646b9cf44329ffcf9ea09bf9d73650f64143deaad2effee4ea3144349a0bdb9d07deeff0179a2a1d761aa6132fc1050293adf09b21d1238363b6af314aed8715efce3c519dcf77d9da0cf3d321f1f4eb9c0118a385c6df6b4f7753194957d90bfe7b022ecafc558dcad753e2174f7e354762ac434dd8d1c0476c5469c6227ff4ba62277572fbd7618a4bf5e085d0623f80b9c3f9f594ed7e644bfd9f38a4a5d1cc02407cbf7b37b9901eab632a85164c103278d9ee7422a1c6a26705f2fbf012a8337cc7847de22205f064d0be2b742b3e128fbd8bcc10c95d4117a92a03d3b0c25fd42a8374af1547cf552d216baba794c16aff7a111c5cee9c79cb8130e8c9484bb4101793cc8348ff31b5f2dbcc6cea92557f6741fc94167a21dd555f9d1"}, + {"00000000000000000000000000000000000000000000000000000000000002a8", "0027752cc45522e0f11b37d0e040b80e9bff5adf166cde4384e9311d7fe44437d697567b8a635b9f521610d68ffe00e06de18dc474d5fbdf99b591824fed0b19ecae20124f531efc6955add1fcff121aae52524703bdfa41946adf7fd0e70860d7d08a76465bf682ac12cae1d73ed86360d9f737c07069e8367838d682da09bfd25e481b67c72381f798385708af4eb7fe6d2b126ef95050077ff9fe4005443bd08b4e7ede9a4aaf05deff8bd28dee34970b62c0d849e36cce225588162491c56690eb5ba59b1ee58330bf4ece0edfd6b65a223c0b593988ad24dd9c743393f2e5a56af010294f418c2be5903c6191e72bc714425aa40e981fb8b44d061118716d87dd1f12fed118a8a78ecc6b87ce9c651b597dd7538dc0caef69b1d8f06c644f576a1ba7341aa50f35fa49f577a31d11bbe2c0aaf5cb10127fa226d8a2a23f179790f413546b60bba291934e560f9900998996f0028675a06b01a1036506668bc33928c703b426088284db9ea62a378dda7b8f89e77650ea4202d94a6ee4b7d81dd202929d195ab26983fb32b502227321c9da36fadbcde4d2ae39edb01cdf913f64fa1c85b1b10c874ace99abc1f7ac1b74759d7c36f5002996d356be580a32d3bf12bdaba42dc95244f897743ae3f3ed216237878cbc048125f56149613f2edd0e4649bd72b96a300d61a416a726d5c6d5bc9430221e023982cd5c0a1dd973d40196dbc9c06dd17f9e180308fadc01bd5cf44f1a9810e9126718392030537d100c7a60f4d143bb5f7fe0013e7f5a87f131e8f938b1433d5527c12b94ebc201c64efb7733e22c66b9f84b2ab8170ed09b50cb3f6ed4781db7a1ae1f8fd70e592cd5fd8e3f4bf5a5fee1d8ebefe9207323029cfe522dfda7629551a321c7bcc8b410cd7d9b6e8d1eb1c04275ff26c6a8b489b132d5b3c96dae8e395d79cd22037a7df32c5897e99acda06e1f10174d47d00d80d5100fb4d58823b60ff4f8b41e6172d11915d9f6b84f05f5f923f61bf8992150225875637b65a6d1cdf1411da605ef062ab081f47db4d0a1cb67a56505bcfb3f05d33c030253e6ffc0e59199cb6639ec739b35bc25067243729883c8fd6d11e1459ea636d6bbcb3c16350c964fd58e2c079bf7fb42b9139d8865df0014b3f461dd0f41e15a5c6ee3e82a2f2c62425adda8579b0d0395b3a801a5adbdee0d5191d69ebab22cf5321cf728172bb3aa123a01382dc5abffb1e781f3a939182913a1156c786b5fa5fee5d3a9dc24d86174299d506a18345f60b294fde76125b542d7b5a9335ba83c62be0e5c7a5dbbd08009e258a4b9ecba23cad17a3cc19c1af4477b6690bf42f71181c9dda21184ffab1a6c170fe203a4ab4459f5b2ccb345e69f3418efee8d30ad405e4bf243a0ce3f246025d3aec74d29cd099b2560049ac8e823e195812eb3762566d3bfe6db5e39cb1c1fbda18a295594e4f8e632973253599f2ea4fe19d00c814d6c4e591e492f001286e9192904e87872b41b437bed22976e263da0a7749e6ebd4edab7e0380c040ac789d2380cc8c29c8ab0d8c7a18e4c6baaadfcec0f24a4d47826dcdfabed63c7d86d89790c621b97941d3ac3205066e5d982dda5c66d3a7361d35239ddd74095de5ff95b3bbbe34be995b7e30643ed199fd45b05ac749cf581fd8cf038909ba55729e463d9c59c7c08385352cd4a3e5e5b1b6599b72ed01ef1d77d20e50e2dc152cc88d64b27960742b03de95203a8d2b13c2e4cb1ece617d4e974de851e8d3eeea7a902fda8b90921f5661dcd8c459c6cc0b9625802f57898b439a8124a5749ee5df8a9dbcdb61c0775bbc5ad6674b9281d7d895c4ed75d574248e252b4d765bca1986d78ef3aac8f6be9956d2ad38968c787f72fdaea811dc5fb"}, + {"0000000000000000000000000000000000000000000000000000000000006084", "006a57f48344b82cfa73304081e16671d9603422d329766323629ae4ff6c22a2ce35cba14a1f1f5a942026eb76151ed1f1d7b2dd1a2e6f7c85ab44563f21eb42f182c762d3c76ef30799d21dcec30683ef34fa670991ff0a1a8840b38c0541ec8d99bd735ea33b07141e9ca47599770eafda5c65b9a87b451dd72f5dfa051170cbe74138ef03db7ff45080b60e319e5cb92a84286507334d8a301b3d1776e56af42cce381699cf4d0302c36303a930f95c56f12a04beb8d69dde399df1228fe15c7e968f3ef650c5fd57d9405f4fe57efb2415892407b38cc9150ca4c85841495e5e20247ad6b8331e2739ce74cee1dec0d3cf62f40239e9897ebd3f0357b4d19fa3a247c918a42a30405cc586d03a7d3703e289fc3d9855b969fb7085d7d277147cc70c9bf108c6708d8b25de116d6f4ae633d838eed0081a0b731f9ab97a68d6f681c4e8f558cacfdce33f749d9120037d934cd3d34f0364a5f54951524f266f0b3873dd38246cd16c9c8ccba4565b91e95dcf7f318efbb7131682a8ef0d54967bb4ead37f8c3a5431da1914aed823d47cf900897fb7c077c33a7ce5578d907a771d8515e5d54ee9d50ddbe41e58b84cea166360401d0fae2b54bb87851edc6f6d52b837f367e72720f6bd12bb1b1855e68ddb2536e6f971b7e2fc44062ca6d1d45524011562fa8bcbba93fcb4426f665d75d9d156bd4d052e839e12d6fc74cff488c28dca1be28a103740d406fe5102238d2317bacaf12e4b1b362541cb6bae1627ac41d6834ee7012a0256d519e2a16677b87d867b2f268ee3d894ea77f56b840e99d14f2ac69fb93d531f5c59baeb4977a1a2fcb39c1625a22e2f9a52f82a3d507338ad697465ae57e474462cbb71d7d1d741c521342e6e238cb8b2c626b361963a829cfb8fbc9c3d46ccbc0b6caa6401ced476290e670b11fcb7b1690a022f57ccd65ecca3719e7059e7dd56bce10e6de76838213a5448935066ef4ec70031e4283649c93cf6f11781eb7b58e65f439a161917f4d3d0eb41153ff8e33fabf3f0f85702298cd9a7a8027d3f6e136311b40306eaeafc73a1a3b9a0e1d35761759b7915c61ee26c0de27cb2525aab6b2b29c2e49e9d699f151f9db0750baab4d169920cbd0d7ac2e97818cc6207f0138ad226a724520a173845157432c5e16cf9960b029c8c270242664020351adda8db3058ee8aa9884cdf3427e326ee71b28e0a71eddf27241dcadc41076806fe28a7036ff6a861e0adb15fbc4827cdc409d6730bb86807235aae4cb01db5c757af54efba79e1597bfc1efc4f08aed13ce5023040d357453d5af7f66dc6b35cb7273f945c82be5629a9f1c09756c4fe489329065db4c50c45818897471fd0403e65ac73388899b8e4d2aea32feb9ed5d3258929c806f3e6a0a0eec6920ef4ef46048e69641b8e42473ca807562cc36a59dad5f1ef1b170133da60a630c19012e62da4ef8f15a7f2b10f30182df7b9655cbe77c9631308fbfbc83262069642313aa7baebeaa68a1f82a3e75941f56323a00ddfea8f10800732564e2e53088b2235a120aed8f43aba9358135d7e91ffd2cc28ecdcc929c15aa2b36eb3ded854128c3152caf634c5fbf746f25849326247ca740c275dc844b40298f5a0cf14fb7628ef3b6b0a081bd66906019cba698b4b5df8b7a11add905675aed63e5b89155969a0a7928961801b447892d93e76f80afbc2d2266f01cc90535cbf919055db38c613c6847d7a81fb6a000fad92e19fc1750fa87c6765b52b2a86db745d067deaf4182b5957ce53925c7a7c393a757b1d851f40899fc28f74050be8294555a5d13b2a5d41b33f511c3045b134ccf9cd02f50a373a53f136f76b9e5e7e5245a566051ea0054467f80815ee317e3b96fc1e6c"}, + {"000000000000000000000000000000000000000000000000000000000000093e", "00f5506039ad7f97e6ea71be4b99e666792e55d4c33db00d884c5011a8f994746e345d1c292ffc6c1e631fa6237ebb8aec146c7e024641f18f48ac050dae6f22054dc99f4a2ca1c8da038a3ff31a411ee129374f066fa109729dbd09647113a0e8213665160530b91c19660c94ee9aa91b364837375fc77a5626b436c4a325a66506b21238c2aa7d6879466bf24e53b1d3964626f235f384115106e3dec33993254cb62c0b3faed701c8771c8e03796daf0310ccc562af49f896908d85074d8cd965c3de6159b0e4e7f058a3ce12aaf58c540399d66c628f8bf902120334bda8071dca9eb2fb5a085583a6f56a5aebea45656dd05887b97a27d650050448fa5229db98b3bb1df10fb7d9d9416063d58e9e1a00cb56e4af1bab849e82c91ac2bc0a8388f58b0a08c773e10fa6ab69d56162a6be647f350c6651a0bb1251b3040ac97b2cd8b6f9b3d4ee5a6f6f74bfc5c10212ba6818a69e3ff00e43a150cbdaca2836d8eeaf2adedf4054e05197f40ef7da2ee47837563c3ab2820e48caed221d827b9e31f123d08acfa44a0b827342371ff416e5e3ad0197eff3c049fe6b625c14f42a8802c54b72291593d923279554707f8aab3818baef24175e2d1b7c78dafbed3d382177c94c4a3cc9927c1f1c6b6bddbe1d99233d2f15b595f6ad0b08717e358e244c5403106495ff7bd2552bb5eaac0ee5c0173a770759fc0f32064885e63b609d022affd8dd5efe24481730f5514b070659410e5916ad4f69365fdef6b7660be888dd71e23281ebd7f3a14de781ff98493e87e213c80f130348f2e1bc26a58aff60d68d6d59fe57c20b2be68e7715d4d5e2e975241d4d5426964134ecfc0ee7e751e1cd95aacb8612a264a5a139b9414deee93f31a4a1b893abb0e2eefc238862ab0b2e94fa8d8944e655c61596a960e083a72cb86523e29eb89960c2019f3f58144cceb7066120b0bde49ba0a627bed6280a32ff68000ad9e51180d112d6162b29b469d60bd102567af4c4176eaf3542a4e1d0cf843232073563e90f6d0413478938e9bcac220b14e03eb6c7221ff4cb0513437b56973270f35ac401c1599ada29083f38bd12dcdcbf65b3eb43cc5624f7a04c7ef5f2daf0882f0b38c39c7307f019fa56516421c621bd8f72b463d055dd1b4d1c30ac49cf6a15fa8ad74ee650e1d79aa705739e2351055e41fb7390db399eeffa6ffc7ba2e50cb1d4a89e1ea4719abac0f03d9dd269a4bc52f5ed0d762dae6f077bb39591916ba3cae176d95e1a178a1c3c536f571e9885cbd3e2499818d9e226261355fb09040c0b129098d5d5199630697f9dbaebcf99ecef0aa72ab553aaccb771e6727543d150c16f39ec87eb091844af44899fd0ed8830c79d6d375eda2918f0172d33d516ec61f993cc8106bbdb6069ffebd0bffcbc06ff57b2e54f999117c7548c84d207d717601b84ee0aeb65d06759fb6125b4b1d0ad394ac20f1a3ac01f0ee6e2a519a120513a86b1773091451063e1d88e9816bec697b7dfb7e7707dd25616dfd38e4efebc569b0d280ed75c46c7b0eb6f7a384f674b4324c5f93d2b64f51b6a981dd8bf609aa8227e7e42b750a0fffbb3148fe919624f933bcadd7316e161a8bd98e27e242625f15d9bc2899e9db4680552034e8ee9db1570253c0b523d9426d569dfef0ec187332e46ed8c328d1e0a1858a93d2635724fb1140265adddd83d818db53f6138f4171afa1598bd6f97152c75c057c7bffa9e868b44fe62f320a83a0d8dfea484d65f3fca87457b92bf18b0859434c75ff35793fb558ef8c46f1d0c19934047e8561c119f6077f0025572dcb2c10a0a00b4fb7350c8d2fa7bdb9168f81b77d885e2627e532e3e3dfa6096ecc6d81897573fa31b01e4594cab2059dcfd3d"}, + {"00000000000000000000000000000000000000000000000000000000000020f6", "0008ce1f5b3590d9dec4c02ccc071759fb379de6800f33d60a1fcebe3b50495780314b17528f63793d5705818bb213b15c87e9b8a73cf1d81802421c9df9350e513eaf6991a8f4d363a5dee0e1944e24a113702901a6e2b0b44367fda42d1371c0efe201a277596ed23966e4e2fdd3bef1f6ed16251fee1981974ed8e30106df3f1c1ad4ad56fcd02561e5c2b7e16d800f4c0b1d0f3e13fd0fd948f4c3f3d4febe97393f31919cfe0074fdcbf9c0310ac0486107dba114f2b45e79ef3708235f66c6430db1a38b10eea83cb8fd86cb5c76e80fe268c5f69a1fb546a7f8c2e0fb18a6e075180eb8126cf3c5b2153bfcbc5691342159c36cb032fa21ec00a7a4030952b08f0dbfa05226fae6121c9b319baa13c00ae6c306b7535f7d61444012a5ddbb60f6451e02d26a9eee8227a7cb46d5b4617456e5ea7198571f3bd826d4f231a9bfe28794f5edb238a99143cdea0b002271c8e0736997dd7730a050ba098e823395de610d49907672d9530d221208aa2053952a6aadfc3a591468f0cb7dc67c1dfe57344124f051fd68456ef33316b1cd4eb510602f3a6ff3f0ceb9070d30f2fd90a4004f9c09468399978bf907be686c6fafadaf3d7dfd229e3d51e91c5edfd84b2253dcb73a09195dad2a241b7d870e3b9b6b53481f74de1660596a742a551885330fbf4bdb93ed7f55e34356ead190a2df9378db8607a38334d0902b21fa7341de85f036e1f0cf991441158d9b4001c8417dc484d17c5433620471dd109933390483bab750e5f11df766e216db65e33f0b5adaf7418e2da1af1d012179176569944eedfa3da514a683145263e403cc781d64e812936af572590b1f15ff7234d437c9721c20f1abe593eb2de63d5102bdfcb05316089156e249b61651f153dbb27f93b541cbfe17a35e2964c1ea28d931e972578a6e44a0760469384a0b025a01f6c4e71bf1bb60e4e532fffca994338d11c23e2d32b37bdcf54f507a5556cd7b28070df4b91aeb3e97e74d94738e19a851754f913be9522d60b66af958775bafde2b8d3b62c00665b8451715d53115c1de1037725379960153bdec11bb225874c1bec57858341d93a102c00b67c85e1882b9f3feca9a4b8cb8e8941428bc25729fa321263af31732de9cc226503d163b24d21edb53ac1337fd36c789994db9ca6e1d99a692072fb70436084aa7dd60f32fa1c8293310a9bcc5e417168a5c191cda39c5c7c6807634280a564d77321d1c63f929404d005f6632d55155549fd5ee89394cae2b810fe61858bf6966479425beb5bbe2b43ef712fa0c1eae329e784ad5f30247d84fcfab5fec2f1f71f514ead3dad5aa6fdb756d8294dd318bab2d8fdb0ead1052632c8dcdcce1bcf5041cc8404ab99d19bdf2d731f90d8ed54e18dc8f15741dc3bf503bf6629fb89e03f705ae9ecff611d054e45c607f44f11e8e4ea780093398d8069ecfb0fbb99200e3389d068e7f36879d09571d6fd05352ebbc3b91e71cb29e70dfeed5a6480aa51550a4e740dbe48ad2e1036f68a62a0e94a2300db6d3d1a00a0f23742622b10e753b1d50a3d3e1e30ffa0dd773513681f37194bb7aaa7e663f6ebd48f22164bd4e4fd17a7c99bd35fc34ca1b69bb4f1db88f34e8a73730635d952b2a04e8222c00a2662e571be704b012b3180933d1338070ef2d669bd96b4cf911cf1a12e693b4ef7b9796468360403d6ee5ce95d83fba0d9aa104ac5b28e6ddd533e9417a737d3942ebbd6418e32dfe6047c70cb409943d3bf1f43d66be3904fa077c7bbd9befa609df9910e2bc66c535539a134641469c33f60d552515ad5005a85fdfb49233b7367eda2129778e3d9d02d0f1f6f5fea2c0c8834ea4bc361a2dc876ad93536de8b935962b66bf94a9c13331a76a"}, + {"00000000000000000000000000000000000000000000000000000000000008bc", "0063c223da5b62332a111287a71ac0ef23a51f78390eaa28b1f6904ba11c29419b516eec19c1f33113f10b440cb944d5416502651198ca538c28ce1afa662a1d6e33a9a4091bb49cb3277f6b5582170b9d9a48690264677d28d5f5d38e75b0e37d43816de864916ba2229c5c23e40d502d7a00547a6d7c502b68e3dfeca202722ac773d9ec1b13ffe1e6d1d07c3d462ace66de04c0d7d51fe523ed5f6df6d697d6b0032c2a5d4f31054bf4c4eff68d11fd7ce2193899c58daa25b55b101cc7a1631229973fdc7a0243e56e0831529b5e95ef06ebe36a9d93639d92de908b69643370e757fc7f690e7e0c83e1117fe3462a2a1b71f30a3eb19fba1d810902ea218293885b024cf1010ce731df2386b9db2621576434dac91baca31c97765ac59105ec7f3676b60f48c88ae48c2ee36583c156ad439df5657a78020d11baa40b9495c72f885713fee757955d82939a8989021955587a4e8f12be52136f08ad8687d5849fe2e03ac3a2fea5a82415c094457b85e342ce83e13f3ec916cd61e2be896f6339aa53d364c5f073aa4a7fa6242f26efcaeba539333756f759ef6ab88e3264d1b0b003ad68277595e85d85e22488b467ef793302acb2e839128ebce76da8c3e48683a431e2e5353c6a3702ea053548372857e8b1a9081203206ece4284035de2590b32cfa8795f1afbb3ff116930254db4872dc62dae0466134c739d1eff47b5f359c1e03e41d267d779075cc72b7d309f4ac10787b644f7fb223ea3733c85c40c04b2224cc685ca717f9479ab6aff8589027d8e861c42b10bd852dead655594477d5ff9c14c9eb7b11105d376076f48cd07364663c45673d0dd9e085c256a414c7d042e59aa5bcc1404fc1bd0f9b67716def2d613ca4458455fdcbfb41b8274d4cdde8a641b7b7dca1f0826f0dd0b78ecc7c8d283c6c7bfca40d05499f5025b8b935ba95031ecd7f3068e9e50d6da66f96682106d272d9a5c41c8e9813249d6eb208effa2bd18f82d098bed39634addff76147f86f1882b7f9a5e166343a714f26e552d0b0de03664627523d5c0f4f25a5e07626e2cc470167d9619a1c685ef7deaee511b06734deb3d0241951ac8ee9f98264473769a41da3ee97c1152fab6ccd77aadaac6874862f012e73bdebc47c31e2afdd93287e9828e52c3518741ed0663c01c0bee065894e085caefcb2cd571c89b6f8e5ebea83ae64d0691fcfe8119286594d7cc2ceee4ef071a6cbc79e63cf3d520c563f8e15d9684f61def3b95992b556b7c47f2bab54e56a98bad378805bdced6faeaab9900a013d8756e210a1fee619a536d837420e16f6292335e2b280e8aafd4febaec3a23141730b7b4d9d2aac99f236a23dd7a9b66a919d0e3b0f6dc2c65b70dee4d0b36ad41f8669a38950c265380fcf9e10e2ea47eeff0038148b7570fe366ff29110eb09200ee598ad81c8933c66f3515d6478f88f468ef56e7752ab71035c6fe0bb1e79fca097451c93810d581352cd481fdcef0a30d727718c54b4a7b7b9b1a7352f224a2fa5879b62707989133a32f1331eda93421cba5af4913301a5bc2120f94d4c917f7d0ec7bf350dcafecc67bd6d612ef0878cf5ea62bc7c1b13e728224502194f8c4ca442911e488e26a0b07dad55cf174df9ced28a82d47bf09044f697fd4cf69d49f4ac216846c5f351625def7711f7266a03f09e63f338af5aa666162d9dc55af6cc8078068a0a369c68d5932c23e1e63f59d135e518fe22f226e29f3650001d26714d48979239eb4bc9e45fe10d157580b8a7833381ad42c6aabea4568209ea8301d86c73b79697b8be4a542144374d3564004b852d229b27c940852d50548abe3ad087f2e8efde93b1dbb5254468e6cd970750be05b85e46a0a6f14c47f87b4"}, + {"0000000000000000000000000000000000000000000000000000000000000550", "002b0961bfd11882ffe9529774c819f626aeb7a39806283a6940dcec93d772216fae501a809af71ba64330f92d21572cec33a861b320891ac3ed8ccbd2504258b7b7438add4353639c164eb8ce62cee34c9cf7810331139cd29ad8b5e0ccb103d5b53001aa4ff5b1ad16ddece6aeb4a10fecc3927fd830c71ee56dde1d4d18ce94a149d47ccefd8c028c15c38fbea5e1f6648c39bc45f1cf5d548364f6e3ee927331f543082cef9007647be83a2cbc299c6a66e08439361aef4dfa22a51a4d4b047c9e09b5834cf720836da76a887ab88a4212e39268c685a8ad04a5a2161f25c45ed82cb8d10625a47bcecf161ba10005b49aaf58e162c2fa3ba511129b9370b5cea7dd236b21aecde7d9bf0ca09b27b65a16067ae4a11ae1f3be7611ba6b84f187b0be99f11c6ce302c14e4296bda878998e533632534496d326313483b3e75959f8d24af46b88691b3dfd533cde7000846cc88f8d5f47c93ca11824a5c7e2dae63ccf6b0ac5cf715d7e5ed9f35a41b40d329b4c8045d588ac376619c88c967667f5b393b75264c93d01116c08e152b20e6af1d9e1ad487d279ab1f005e270eb5eccfc0111e3b2ae1ca8ab7167c1ae01a88f98796db924d1057fabb5641ca8aff09cc5c51a6ed50659eebc5738024d4b9bc7049f0fe89e56a0f95c0839d1c297940033e65afa73982e938173570b06eef6d61630b6c845079c82c5b787e99c487e30d8bccaacc48008b66de313b9e376f8c693f19ac5e1ac2d69811269491ac97a09a397af724a7aedfc41f29b19f61665992b1c9061202839b120a9f88174a274774d33510983687c38350a277710111ffe65136e442eb6e4c58d7628b842de1c3d95a9e187aa4d48d75953956ed9aeb8c75beabb10801c673e6cf6a5ebcaa49682250cdd272089a2f11d6eed447a1779b2fc5bb268f41b177cbffd149fe300c0ad8e12a013352598f63e03526db69fb39e702702fa0af13b080231d157e169ab40a04511209da5670a4fe358121874c5aa3a52ca8e9f196f71acfb9a871f4cec42c8b41629f95aa35ef5bc9e1f2e565983b803609bf1ad8f7d278ed0a283dba327c0c303cf1c503c7723bd26ea84798329055246ee73a97b0dd82e3b139ec38bbc72528595f6d2a2f1e50365068f7393bf1559215844cc44ddcf3be1ae47fe7a7a0bc21c08fa02718d96e21d6e37503670c87cb843aa2bb2d25a1e2042cccc13093d65f108a64cf66b21e1e771fab34002e133d2d29abac5af2f53f080422a2552e0adca0d2c52a436e9dc9f45d010a5cd3ed85d27b63fff1ba809231234e143dc1b71f2f86ab9c992e22be7d50a675a69a741e2d6d6c740aae7384d4f586ea47eddbbb216d4a46fcb9d5668f7f7c4a8b6c7b8fa1ca1bc9d724cce64c95915a5339b0f16820f432ae9a996f278e203044773f141cd470931f2693a62e3437b6d7c2d671544e4156e1a3fbba79d8423036d33c743f97ea2f603cc4f3735c11b1af9f74238ae69d6dfeba41faa3108b54cc68c59f804f1a94243acf031a730fe3a0cfa07745cd666505f45f780877a07f62572b58b9ae8b3424ca55a431dc4e9448b14d7e1336ccec8b2bc11e707c88888a7823a4ab9b6c256e4172d451e91137b83095993f95610c001271f45a250d6365e552f768d0403070f91a19b480bd032726d4e19583d4b544da3aa05838a9aac1a58dcf80162da822061bccb1971464509597758109796392297a50661d21289449a338dd158f94bbcb3269b895ca9768fdf6c40162a82f7b24f039852928236a717cee744b8e453610f14459e84d20a1b6099809f817d10d13447b3a7a5b27539bbbab00516bf240e8b8f40adee33911da6f2d704367e912b18fb5a0ad8294b75701d0288fd2e20718c987645fc"}, + {"00000000000000000000000000000000000000000000000000000000000000f5", "000d39b3d64ce58f8fe7130f247570a4e4ae89e54415686341ee0df5e596ddb34e3fb2623d2c7e379e9e0158a61fca4983d90427b115088bb05ccdc2dd281432dae3b7a819fedd5f03337dbde43991ea34df0e30080565cee2da172af14f92277d4b5c1e883117309f1c65268ca4a4a80bcb9ce4208a6914ee77b3d76f98182c2aa166dba453719241a7a6fb2441d4e833e7ae18d2fafeb5d49047322d01c6d6de57e514934da06105978481735199b556a7f2caa66d9b1e7297fe8b820d4036216a847cff0432122dc7ce4904924a3d196406491442251e70a3a5daf1e3d3d91ecd773bcee8e417b605f63a064c3cf481d37f7151449d588172d13a0cd53109865ecd83d67503cdb7bb93bd1138692a830f78267dd49c68ebbab1e53258cf1a676e2c9e6d8c0d726f8e435ed8cd7a1e0880b877c55b3a9bbebfd4305c91e5daa0373fe08975224f502241a9faf9cd0b012e57757bd87b42f62213a20732aca5b58b5e779411c753f8c7ed8beff5e5e32dc37411d2f60119bcad0be0388ca1449cb2c742fa66eed63492b3527836991a3463095912edfda91cd20cdc2cab05dadf13133e04bb7ddbd80d11a1ecd5713f8dbc380557ca95a9b138a4e651baa97e9b6be864fc1350c5be3936faa1ee2397027d2271130fa4d76397bf2f25ee6256d65d2a504daaa10b96c89374e7b592cccc8372347cfac776055ad566f3c2e4599455c11c221c8a088a32bd661d08731f605cd32fa6b63ab266dc30190e1e9c7a4a8a0aec7ccf0af18d499969f46b696764f66297df1e34330a6bdeb1b12db9d43604e5aef1c48e286575a3eb1c2f4d2a499953c3466f63f965e8a571084ace83da3d4f1a051a589dbbb1ef6504a9b873c55da4dafd671d0831498b2c5a31b0f936bd023a28ea32ee9b1b1753972fedd22714278ca1da814556388314b9fda24c00309ff04a1c8ed394f761fa26bc952f4a8edb60431a78358ce3efa5878027831b353207ba4d89bb1371166afb89271629fbaf9724e6da66c511603c8cd0661dc181253b9a6ccfa8ba84038ed2772533a8f97f9a083fe4065aa89a45ca4e328e26279e40cdbe8a4dba154efd440d85e4c50fae71d76f21dde8c43394fd872564bb239dbc36affc63761f7e4bc385f2899f4eee3682f3a3bca55f09eb71b36f6d56884259553aefbb04782171b008618e4c5de651363ddd49f14d5462a346683753e6930676e5e5ca2d5e51db9ec7641fa2fa23b4c349ec135b032c66c2a6d6eb643a9d5b7e5777456edef5a8974017565d15f4fe7cbdb71627d9f5fd232a2ed4e06c9be76990433cc54061e9b9e696d22468a9c52bf823f7f1a2a7174cfd40eff74f59dab563573c25c5f92747c994cfd7d9f17a78d2bccb5d6d0f6097f56d37e281df1c0ed7b88fddbfb76d06fbd8d8026385c10f2b9c51ae1f743083d10a15e243f56f0f194f3407ed153676b5a4c38e37b89e7f172d7a8fc60305da89c38e574b449420a678ff46a1b3f55e32de07b2e5ed88d3979ccddcb40a43e3a53ddf78d0ad3409628d5606d0960abb1f21327c5643905dfd0b578f25ad5398de60f5877c7254499bb6d88db5f236796711ab9447b60b0a1175afe3a8a1f3666ee4e95fdfc217bac52fca9848ff8861b1ecc03d85089846dee3ad0a574e7cad0a488365a4a3ee84edb4854fc62e044e1d557dcf7ce82353b18bd460042841211b0e6f315112b14c21ff6a73f37e40c82f90db53b73e171e3e2b4b5597db3b1dea91cb45c85cd5f84e164ffdbb0f382458128d3dea6257dbe6d24f33f5234921f18c4f147c245402606174e91a43bc176e78cc7f4684db5f5b2959c38cad96911d9a20f707cf5d7715ebdcba6437361bed9459e2bd39571c64f01079bd2665e2b9fd7c"}, + {"0000000000000000000000000000000000000000000000000000000000000e31", "006d8c256c00279f3cc498c68fdeb502fe865c8e461b9bd5ab7b11684d0edba36a232cd2652f4ecc685d07caa0bfc2063388f03e01b8fe28a9d627103bd55120945da4581994638d86a36acd66983d11e07643e710f9ec425818fc8b830ba4f038f1d4daafffd88f9a38e35d7410d4081aa0b7c5b875e4c27de9560f5ab913bfc7008cb217d3bff3e2ea72e97f89d3fc7b53ec16456d44fb49b0b8f42b7411fa5b29b697987be36f00b1024beaa307734a03d69faaffa8fe07cbb387a61caf56c90b8b28e305a3685810c4c9465554d6a4df273676465872bb31fb98136e5fd267a56dd22cb43a4f514443eb745b59f5f556983bd5fbd6375ab1d7660a8728a4cd102027978214814f275842b7cabee66a0b407d6d7d57b528cb960647c6c5042611879c33580bb5e14ce4d827cbcb90a25d2d23a050e507faffe90c3da8a5dcd01f0738b9566a367f5fae0260ff2a5f036f68e11e862a587c7bb1acd7ea4fde9c7ab7b879230daa6a9c8f5441efedd61c94bf37fa6650fed17608da757299b453b1fed5e657e87a2a4dee939affe31d8916a8908c4a3d0862c36b7d2eaa8d12828e0c2603e20546a1cd5785d4d2b4ca01c4ee0559c5d9f9db07723438f96fad27e9e32af19575d9b30b4f7a4ff62442f55707cc027dc557c39355c32dd249cb58b6813359b5b4b7757a7dacc135df994aca6dc3bed53b3f07b00095248ca13b7caae476e7b5a5e9a7b31c6c2c0c918de841a7ab2797357668b653ffbe66edffe22509e2b5e47cf05f9dbefbb11bb4dcda72a8429d1edd2b3b9193e5ce148ac35ea4767761dd3e1848d4cb120ca7dc1729118e6597dbb0e13f3aba04e29b682c7830e3ceb4b9b0bf15c23be55131d74d3316083e260d1ee7697d964cc7d6e658a3307ead2172e92b5b73fa2f903aa2940c174ffdfa857e6253bbb1d7d3b1f6160155aef4d68fded5d62500e9a8441d8f0637dc852b06b1144bdb0e0351b2c8a0fd27e0bb09868b593d0c2a64a2ee174fa31bdec4840b19a78196c38719f26b35469a66fee291e56ce435ef3eb802532954bbd12d0a45cb4eecddc27b9e9a665044b5a6c2e16b1d4d15638714e2dad9c91b7865d6c63447e59a91715ebcb10df0c3db4907cab2f00b563980ee3e799d059e0c36360ae2dfc3b21f2fdc1a48afb2dee1be76293f6a8d0383bb2ccbdc2dcfc632819315ffaadd8bc7bc17a959eef7b8df1c8e35f259e9ad445cb4af93005fc62b1b20cc126d09a8772c9d3480893aecd15a51f735193e5d7bb8b2188ea9007a640866b04cc1134f3c4d830d8ca42557a533fdd88865b0b5b18a121d49172c0f16276feff395e40fcc4d5642c3c3c2adf535ddfb1c198e261e3d49f95d103652d036bb5477043a5e5238212ec3a439a0a7b55b6797fce64c28cb99763e2eed022c03b80f1336a9f43fa29bc2aacdd2ef609c57dd0cff1aafa693724dacf7729773e7b4f977a759c9340f0b4203bf181a2f960fb370791cc75235b5d6620518c619d80128d251807978e85c6a6b9bcce3ff92e50b1f9f81eb597c2fbd3dc0ea61627864f406d746c31035b25f3176e519f753b1f50753deba7aca18a0721a4e450b2c1738392b5dcb41cc794c36e52ede78d637fc1286221c7801714963c9817142d2bbfc17ed2e0252954fb5d94ed0d7c091422a2a3656c97dfc9ba328ca4b896c22cafdf0c802deb66cbd9ed4863832130a6b5a82795146b9225731a2f4c01f66294479a750312886714e268fb1c050d9037a5583da73b91e4dde04e293177811bf0791161073ac8a4cc9e25edcd9bb236a73e57a240dcf52dd4375a274ec3d5c973fcc0309dda567c40e5189437455fa01efc92ee37a1c4f8b333bbe5a5c6c7d09957766b82cb97e1ac47e3e87e6"}, + {"0000000000000000000000000000000000000000000000000000000000000c47", "0021917b5b348417f8f0c5f8727c61df2d901f9e6909a596f6a513df214dc2137a6b5bf18146494aded00c0db6cf0ee545b973f5116f5a0c895e4c96f543f5117ecffd181b22c77a441b6b8875a96b05d4fd75050088a9de23e2aead83f7c5514650e02a6c2ed9ce650e7ae0be1c6982316b3d428bb5bb07aec8b9b6868e0538499d39f9f0c9e24c77f69ccb7b2eeeb87a80305f76174065654fabdf1c0960d65341bee2db3980d8023f091b772c6491b4998382393ca2050ac2196b7118556b653195f31ae596d4edf54e37656c8ab7d2c2153b3cba53056eb0631e12251f6a7d692ceb95587c35374b45f3206a71f90104e60b5258f304679ba8c30306992023d9bc9d4769e1f2daeaf89a920a157f941bfda122f5112e111be952960d97068cd68638ebf70b7ac87ad545c1efecef33f1d8e50f25f214563c7710447f10f7560989a81c232385da0e7d629ed066690192ef63ea4c2f70e9b2e22b3c570cc9b8023048ee585034983398a9fd0545e58ee0380d49b0f11d15cf1560351c6fc62359ace3c2ed98e58eb1f4dd3bcda120d5611c9f22be77ff1cc64adaf28d9e2443b1e5e7077515bd7de8cff7984482063cd01733cc3c3f97b9118c12bfbe52b582c81683db422327b61e993ab8b516edc9ef0d107d87bbb2abf750e13ef755babb6e973db2bd827f5de2957e48ac41e37b7f1f203ffe6f930cd15acd8606e327082a0269532faa88ba5b1733fc241451bd1e493ad0cc94949ab7c5e35d351419a75226627e866753b1f2be4ae3a8c9c617c6486b76335742f7077070ae355da536d6dcfb4b05ea08447d16c1103d8650a387ee4147ef2166f59f98125558f919c82be6997a5e3a2129f859b9d851f71392d853bf87dc27782c1b33d46a78f46cf87ca059df8a3c7a985a0a41215f89bd198b59dbf61490b75146ff9e69ddd3250159af8380c77159e86e6395ace8bae900f78f5a222ab8454d5c3efba7ff28c4ee576530956d905aa8df1492b6a0f952eb14f2d2e2777d5c0e55f3339c13b429db7a54e670447fc2b214feec29d025afab343cda023fd910922fe1c9eb8960a820773c40c0fac621be4f0ead40d71ee25bd9d928ad017d025a43785ebf42041f0716b904d0fdf3e4a7e4f9f7a552020bfc39c606db02d3d493bd9ee47216b77bb992e204d9d4968902e550a4b29b5f1783737113f8f6ab9d3aceee34ac1d45770a80c959070142ab4ffb6391ff6e06ff80c80410f9374a0b75d9d7e5538520ea7de3042b9c2b4b25d3fd881a22044f7fff8389cc5a78edf402afcff3059fd548cc854c7ffccf60f5adf61e74b15ecfbf09244cb50e51967e57a3a9226039ec6365429538db7b2bafdba6c72f0e23c926f6b7713970cb8d9e1fdb053b3ab6898cb49379a5000701cbc0862aa566987b82056a5784b314628d405a8121d1e2ff8e05c237554c08bd44a0280f282dc396d4f4822e2f95499dff65001008b73a8bc6a3ed766301687f9d3e0e16355256e93e654ea9c12b1f91f5e247cb745815424eff1f19e80e8c141edf04bfea6f4f120ca0d04e9525292bdb661ade23941a30f9c1b17a8427c134831aeeb1b7fc9014b6037659ddc8d33798014f386ea0e511b39237f81c6cbd8912d65065927f06518bb7243e8675ffa748087a1be1a1441916814bc10b1944e259a7a1ede04e174d49265a704557ab9e463378c21f3244303735610ac4c6b025633fcfc5b78776a93d599fdeebff7a02446a93e490a71301e639250f80e947d1fc6311f949105d7cb6ccde51ddaba73c9d8cf9e6cb54edbc4f141694d7bb751e375d6d8ca2f31c590b9dcb5534ce93196499e7186ffb37d3b521bf6bae2cbe459336ae7c3d00c37069d56defe24a8688c4febb1df06937210b"}, + {"00000000000000000000000000000000000000000000000000000000000017dc", "01c5c84ab10af5c0d4cf93aad46a272d4cf9baf4fa3fe9fffa00a1dc65393fb57e24b76e3b3c803ec87507652c12c69c86e7d090b2f092ce9a6e172b5554e22b127e61855b20955043c4094b6592de8e149e938f0e173d4f4356c16b2ec549125f5dcf1ecbd8d77cb619c31ad40550733764e702726022cc9650107fa22a23626ec28d131d331c5bf2cf1d42807d165ecf340432b5fd68728f6f68a0ca8559aef5f8eec3df36f2ff02e2e4a8f5dc4a0bfc19522b6be8b0191a27fcc0770e91eef72298e7c9963e943d78ec400f64c5bd28510549f7410f519f1acd41c145aedd050587dff0e8094615ca565811a87554ced71250c97646f4cc792b4903dd4ca004574d6518cb6062ff9ebfe4fbe34e1cc008c7a37f1149c5dd2b2a0b04be6d1d92c68c3be6ad054d674cbfa946b3fcf16064ed83be454ccdfeb3aa08786546bfcdaf953cd3c562d34d42819ce0522da101d1bb511151b41dd4f852833d62df95442d0d098901d411567c67e661c30ec1c2ffb691aec5459f5bd51a01d5363795cc2ce54da7eb8153536362981f7ddb1e9b0e8b38115c71fe24836cb455cbba868dd62ec60dde2b4d40453c14b089948ac6db761229543864521f74a45a305e16997d2fe30b66348d6e2e3191f0bf4d54d7d5f6e8dfd1cb89a51613dd1c629f92ff74c05607ccafc5272713eae316668ac4933a94f5daac360249b8a67b5765dd3592b40b2abc71cdea6bd6428f1336efbe68df643be248227a2122842cd7223c129f0c3d228317918293364fc25490a7b198c2f8d59fae1cb6cd556208011e573682e06a40f0b19625f13728047d44bd7c900c6d3fe5c2fb0dab4439c777f76bd03bdc9c66b3956e2d7c70f50621ec5fc604e0f0a3490ef90707344b298c717441da8e75bdd1b770f16f1d25e823d310d6a29136ada618914c55eee172fda2670215d120e66185c98d5a70556871c71b88629cc6976c430f95a2dda8156793b7bdf0f37b6ee3021913940956dd3b2e33b95bfaa595216e5a835a32dd1cbd8423356695afd8b97bdb3ef910e77a636a6d0a14260a075d711fe588410bd371949cf443629692ab1e7b6007d384f00848b2cea7dc8558087a4f55746ffdd3ed0927363ce152ce3d4da8eb63a87a8ce77ac05f3b290c0d3ceb0e1f7abfc76185a9c9f8434f2b539ab6cd041e30c6ad49739bd3c4c0b8895712588b465b3b560d83aed8b15017ff9ec21364fbe186e5013ca9ea1311cd9bd24278cbadf0ed52321850bc6f28141fdca611e8955efedc336119d077ee91e14a6ade06fb80cf07349cd3b29dd37f7446a28370af8806ed5919cd696d476cfb44df14e3175c271a1fe061b6b7e4173aa80c936c907657b9b8e9cf83b4d4695b9daaec793a2f0e2f1655f3d34b92c801622ca4e10fc98f7734509a035b1db13d1cbb0dde95157f22d363da48635fd83023ee41ce8f5ea9fd76581440ae47bf26b596b7bd8c2a7235a70e68c8bbe0b563ffbce4340303715a00743fc03cf7931608136187c465b2c99ed91eac19d4a10c094b02446a44755f4b725390caf06a414758755422c6efcea061d48def2f82354d7f5c86d46ff7b04c0f60d606adc936f54fd12219b76341771ad7ffd6512cb352a410d75728d67d3451a9be47494c28f893e80625ecb61329bf3367d6912d0962ce8dfbdd1b34d9099a712725281129805874ed8cf65d359d67f0938412ce25a3aa690d17bc5e7387acab83468fa3b8a837363b3db37cebaf497eaba51c37ab2e4d49691005050e2e073eb5516c0cffe2b13ec219970d1accba359c5056263b4454fd4b0450d77f74c9e055e8623713cf19aa9fea1f1e34ad941832ed83e5e794de6515c52939a42351b41c5abd2a766645cdd5698e5a5b3887fe"}, + {"0000000000000000000000000000000000000000000000000000000000000e11", "00859c124c04432a2e12137d1fb642530d1b988ccf03ee25bf366b1d378b48d5a17c37086e50ebde84ff1d48becbdc7a4ae7fc3132682f7eb6adf3c61ff1e82642fc604f904ff19e5e04b3ffb0e666d884dea7851fa19b9e67ce8c03ae82e2b384327fbac2eadf5a764b3144bedae43b532f763788696fe98616a2d225751fc76d3bc6ecb1f1afa5f5a1063345c98a6dfea53636f11b32d32284f7757bf572fa3cd072c69c99dbc40334796516a1ff1f3d9d925beac68921f102390b7f05becb020ee27c19762265ca79537c1197a3d0efe41178a62b8f1cf393a8cd82c346e110a74c39bdf53115e6edb91259428ff3b748824b6bdcce8c6cd7bd31056c54cc0e67430debc7f2b803a3aea1c9c05dca070663932568df1dfbf34750d40edccf3726093fb8c71a25a6292e4ec14be81da394166b10cd59398c984521443ec6db4a4c34f745269b22e66626ec997ad996016b55ca328f90ef878445593c6ecf45aac43786e40214942c1fa3d2bf71d400447247138cf4cbfbfa3d0cafe1d9f9f389b9b1ade141ebbb003e1741924e094f50938b181c570b37bfb4fb10d6387f1e499d14d00b2acedd4f0ccda172c3d38def417df13a3f0ba86647f0654c5512085fd1cf065e16dfebeeab60b9ae3a36d13fdb9b91ec5ca70f36b686be6752bef01e5d1b4018e2f3eb187cf4db2b44b03364acf9756d39585d020b34c7e6c650d6898e25b99adf637a569a761b9407d4d1d14b4e3da155a083bbec6828ef23a6fa103b0684b0663974afc7ea2614cbbcaca8bdbcbad3291423253f932f50fe72913d8316835081b979a6ded7e50ad910d55746b1d651b8255f5cbf667aae88be03161261843030a69045cd83b19b357766bdaab7777bd41a66120008e627ad5797127ee8553bca9b9995684629bbff3cfcd09efecc00c422db6b2a0d17b9f0755f019747cd8a13b59fd8c6e747dbdc0e2aca8d593f2e7b21d71bb4e3a1e7f878a94e15eafd875d8c3ea86b1611b9941bc5b4691d1a73873ddc9a3a0fc7da86851d74a29b3cc9307785515ace25da0c4f8c967f42b50b394960b6065a96795eb3fc56d5eb26234b5b4a6b0d195a0e831051e766bab467e03f8b1de75a50ddf80b5a7c69af5222a3a8c6e14f51bb7ad0cbbd3a24e11aed0b5b31962340ee5223ec8dfef03de7799e48680b4b67141edcb50f792f121802706c4129f20a85333236b39a39172d4557e3832b1ff7c18d8b5b30193e0f22d919511d1ad14057545f40a85c69bc1a958f5c43897d3128740b8bc2dba45db833d963c1889edda00f587cb0e79a7ae932f27231e111e8e148ae1307d243c3e3d52b1ee4a12b0d0489bf67794aefd11beae313693407adf768bdc8c4e14b3c29260a8d8135e7ae47a07fc65692df03e881658fe8d82fbddc71dc56de029f2a4926e56bf1f437d2f5e9dc6bd595c29d59fb18542d8b551560fbabf761e2387c2540ddf758a57a255fe982b75b29cdf8af190d7f4973bad256d85c2562f3d57b6dde54335d2106489bf40a3e0685f9a5ef118ce1e52c4c65e3abb6c6bba9cfacb68a67384cbe4fc454a9249e01c925e8b52f3237159a9ff0565871148c18f856ea3149cfca973a5842c319dc5d15f32a28751f7301d25f4ad73696d23a66c1b2ae557edea503c772e5dc4b5d0f38eb418b6d62225b00f51f1c8c117183e59bc983d3e047743b33c2b31da890bf8f9a21d6ee8eae63c659479e4da567f62b0376af1d18a13168c33f084fd70d28e777f2765b386abc4e9a567506df06136289109ed31c6260463777f09ee631db57129bb98c77a7233de14a434513e1b4518cdb3b0bbb1ca8e1d89a56054cd4b19253eb2e0dfeac3736c25034cd5d0c1f9ce231e48f673d3a67516e9ff8777aff"}, + {"0000000000000000000000000000000000000000000000000000000000000f4d", "02f52fcfa927c06b675281e0745ec605ff65bac02d48b6333b389c4499fba4b964c2d192dac9199c97db08bc6329f9c677ff93cfd45471ecc7559a233e870515db577fc485e086757f27e488e301bb8dce7dedec1b1b271f38501e7199f07465253cf88e04693cd544658f2b6b9a9d23d9439c887b37588162304418f8e62c15427bd157926395b9b44ef6ac9479ed611a39a432cea370cb67a48d5b91a4b84a3eb72a7a9f763818032036f90c15a5176785b3e0766e87d628a13af347255bc6df13653bf1f3066544e6d2d42e38b7552ca21de69c90d1e744edaee4f54aa8e060dd988911cbfe25e87ff3f93169fbc08c93db69a2f866a6f539f6160598cffcc0898aaf9390614b66ec84bcdc124d983c1829cf2b85d076692627b32369d08219cbccbddcfc06918efb7884923257e813648ca037d4d98d9c01d32283c1f83a15721d5f327872b14f5e3b7d277e0c5d04baebafe5bc0013f2cf9b1060f3e4dad49abce9ae0ca504ca4c29588fb18482c8014746466f0e567736055de3d8d5f84955f303a439c0db41d984371b1fd03f38edb621e02cd77f5e2532112d22d165fd9000150c81b1061845c7ece48c53a5cd434556f3281a4f402360574767f30c7bc469d337083d7d231be91fc03f1029c5c1f420f6837ba4d44ea242494e0ff31e1d6422743f743ce6546bd13184082951dd128acb5e8328164faab88fa25c8daeb7834eb1bb0932f8cb5cc87318a0fa1b4ad48030cac721efa149f57c8a9c7f0bbb216e42fc12a601a748c7c7302b60130e84bebe3832395813a073db85855f4e6564f04a603db05633a1e228389b4cb70d9a19f283341f52f3aae60e7b19b89c65a35b67c965437b474e27ff09df90bed1011ec93c38be1aac8eac763bfed8a3b8a2504c63261b3d06617c1d876c822646094db7ec204d736f537a085f8f1d0335a3a38cbb81e9f4fb649d003ca632a9717693ed2e6dd2ead4ccd519af61b32395b9be5eb970b60b06042d6323cacb9051b1ecf1ec44c7f06883d8335e780465ba98434fc1689b14a5304cf4a842319e77bbb3086f7868254bfb97fca2017f70e2f58e717b7f7ae30b6edbcbe2465311d29c70c5b128910db84e4f8ab72593ec770d1a71e19558a4c1b8ae66e70d321c134128174b44b2e1d589c0bb261843fe449e56ba5485d905fb8114e94704ee844660fecb48c8707639454b481773ae262bdb4e0de869b52899d94f91ac573a7c5e09769326d076e55dcb158219937527ec8be833ddda0e0986fdf8d0c04b16c4bb07bc62c8ef950c5f49e80ca8dde29955c4fb4375c4620ad59c8b5698bad2162b05331a4164f0c75e58933c489e81ddc3edae30cf0ef77083f44b73193f0af262d3fdb289388d5606cc2752bdadbc8e2c871cb5e28109ab6ab1a8077fc0fa0387c865a8019245a5dae043357af2621d13d629d215c20ff256ef2927c6a416585abf0439f1097a07c60f7856a037b4661bb8bb32d100bcac4ae09b59a9cd26c722181e35cd03ec6c42a9bca13ac6b928fc1f0003c69c679eb72ebbef6ea38a0cd374c9f86e7fca5d257e1cfc87a191354dd7e2bf182d946e1a31d4790a0b2ec571546962b7bef486af817f2e2e1c2e714f5716bf16187ce0cd0d87cf69aacc736aaee3679fdf5f08f846564554b6ddc289e15e46d8927ce6b14a85bb3a9cebf5e3dae6ab21d53506656b7ad615381f0a631b22ae38cceca34bdbf923af3b5682ed1c3612497523f4edb03810e4ad62c8952607dbdd598e085d612117e012eb0e4f6f015b60c37f64775e0729a63fdb4833aba45696d972152ae0c63427c380c1b1ad9d022a26ed7ce229e11837e061728e8a688700c3c90ed1322c4f2e0bb0daee7bad3f9412b8579ec55cf92c9c57"}, + {"0000000000000000000000000000000000000000000000000000000000002e79", "005595341bcf430b063f04a89e2da14549cf7cfa2d08057f96be9f895377aac475324bdcf2758338685d0245e3f8e20dd0eb0a51bb77e076fe3396fbdcd4544c3284827874693dcca4398c0b5fc1d789ed3ec78603462d14118525dd858c98404e4a57fad2fd1cdc1a4b48b2c1b7670e3560e6c7bcbf713fe6c258d761cb810cedbaa6347151a614db6ee860a44734f219bc3391ac7e672c285d9f5ab2ba3ebbd2e6a33a1efb63910261346fa257a035bc7b10e53244c6006f8d64a86082856e77c2615009776f392ef2fed316b3cc3fe753427d64285ad8026efdec742f63652e6d234c0ddb104549a560cfd31b77eca548d067c6bc463c6614c4e50a837271c0457505a83f40b29533666e8a65d83d4d0f846dfcebc406cd321d9235f6593f6093ea0e72762193e2778316057b1aa216035a35b71e62059ffcab46d7b700652377f54556e475b04d5d82718e99e6cb005e4e2c174acccea07f13bd5ec85faf2e0ed9cbde0a52d1be3c311afdb2c362f3d077b2e5c1a5ae9fbc087abb46736063dbc47b838f65422f6da02d7ced1812269b01a989d13f9c1c52f03ff8f5b5935ecd301213158eb324b38b77c9ffd855d5cd2bd637577b455f3b0339daa06192e3f827194f6b4b91965932dfddbc19b2ad69684f52abd021043b99e733dd920eb13aea21c554a258ccf9dde5cee30dde60f04dd76591310d02540113f5d51707f753902caa81c2bd09c2167e390fefababf08da258c68c01fffb969eada58f543f150c50110128cd2bc3d077c2c82751df463b1a3d46d73ba11cbaf7a05addfa0e94ae9e6c4949561352878f0b420a331497d495698c727fa1a00b9e7b3535dfd013fad8f917cc41e3e90ca4b975d19de75b6ebe49871f9ceca6978a7b5d5649c33b22e20b4d5bd452db672d6aaa6607cc6d6f1ff444f545eb1739c3f4bbb81400662767493d0deffb79e73719e3471a45c45cb2a913d95e5f786a582d53bb25538fad37615d2fd27ba20903ab93c00bf93d4ac141dfbd41306166697be3c7302e87dfb48fcb22cb621426817343013eb0f017a40690ab01aac99b21d023b430b2787a0d0fb4d8f1444b2c0b1f149b74c3976d99a34e7b0d1eb44dfd12541d704c31564c0eeb039742801e561ae5acc4f1cd4d65b0c50d051fcc135868e67ecf6b9465d6b1d09e9f00d52c0d8c9c83e52bd4431b4ed15c32e5cb99a4871353277c5a1e8c514bad7319c24a1065a136edaa631008898ae76816314dd8916321f3975f12145b4227189cc51a9c8d9b90ce00f914a7495832a5b4bd68b304061ec5bf8baa679f46441bd026fb35237852a15a131c6ad34a85d06e3add33d212f656213cce92a46a1d436ab5e951e483d7e1257ea26240a679267b6a2022cc7ef97d91f4af27fdf25d607ad4c981fdf8396102f015327d8f70d72ab5c65463708446d4b97afdca0a51f5eb5d11e86b24faf9b30e67cc9ed25e1b09c62187a4952651e17d42d9f271e116ddfce3cd7ee96830b2fb2fda240539517654b91efc51a749aadb494d041a4943869ff2bd62cf050aebc19fb97c0f10b8160df272c0882f117fc46f24e1d0bb703190b0cccf021dc7bb9daba8ae1f7169b3136f310ac21fd77dd00f2101151ef45c1fb95c1582156529837db7017fbdd5057fd48075f0feb38e03916d1726d5eca7624e0fb01ebd33bd366162eda81d463c6c64dd6aafaafbb17e0bc703245da65c8ff2e1c3546bf320aaf132fb1c332c33e1d8c8530338b4a5f5ff6d521c4a530cf841a40b105c50f44d7eadc2c8e9131c4f1186d1a5ffc9ea146d910ebb330151e39eb257f5d1d20f25e4bba0a943b15521a736578ff65325b7003fffa59db92df7e3557be553e15c3bbdd6f1e6faa8528b0e747db7edb1"}, + {"0000000000000000000000000000000000000000000000000000000000000c01", "01a32280aaceb3c104bad12d054a9ef6d556db1a460f31c8f4889ae80f7e44a2f8c245900ce3f49361d3028bf4a7e2ce114d4e2f32e67e29e3a640619f1ca108187e53a1ec9c21c53d714ceacec07c802e166e290912ccab20974042cb6c90e1a33cc6267a55bf5521157a9eedf94a7cb3f33c036ad279f709071b95d4790a9fb393d192b6a127f4b18ea9eae43106ef8e1acf25ad6a20635ce4fd414e430af7f9fc9687f2179bad06e65f4bcc48803baf1862877dfffecd26f7af48f00d95a8acb8108e599f77a200f5eacc6a62f9b857f71e5489ce3c0d9e62ffd4124542ef505322bb7b9db76fc39d907aa4eb05d3ab388519ca64875c971b982b1206e160128e9f477fab3289302e0948f6ac72e8923a05e6f92dd432f5240cc3d327f056826b549d70cb301d2dbae70f9cfcb2d60393875a97e0fb00375d0732a76a7a8a19c161a1a4d33f2aa12cdd3ff9b44d0c0816c88646a2e7e9960500b16611b83d040bb2674b1afde4d22c16e9b384f215a4c165abe6b8f8fa4d451162b5abdcefac2da13db554e2cd31d2a68f1a157041d78fa35e694cbddf82b831f56dea9b5b9adea2980d19598a7eeaa77f732bb0e08fd7245ec39e5f4405194bdd06c6ade8c1c54f228566de9f3a1127d8a1121fca3a84a409313b89f934d723314bf9a758b12b9729566d429f3a3679d590da4b9079454eb434371b6508382ad1724f1a4baa0da44205a33f1dc094db80d525fee67d35d7797fb9b7c36a0c6c727db62bd76c710e731720205924a16edd5abd63e76726d42ffddc8233a22204220f5eddf7335593307b8eca011113b5b520eea7899dde8869950833b1ab24e2ad62f196bb2538a30388008f15f1da239495adf72882dfc157d4cf2bc4ce6df756c77fd950e35111c8e925eda85caf2d4b61852c6221087b17e9f5b09bfd198d73571e305d0239558f738e398dad28b05c3daea0fd64d991b979138e0de6bc2618a131193b1e505ad89eef85daecd929508540d4580322c43fb6de6b37b125e9e3b93f81587b55b9bb9f955972ac6b4589fdcf670fcb39a46e1e64b71e25f80cfbd89843fe89ff0c0646601480615c95e5a522ac04d9d218c746f5706c9a44fc3867382076fd0a7c606345425a026aae1a1e1cb24fface14295f4f1f025f0b31daa355b2463ffaceee2d9f126b0918d589c71366094dc1d0e931d65340ec6b28d2a7374e9b2aa156073f8217b46a00e79ca329045dc19e18ff0924fa228fa3e1eeb8129ed6256a71471834de21f00fc0e18e3009fab05497817fd93b2c1b3eba13120eda2a09a9af9963a4fb0a0f76327f5394deadbb12a43ea3f790042efb9261aa4747ca25901c92f6a916b34e309286af5def78f48ab16185697ee85f5aa01830f3710e90d2f166a866acdf3e571e1a623cdd5e031a94c238e74c4d3d43359ab7f280ee5e18f9467504eab87687cd92dd8567d16a283824da30c3f61ea306de08d56a43c542406316d206c86ce6399853fe8f1b2b0a3f6d57c691df75f2bef75c4c01d132138eb913fa6f6a5772d2a3fcbea696c454a01a92b097480b18a99d2bc7cec94f693ad3137eff9875615d93f6891658111a45ddcddd5675268e70d236024164b358b93569bba911d51782e728963fc6b6713302587b2cd00391b57bd1493c94de10c2a14afb1bb54442ec29f20cda25dc672195952fd6e435fcdcbfff68ab1daa8f12fc70c47f9173bb22b5522f93c6408deb8cd6d7c03aae26d783f1eab9f44626a124734f2db39677b752095edc43e8948c76adcc0435ebde890abb073f99a016760a7c9da78ddf8b5b9b7d16e7015bafe51fcd410f64a9d79e92d1b9e1d7f1f2277c4eaea5c57a04c62c0fc2b3c8975b9fd98a33a77022d7270650b9d553"}, + {"00000000000000000000000000000000000000000000000000000000000009ae", "00021fbc1ef4b62dd0df68d1555b6c26b2403c9eb8399f6b4fae3216f3bfc2658d2d4978223033bb162d04537e3209cb6b44649da0a977f84725e5a4b5468113517b4f790a4a40f6ad519cad6c0af3ca81dfcca801fed5b7c5747565abd9a9c22659ea63cd023ecf440794eb6951c697d5cab85317d5f03c18d276ead27403f5c27643b736e1c8c1a161402a737c59c6bf782139489de2270e6b80858de52a05c3e9b56677ddb5a701b97cdb5b0e90fc841bf87a4b6be45a747134f72d170bda3d832826f3ddf463b830fa4c23118bbd14400623ec856f9895a37db2a19ccbae1e7cd8d59d3147323adae7a8184c7dd3c923c6a1a02c12a8157dec320933f69639ab1667efb2e40481ac68e65bd035dead597607940d2a555391e878b90ff8b42ab4badd1989106bfe3e7aa22c23e2eab4642b5160bdfeb05476ec55812ea594ed126993601aca63df1d62fa82b8546003a3e4f7dacb3beaaf00d6786ac6a80a0f7b1d6ce50a832979ac904acddf6125cdabf1951da70ef0a14f0aadd31c5c0c5c4d6a44262b6dde521e9c0afbba650ea84cc31f564aa59b6dc1d766cc57c8f526b386f80404ec11fe2ef38d9764b2b5fefcc5514cac74026a051ecbee3eeb7c518448306e18229b2027baa836432170bea3c149d684eaced425984818dd556bfefa8724b149cc4ce6b007999128afc9d70a031e269b337e04fe13454c19b14cf0bbc12a55f7c1dd73ddb83b460f72d2d60d0949b9a922b9d83b6a19132d827a7ba6063c3f64b1d479030b63465238e825a37b693cf249111acd04dc11a8cd3597a27c2a768a962a4cd928780d49a6342df54ee1f077b12167b5acf8a74a0ac550106e9fd7955f96dd4fd503466a9b8a19732dfa1c540d630c4a36979b3398cc76188fcf1dc67d5c354e42210297ff5fa93f3b9ce1342bcb5a66de6f7379de8e00946de0d25606c725d8b1b877bcfff5bfa2365f760a6bf4237a854c3c373ef2c581fcffaeb8193a05ee2815649efe91e677f23ad69c7adccb02afac56734728ac054601d34d76eb4643183e3d83f9db667b90f219dbee38702b309b6898b50687e732255426dfaad33a73ce4c6359df67d3f71514cfc33f2e84eaba2c8a1a9c75462021cebb337b877867e8d8fe0f487e81f32b8223381a9bc5bd2796e626376369c2be02fa40880224994b7c85ef8bb6cee191d399467d6e19996e382ed7527765ed788ded95172c09690496bd99d876e3023ebb94eb0bc00e761d1761be570b6640539418131a5549f936e66569c366c34fbff81ecb0db4df0f2a066723f5a44f65809bdb073c12e50e5297e27f715e0abb4aba5e841213c6fd56325ed7349266d8767fc40c263ca028909ac9d8abd0d9072da71f3ac83da09f336c962d929cf83daf41e8d311712de319b95f2818033a7d6d94aacab5a033303b985c91f9dbc473fd0f2106a584317c0b1dee6ca76368d862a20a2052667c15b1750f1f510e09f83924576c7696e2a2ddbd4c4733d99a940ae866098923f3e9e37d83ed113a8ff79a058f0c83e9e440e75cb2b32f13224e92a79937a26145e1146ac7d30da5d8cc9a169fdf77fac1197e57dc1a89c7f0d9227c1321a1a3e4936083d537285d587b240a2eaea4385403f780c255ce4a8f80bb53fbc9da054aa2244706cebd4a8701221791103221779b753b12581e5df519a889729bd229562d1335acc43e14a10fe698b89d879673fe0f9172c7d05b10d120f3c18e3a813d0790d9caf5172f559b8dc39211bcf810eff40f2852fbafa86141d254f1c92352267880a83eb98b4ca375813d93e67bcbd1f70054420f1a7fe55e3246149b6c05b81028875fb04aa8e77e14a318469f5547154a1738d8c91ecb68df458fca40f86da1c1911afa"}, + {"0000000000000000000000000000000000000000000000000000000000004e76", "005226809951ddf9e1ca4282bdb5d9f59c28bf846e08c50f6eda5e8399f61f02bd74ad9e91af7d3b6bae1320efe40ecb9e96d413e713f9dd3f99eedebdd3212506cd30c55869c10b8bf6fca337eeded7355ac91a0266854d3394eb8d265285bd13543166655254efeb2a089a227052d72d06a1c667f676c88dc5b41d018b13baeacc7663bc8d72d307483457946349a1fb3a5726e6ebad3a28f0d14f9596afa350bf4a9070d6317501b573292cd61365510cd3e238761b294a8439552e0c24cc92790f632ce28dc64be3fbd2b1ca63182a320244571522afd3118bb7f98a16dc2876f54b7d4f0d40c87a11e96346afeadf949fc8e6601563461c533c113239dedca86a77e94cd1b58c33f11dd8fd5e17a934087f5d7dda8ac971ec28305d5c82e68e87def19e1f73566a629855979532522f652f4d3ea3c7bc1c0028a7ae7b2ca79e91f32534142872da2b22523fce0a01baccd2015bc0135e81e35c0871ea2d621833bf12043f6ecb56e7e9195be587daf96499a394b2ddea6902e2bc8b9e4a390a6504a501a2ab9d699e15b67be22cb7eaff1b15411d2db5aa0c2cf656a736057c35b3030058354ad8b4f6df924ab96570febab65f1d8cfe0877d1f57a543acbd53c82308de061ada7326f4fdd18aa7493eedc6e35ec6467f0a167cada2241fe006c29b10c80eda5f3e5e02b96c04ef2f9f2b8fe7d5ec7025d69990a9980b2e93b5122ec1c6cc21edb72f8f320a9e634f230e55faf4b23fd4cecd6a52db2d9519b22e575197566e059c8ca32ebdb3491a2d53c3d17f231fa470680f40dbdea8144313bf9269e642cbe1b55108a21dcd04d83af339f211cf0a9fc5cfc9f92b6212004839c17d679f3323c93ce4263651d036bb1e42757923cc5e36d231fb9e38787b25acbd31a5f1eb81c640f6dacace4c12fb471f8d3afe62b8eac7437b2ba00f1a603800a7f671274e45a8b3967ba9831d5e19d07c13f59169fcd114d0e548b8abc2f661af3bc16c30299b35cea8bf79306e2113dbdd2e79167e9b4e47f04f2f2b2bac849a0fa1dc094321f02955c0e361c5e1d8d9c83fbdbef4d7c2ec3c148b23ebe7dffd8090c23e5e2326599d3adef1a34aa3dde633d439e2a7d92244d73f11d936f08f766139be4b9d2f18c8ab5d684246d15885e8c07bfaeb6d4e1de38569e3dd37dbf0304b8cbfeb312e8b1ffab711407c3f99f1d639c1e66101bfeb3f06f3e2da951f1792fe07a8488870e5b8c1bfecf0fb21360359309e805e36b0e9657ebfd60c63e82bc3802cfbd5fe547f49aa4f8fe92e5f33a2f800c8a2f6caee24e179c2bf6198842dc49cc26fe7be122938d5cbb0f51e4a780f5f710ce74daceea9fa3131a2694d4e4958a5ec266021a1e16082d2adf112ce632c8dfd92fa704eb3f2197cc25fbe08b62987f8c1e014218948009b10f5251c7da5f55a68a4334bb385808eb16277f0b8ce1aa1dc85aa7f7e35a29b491a947226654a2d467948bbb7b87ce47cd499ea5211b633c48a38fbcb1971c1ecba3b53403eae85db12537a2520e9c53fd20a4ba4b6527a3365168161148fe0fe7202f55eb0cce70d53792d245122e71d18da6df7b76c01a82f98f2d26669592b1f632eb74f88ad20797ebf32a6e24225d628325cb1735841dc1fbf781c9dd8f850b20f563a396a11d86267786e8eb68e5f0d9d6905a4eebc4e1d528a095be3226fc55c8bc1687cedd549f0b64fe8b691543f9cfb1340395ee94e56e0b5e6ff82dc1a739439695b3650b89caffea3f5f40a41a4c4d0c0e1e7c3accad8d4aa8e17a7795e7e5087599544d1abf81feee2f57d3d0f2b57b6ec79f5f737b7fe5eb13bf493505c82ae0ccfca3c60d5c37b9d7adf78ee82b38545cd5d0e189c9ce8743394ff2d1d9a8b29e82"}, + {"0000000000000000000000000000000000000000000000000000000000001282", "006aa10ba3cbbf77f082b760a2cb35f66e517928ac2a6b06f79796b637c64bd3609a20b01311647f2f0c080bff9f500882a64d6ff672ceb55607b0bd3e303b1c51f422295cf957272eb27c9fb5568e911d5726bf0333986bc2c8f7e7987aa29d76d286ff80e01e251a159acc4a286c8069c106a29343accdedcea977677e0bbcfa12172af7fb8e9130e616314b7cd3871f50110c9339906058126d8cdf94f76ae6976df03ef4ff9801b829a0849d8d4fd14e38106fdd21fe3808fe9bc92f019fa6e79764a725403437237a42c6e364ded24c27a541c64c173853a48c82e74c3f56f4ff29c8396f42e73a297ce55acd74ddf9a34e6ae02f51e43b34490e013c8d42d270352e86d1de4466526a4bb63de8b0109dd3ca104a489d0af787acc6f05e06ca2cfea72111410dfb984a697b3774b26c8ca5e269f247137f0212c85c1c74511b09b4a23350a2400a44d6192721f8083d16e4ab86ee8df4b311f95f71c7ce19f7d95666449eb56582a571a93d1dc56b61f5938a5422fcd19f2e38230864134077b75b76e03856155a35c5bd49b185fd67753bf22739c8ce3c5f27f6e69726d6ddc38e0e3621f95a6cfe1bf1332933ad4eae027adcfb8e6020a6ad918ff4f85ba94503623256a21e47c5f7d27913cdd3d9b6080a9cb977d344574ad8d2d42e9a1d582165c9e34875fcf9e94fd4835c38f3a1aee07a3f0208d171f6ab84bf5f4cd409a2fa60355acb8b9ae9650d3dda092d52e71b7f92323e842e76b0f2bb4fa60811aba676137a27f1d2ae584b34eda2de27bc3bcf0670730c2b6b65305d628a3811c355818e0d61b79d8f0b8d07b4671fa51fb1602227361de34e7bb174110b13412dfa0eb4a4f3ba6d4210a15a0df32f637c7c7d190245f5870c03dea3b1c3480b6f6ffe914c3f2fdc51b7348514149304e34886ee303fb22a9b525bfb55043b06cf978fc94922c3927b3b959f894409d33f57084446fcd9a267f724dd10e5b199b0f6795ad40cdb141ce68a237de2c3efee9656b548115a24d8bf0b3a30d0a611cf9f0cdd66ccd393e6b2e80d6e22afe90709b711d8984d26095e7774aaf23bee7df11e99b78334555e5a671fec879ed8d37f4b9f78e9da52598b8d143b7585163929f9e8b6c879867e7b139e06bcf8474bb37a8f399a4e3fe0dc2950ec540dcf30bd7ca2c905804dca81839277afbf5241c6d66789dca61026341a778d19ea958f63e0693a67217d325342d49c4a9213b1fb1e596687a177e956bf0970b0027d4c19e68c15b18fa4c36baa956d6cb76ba9f11d6649793bccce09f7d7ff1f12a11340cab294f4af257550298f03a537f6b413fe54309b97d6ac271768a61b76a55f59414bd96440e8347fd9e9fd168035fe8e62fd5d7e14ac774724622b6545172f7d2a017fd84b42aceb1df0e8062a2b071199007bdd6a1425152db11da68c15e6f70bf0225e810dcc63d77fa13849f003cf3e815ff7510edf10a3fdf6579ddb09b35c5dcecd9be0dedf1bab1ea7ea43e8132b3bba82c581a957151ac5a33f0e0c0f771e2d1a0c462998f2b17f8fb7bb570a05de123147858c2e1918f069d513354206ac0d45eef8bf487214791560c54935374b9612a193d629e5fff015a01f37977745d798bc39205efb934f5daf0f245c3e1a880dcd4310fe884e6913e5662d747c5629e6cbf428012db12ed31f4f10610b7a77a02172ec7675689942f50e1825f4c90fb5be9e68d4caf3ad9c36bc58be764b0eda09cf4bce9b41130d77ad97dbc67e44f67e43ab0e03fd332c55501f782d71b7732adbeee7627d66ef2291737f10556d19112b95b7bc3fd1628325b4457e19192b4f51d5a2c183b4639da4f7f00d57e15922883b8687724dd98f77b63f0a95676b1fa7868a3ebf6f"}, + {"0000000000000000000000000000000000000000000000000000000000001c9c", "00879bd38c50100948b4211be962721d406930f13711d008b661885c7aca1413d8d572c3263020b6acca05d5e472ca08ba8c64f83240d5d38bbd891c3534520f5b65d97b5c0dcdb78d11cac4d2020aaf9dfc3b6a07062987e5909ba10f1ea21bf9616974970132f20a14a4daf5939767a1d54f25279ad6a49ec05658752208f55b051e900a23901fb0a44fe82cb0398df88c1e13e15a8c46533c77fb9227e2e3ff0af250b157fce0085fcbc8c4aa4ac7f0ea4243c21b26d9ec2835078908733183cd203a37573ea3884f4fcc8cf422d1b70c1216a15d8db04309b21474bb31503bb18d8196aa16168f62c492f4d8e1f4b2660519d947f65c45b2ebc420f8b4927e199f5d4024d2762a64cd08b0b2adf0ba3db285247b12deed55d1452d146ed8e21c66bea0172aa92bb5748f3fdbf59a6633c7b842a64c4ffa66413d7ae562ada5884fc51a35eeaa4f6a361e801256400b315b308c0fceffda3da72c9cd5d40695c4f9ec13211cba717c995dffb4a9a36ae3a8e7cab8acf7f026109477dec9dfa131dd7741a6156d147d35566dc7dc464eb61f69e147c3ae6475fafff1425618dcbbb6ee25f113498f10f8f7a8e328dd5f4a9a72db4cb6e096486c7c5fb8d6598f1e6c990e26490ebeb61dbec5b429cbda23c5f42efbae8935235cc518c2ac849cf6ec3badaf2972606393e7cf061b5c54ae61b3050fddcf0f2f0ec3dc0a2b83fcd9280cf27a9fc2b97e7e36c321d505b187a7e42b48dc33d7aeaa0399d8bd7df18613b03e56d61a8418ffb034f89eba00b72d7519d237232f1fa844979821253ff541b6c03cba88f417f2ff19915c838d9823cd50f294e2a8cfdfce106ffc8cfd1b320dd80d183f43c334c5fa59774a2a48ae94169b28a3b33366979742d00d055a415b05e6d890b8f9f93cb7fe4572e75e9b517b69e19f7022c75a923caed5009727f82377775bd8303159edd5bbe88229b919a1233fe6a746968d88fef3c7b3055ce19a6bb8583ef501be06a5c605c1e285254694673d14c604adbc6c2320996a69f5e96a39a6a5060c7e561bc62ae01ac7e10139a07f7f9075679366d316235ae621523dd5836021ae868e4b6aee13e26ba271cc37c93ec7145ce1ad08a28d8ab00516c96c71d61f14cee3c2a15d17e39631526d933d9c82df7d9ee6d65c47fdca4a6ef391a5020c63fcf614d71b0597d0922023cc4d6215dcfbe6420fa2af9db57e95e14557efb37b422a1b687ca74616f90a32f7181a7df73a48c81850ac83bbaf1e8223441e03b79cf1918dec55a46872dd730276a7ffc7c10236ebf6c5368b0fca17631e0e79d53a8a74b9902c05bfb2629c5383dd45c306b605e828d1c447f26bd808fdb211eb09694b2b8571679df403fdab66b42b031b38d38e1448ef94c0fe22c7cce6072d93e56d71200d1a66d9be4e59e3d4fb84cac870a9e2d2e93fae2d4ddeab63c5b098b7bced06b613754b923bae5cb42611d01a9c80054733510d11ade1ceb7f20caafaceb914ca2aec3848c527f82e44bd03e45e82617f3cbc3311dbcecd25cc3b92a9f97229dcbaa730c6bf78dcdd48596a53dc17ad4142f484a214e24f06056a3ef23c1b406671b6561158f02563cbfa2c5b01cbcb7185ea220f4cdf3f2db2abf0c0a8592669efd2b194fddd2c0de729a7561e2d1108a5f39565caa4ddd2f63d629852b446ded4b1eb41c673d72ff0e887a350799f0de834bf6548e5a81a3dcb8ea3f59ce7c9cf87833dab4037d2a5ff511dee096dae968e45d3140e316c789c9d140276f5062083353e4b322d92a8955e3e34baa28f1f34969083611655b2192be423e1ce03515f5e9a683b2ec3baf1e13ca74fcc73cd77d1f7d571917819d03f54e652eb4febf98d1706682ad2436aac1378edb1"}, + {"00000000000000000000000000000000000000000000000000000000000011da", "001060d493c7ff8a69be07a1be79c3b7706f5bcd7e12af0e66c790e0df6ae795f30c36c0c667dff96716033e8e4ff0e00f7ba1a4376e85db767ae83e7dc2d53dce3c1bdbf19d3df77c872f02c768fa5ea79393d308cbbb43bfd33d75542a9a2915dee5bf83305f6dcc401d6c47d811409f0617d8c27d6fdbb742e93b394f0f5061e9ca8a392330d2152fe05ef4974566da820435d95458ad223a21cca1e66cecc4514e5765fad06d04f9f55da1f0bd73c9c457abba495a32abec3f23dd1cf38979b8483c5c808427bf01cbf167487ffc2169193cdb3eab08c179ddfdb3f027ed1412de947d7c9e1c8c82a2e7f04e39ef8103d1c95b5ee1732decc9791194344bcbce9e3de61d647429b8887d2b43ab5cea2601f30234942cf12be32370c5cbefa67982355aa215f1b74c630a2196530973987a7934494fb014b24730fcc58b17934ff19c2eb7a30b5dddfe29cc5c8cdf002fd93a46c464d893bb010d64f8560448b25ce4414941f7ad97a202d57291858bf6c75d75ce970f48250da12a866e9e619769f684192da1b9f97be9f6f35c2959b1bc382915d1b59e286abb4dcf8a803b1e8db60a63b37f7894a7db02f0f28e8a63875d8c236c6aa31e6c2d550d49b9a194efc2d7f62ab92dcda75b8d3e0f453b6726716e87e754f2e0cbc69dc905e9b0f754340e421004a215813cc7f3b19ac4490265165846610689856b73843977a1bbe09ecb08f819dd846f7ede252b2432779cee9ba2fe98819bd791628f1e1770be2a595531034f779f2df432f992f0c6c5a7e997abf42d6b2a0d1cd0a1ade03a2bc30e7e6d1be8895f58360aec8b617fc513c0c7c2e47bfc6a71828c341ef7893ad2beafa0de0931609059e880f8075715a77b1eee10871c0953ca2564ef49f5131cee05fe5ece9f71961315c79ede4e2541ac2fe45d2fc980f18ac711125900d3c27c91cc10838ede428d18e99783f0e09fc135067037fb2e8da4d8c9dd74c3d03e9f05ab0c9383f6125a0e647a547451c97ac147c91ea5e181abd23b1b3ff5c78fdf2ed621c88619a9f2d6d5da73d85e81d00a430376b9ca3b30e694a3833cb71abe9324364f6a2224fe28ead1c6e56c1382333c2fbb759218bc8bf928092fa3a99f4365c1c42489dafb79f32bccdbb00a8e8d6cc62171e78ff622cdd496743def93d2fd960f08600a3f5a91352b92da60b875c73cd46f7677714a21f67702044db873ce2af41df94cadbed7fd5b7209238963e8020da303fa3495edc4b10ea675cd5b2e5854da8511409d069ba599466facdb640dfb695593391414542ebd187f63da5501918cc1480ca4d04f4dcd361fe7f8ed94f07557abf6cc0d5e23a9df60d0aa77332f4b1b8291d10eb9e56364154bc6e103950b1e054c4e4b5296a7620f52e7b9905058c7867c4d59943e0607a7544d08aa1174f606935c4cf4f2e5c538986013da61563fce8f1de734c3d1bbc718226cd31bcb0306f7c452314beec1317cb13f7cd986d865f9fb4b1b0832f2e63ca88bb544aff217ea74a43967f93483bb0b33f7b11845cc66e45d00b496077fe06f79c668e1454047d110a5b5fdc94e08b10a473e2a952eff4193109255e97d5eac930a711640605806ea18b99c45072305ecaec48dc60b35c1335db87839a60b9a560f1f166f8c9c600baeebc91334428b5ae86953c48c560c1edaea092416a3babf9ca5dc056e38b5b1663cf546248672685b0aaec911e6a8ae4cf068fe78fefa21de2e6e77516fe06d89b964a68a4859501af2cbd947cc1ea54d2773f185c98e9f6569cc41bbc9a118153b5055132f8f6b95c90b727636156ce04a9f8d701f97ac1fe2118beaa903f3b1e5a27d7ea686e731769dfd7521a96c075cd57f9f59b4e2c3cb3ec92b48b03ef43d"}, + {"00000000000000000000000000000000000000000000000000000000000013c3", "015c36351bc419d78a4e801bd4be433a56321a42112ff22180ba762811c36be89ef3615ae63a901ee44d04e5f8a083d4142b789d4065be9da850aeb27c1d392269db9ae857dfdae612b47cf2ae06855b5d94110e05150b07d0a0d951bbfe8335986e9495ae5fd809352b3722e65b98d651e78ef336aa5fb995ff787bebb4054c7cb78d82828c448b010cdce594605c2603ac7a0ba97b049c8c8b3c7556321128f8b609b9692f77ea02392a05d991b238d16f00eeceeb8fb18586db41fb1e3dd1febbd1ef9b63ac5217501d1e84c746f06da2058b2273bf86a900f04934729977231ec7a21a084f0da77449cbd0506d175dc77b67f5ca0fae8a9f70120414b8442394642d2be9a3fb98f52541b07d7fe8190edfeedabbd2b025af4833667375ef71277a37dce50c2ab670d708da5d4624369202536cfe14d09bb3f4246d3a8e364cd0af317bf519f1ae8195e30f97504b015c72173bc1025a95987137f9aa47b130a0755d651d241c530f56230f5205737cd75cddeb8f583eecd33108b6d3f0cf3930896296b4364292ae93d2ff811470510db3f89d436b5f6fb77a41bbe82a14c5fa2d4401c930cc715e0d03649ff0e4ea7fa889c9a65eb83a03800b431350d77d15b5d16b393a1876db233bd4ca145ea1e33892eda574d03312264d64a50ee43828821ae016af4d8fce0504aeb2b54de0f5b98dd8f3f9df041879a4c7151dd5c8f0347a56de91a23f727bf0d809f57687cc0f5b1ceaf5b1166bfbd41203425190260a3182b66f041e81ec0bf2fabd3c4c25eabd7eb2aa5b69e413302a06018a88b6c489ed88726f4c9786c8050a93b8134475bfbdd2210b0453db4ef6ea1de7711d0b74843432171da92536e2a5dd0195be621825a205d348e3b9d9888bfe1751942cae0712e81ad83b85253abf3fb15a963f3d1e17e29dc6e852b7019a5d3001dda349420e26279d29902379c7664283301a9120194c9fbea7b8033bf516022afd3071ea0ae6f25d480d5c628fe6c849f182c494adabcfcba65adf7d8d3a259099ed120b0b2284d548d5bc75c90bbe80bf9f1f02794b90a66648513c2c87115842386fb8dbfde46f167291b8af96e806eef9026887fdab0244495c7fab032c9f4b8c1a83fdfc38d35a6e5bb100e9f80a0d801ce96740018ef9bed42c33a77d26c45e4fde3c799f0564ec7420c798679a3f05b9d2787251eb68bcfe4935b5ca1c0c550d50e8eb076c585de39606143ce8a1100116c5ca2ef573da02e27d2556fbd4f63a2be6631980f4a099469988820929cb8cf09fdec6d918dab914a349cc9c50a88541d7d64174e29969fbcdd8553c28e1d27e09d6471160c6e685aeed300eb6d23a16dc293a3c8a9f24527def51675bd0ff9eb26fbf9be6382ebd76ad32736481cace478201550fe6861396897204de13e8eb22cd738f0a8761ddde1ca30363dcdf9e253e84979b4df9907495571da6f9b743399a7fb86d087708be118e732f71ecd328a9f1f10ed38c79779c1abfb37aa35a9dfdfc9e2813ad7e75c63a123f4c0507def3117b973f1be73946583e345c679ff97f352338a0dd00416500bf2bd705d42e68236991d4578a651c497210d50ad423a60fb327a7c8efe95d4edbd572424c67bbd5a953c3ddf067e10545e8223162f6dc8105af05344de3090b6289a093a52f3cf523ef6e2d351a2209132796845b3ac053410740f82671bcd643370df8ca84810c262f1ecf26a5ff4c3d1a549bfb97f41fa012418a1312abaab9a2f629f7b32f2b7a9ce66b0919e58138048366e8e78308ba6fda4ba8851eab00666dd7eb06a7d2d98838572b787d0d4b04c4591709126914b1e5860f98f8d8944118b45af6df0218680c1b637100989c6395705557455be5acd6ff671ca10c"}, + {"0000000000000000000000000000000000000000000000000000000000000698", "017d3bd4ca55af7ac9daa37edf201985ab20181b1e258bf61d9d3b1c05d9f6d5b088b3d51e147770c34a0d8d9d502f8577691c2f133b3f7ff8dbf4b03fb33f19dda50227af861994d7b647ce6aa47b95161fc8220509c70a6d554adf5c4fd559d43d7e41da4c3f23fd098a99017e4b517d336e53965440bf27219fbab5f91be2cf2f6da1c0c9b30435eed869727239f353de614b3b67e51427cb175602a78572f7e90ec8ba17df230415fd04a02253b9821b12285d77bb266606d89bf51d47e392ee171e65f084461def399b8f1d849f1ec808e1e4de57064c759a97a34bbb1ddcca5b9f3f579c207e34214814d75b5c17a60f7238ac0286a5fe79101d49242963479bd15f04a584cebe98edd67dd8b3c140b7be2a77ed5a05cbd30562dc3bcbce045bf864ab3faa577b03744a73ae7966338fb7bb923f96b9158164c7ad978fedf3d176f9f7a35d7bc51762279b76b5023b58dc430d32eaa840a1991e264c30e309f6a45803c32c4baa0d6170d41b31a1562e094af0fe388f7517720b6a854c8532f4a702d8fb98d80661409aafb964feb45f57a1a1176a5f26d80cb84c8a6067f4d5440b09e323a2dba7efc3fc21056f28cee0e8a1d3aa0d1746d6979346f27174fc04a562e1f08ace471713ad15c2cf1ae105f4f735ae0202c9a20c26228e3e8d1b4f735c11a733b997f2fb8dc248756affb6cbfdcd9504a01ffc241bbd43ee70c349132df7ee1bb6929df91664247721968d478aa521c47a4c4cf09ba9922b50086b4b2388c8e665531a21b6f3ae24255dadec2a4027002421ae2df8cfe868a828ea4dbfd2801bdeb4b30d26730d540c6acdb73c4483474472460f9a379d1e0fe3c7649d8ea34ec8d8f4a58e761e2b29b1b9502a0d5ee43622624ffff661f93b8bfd2e3660d075b30a1879d7777164d731934fd23ccaed08e27335fc551f04f3d13c25075db3441b052ff1bfcc11c43d58a13408b0f9aecb2047f355c4b1363354a5c85b59845cd509bb3943780983d485f9c81d967d321ec11c5c1e4e217d7e5f0a55f8d931a51711027f1d2a25a654d0e9065313f3b95c4ab5e3eee34bf9455fb24595fbf1983cae9caba1aff0839235f4853345ae2d6eeadb18bb19f8169e540db2979f0226ad5fc664331c193e35bf21ba3f69b162356d785b83ec45f7cd95a2ba7826060526c454e84b29299319e274e6edbb0b7fc29eb86b2c0f95d8dd9c4b2bb3edc797ab40027a560113b651175c27609305e3718367746e4bb72bb1ae21bb487018f87570805c6ae9535da35ddba51ec63aa692c15b052816287e525459aad4c402173b0621935af127d740c285de635e20b96c0054569bcdbe4b50237dc05b07067acfe815efc34ae319d10774b4028792fb2b6d2611d47e741c01bd993075d06a55775579b410943407027182689826a7a65585c3486993a24014da7ec907a6dbbca2395645f8f450f65b1a8e7461e3f2ce781d22b20f74f019e3c6792323c44812295690526f0d211e215056cac2ad4a3877035dd52d2e30f7f47f6e0843ef5870865f59d4adf0e52bc3b2ae4d1a74070346552a8cd8e068c9f87318f998ee519ae823ba4c1308f2d282a952b2dcd57b293a70ef5bcf2e1f9cbfff0db733f7c06decf3f92a64dbe54c7e020e9b192d640739ef05b72fe585c9edc68cc8798385eb98fc52b92f201fe3b799d1871638f6ffec5f87c65d019a09710b2175a7e2d10df8f33c93d765748e9a05113c30e710235a7ce0281359489f51e595d69a5d811714231107fe560d869e192fc6a036f60c408c0f1a903a55933530e71456627cf94f7194c5a1d57fd18b672ed2cf2ce79f7918ce82b07e8114628da45a29314b8ac54447ec6d39b8d490412e9b658cf564b435716c7f0925"}, + {"0000000000000000000000000000000000000000000000000000000000000a64", "00fb69c9df91adb968c22a3af55b0d96a887fb2a5b15fbdf4140937ec124e604c85368e5f5a37a39ad5c13e034242c9434c6fcf757525fcc26027ec8b9f0e31d5ed4c3638cfa7fc26fc46baebf8ab70d585cc28804b4d878a28d057530e7915710d03e70ccc43e434719ae29288e476813d5ca24f2e2afa241c92dba69c80915331f81985255b07aa26d4fcd0c0d77bf0ca80210b6566de79c2f3b43a8f11bc39d1449583639a9e602d79327b00a4477bcb4724b211b0d1a6060fdcaa804bb291765394155db23843174223121adf8109c8a09037920620798bae68a37fe95fff33377a09d705a3534f7da512760e9c2cd47ff85c5b3ba6f89faa17d049e7de7300915cc62dfa28d296064ab5bb03db96422781409d2553bb4ce13a9a5e9590ae28637d9eaec0a6a5f2ab22f7441a81264f6e57a181f44255d8f66185143144edc0747f763126d521fc12dadadd98fb301c6be9bdb50f33d6172c14d66d198ee09bc35be810400b60fb5d586c5e37b787e7a795be23c055342420369948ef04560ab62e9c2242ddbe048b7a88c05b2319d02ddfaa6b6759821835b54a4feea6a8d1c69750951f41c719557bbd62215639aba77924ab43e92bf538266c552591855f1596a0cebed6aa2fbd55acd441de619e5d265f6dd6d37d3737ed189697bd44d3f521f4c2fc4ae498e3a92f203965e272516f877f8f2cd025c8bc10c57e32f503885d4cb4a7e61c9d69a9a674af91c8a7a5331c1848a972a85c327ae9312ff5cc402983215290199a74b1b669b0664324ef66cf8001506c4cf6f5fc2a74f18303309a3ad9392cd36f7355a1db7fdf8d964f037d16714876927580639169cec86539202a208f4414fa608c648177b686724a63e7bc621987ee14010533a8e9783f28ffe5fc90dfc4e130454c233e15218ed41bb7c678ed7f45d2aa586b8bda202a6c1bf92d898f9c8337677133eb48e82d035a18c275d175bede5a6937754e2c1c925d241d9bafff697042d8a22bec61ab14aab53871331fd45ea137b2a6d156d4e73104bdd7bb04082b585388f59f6c1d0809e04d43227b7d294029cf1d1bd78a20b7189829c5be7363113a69bd2700950fa2496ba665cc582ccd7c5fc12c24d33a4936b3f51b9730c41e9b118d58fccf97330722d543d0f7cf599fd5624976d07f70e5259e3d4060c3cff6894a37fbb72b675d6f187a29a87975a5231a75f8b9a521d0f0dc6a43bd2268656d47f774bdd0b4a1a693e439ff4f989130f9da600fd25c2ba2a9e135fe15c800a66e4e682d167433f967a2cd8f58f550c7996d970a7c7154cba2a173af2db529bda7beeec26f5693e6ecc4b4c9454b71eb6ce3c36b6915ea7d61463e1092ed54fb5ea75a77d0362e8a2107c9b064516f4250dbb4a9339043a333c8df11b5f0ad85b9f9b03bb12d40cd1b99cd30bd0d7f25562b0d622190d5b143476c1533e327ff520363204e79b16606059bbe40423b9d64be39c5b1fe1d53b4ccb8bfaa743bcb9201f554cdfd4228a11e3dc827fd9d66edeb380b786fa0bd396ee071727b5fdcbc228ace20e48b3c488734d34fd3c1d33d23f87b61753b63f424bee868b77a96812b7b988020cd9a31d9ee4a253c68f661f8834e53d18bb34cd3d9aeb8b154ff69aefc09b95ebf27f46a20a4f617acc0b373b567081cd969f9c515a5f5b05710b9d215cd945c3750c93b18c87b328bf25d5baa24e1fe2ba49a31ce319c84f23083975e1a24f3352ac3b21a93cd7ded995a76b3455d7a4e5cf4e532c5656c31645d170f6ad5847de78e24fe4b751a0f121f6f4e719c614ac1b923b817e28340bf179e30b140edd258821e70cf8575a48b92ca7a317459ef40106a6520d8b2c16b42b78efd94b987c941809c9ff594928f1e12d"}, + {"0000000000000000000000000000000000000000000000000000000000000872", "0056aab8fd4ff5bd63afb7452cc236e2642a9a4ed60e9f85c1ead77f18c39cc979237469a70b5f98c37a03014eae2f853d0f5e27b446245c35df95707d57211971974495cf248ad011343a8d46d3f1e543561f330dfae14093f56e97dd0d0115d4a1a520711b07b01c16e834d9c65cc581219a49d2806bfafabb0c5847a11ac3bc37df63fb1187e9c1f7d895b06c98dd06b4c02259237ab08f26c5d852f31e024f2be2330497165902f244a12f53d83912ea036ebfed33468eae15559c1786adc9291d9551137751d19745eb4e24ce31cf7b1a8279a97051e9b3523ba3a4fad9fb06086a93924a1ccadb615ef32033aaf80255cf329e18aa83cfee5904c884f39ac1e7a33d80c258f02734618fd910e1ea0cf79bbe7e948383bd5cb15663710b08b5774b5b981b01c3322a90e91fb0521577ab4ee7d57fa10d76101f1692e6aa57b1638152334eb75171e63c0354baef0174f2a1196cc0adacf3f3df6ee7da0646c1b5f9cc0deec295274f92f9d32c7863d35390c7552e5e38600eb52a6c39c5ae78fe98413ff8d0ffe1e78e553be34d23e7e961312de38c3146cbd3cabffdb42ed97761018d803b18114fbac364a033af46c74ea7ba1575530b8f74ff2fc367be54336425443089b60a2d7116c11002462b8d18266186cf477a7decf30a1a5db31a063cbc468256dacfe7195ce685ae63d55617adfaa1a80244b659a2f00969b83e9278492902762e77fd522d09086e71ad97524bd8dc91c16f5f0f271e693f82fa05a12d3b1df03c69bed6e60c3a562db60063bab95a57644b9c2ae8276ff7e6999d1471868341f4ff82600b4a9219e4856eba9ad7e1e456a5fd91a4b01d8f461089fc93e2cdd2038d92a7ce3762e2ced2afbd0da6285ecb410e9066e71fe5f32ec16d169988067ea77f5bcd27d63ae3446372d5f5e2025bd216be21fafe5f01367303a181a976b7e95082cf4df8cdd2c99f04343ce394651b229fbb2bcc05d26aee0359a03339eb6f021192d241dfd519d4c871be999e4050e3a70b9c8c25f5ab460420f4e33a6d08e6a651c1e6cffa39f0b402dbe3b4e6a5c6f7a03a367969fa51bdd8f1badd57284ace7a50a87b61fd4bd5be5acf522779453be993062007bc8856aabecefc71ee441b2c9212c516a4360fd91f67b76de2f7f1fe3254cdf556829f5a16954c06badbcaf85809a5fcdff1754a28ee68ff62fa4a82091e1b195784e15556baa3a181a4f335cabaef07bd11c3b4fef1516181f105335e921f48ed39a99a6cce5218230547970a4b0b1097d4ccc6a88b040a7f32830731cc7c4efb39c5eecd41d6fd5ad69e1c8bfcf7f06dc9ed4ec4f62835fa5ca8f669cae6db4b4ddf7dee16b24962bf605081946b33aaebeaebdd051f1b7ce148f517f0bd144131dc065771ebf0d0a23ebfb5a65b02a6bd20a94248983221d2d9fd1f6d7b2d9d3dd1ef0b0e2e02399c741306a80856f454108ec0c6f9ff2a0770aa06ae05633886172230d0fcf68d2934ed839307aed6ee5272fa1fcc24e687b5597589abc5ba464303098fe6d94aee6d1572753619e7e21dc99c9e665b1496f1ec0bd3c1fbbad8331b6e33b8f9bbaa94d63b0436438ab71a7bab8e31f91b887e15fe9d947c1ced0994bb65865251c957e8b1b0d422125a49809b161b0db0e24766957e8ba4eff11a6919fd8e615ad5fe8227660333e3285b016327e44581c5933201b2d7dd664586bfc5ac1669a3a2f0f5bc212fe6762714969803607005ee34314eb1ce3286b5bd4e5bfed7c39d418e10fca7afc75f7a5b8c7dc40991a07985934ddc17881799d5c0d48f098bb8d22182af57d3054df6b1d8ad1247168639610a11e426135557e211d67207dbb56a1be642df939cbc416d4609a463dcc6869255791e68"}, + {"00000000000000000000000000000000000000000000000000000000000005f7", "0022c5782e9019a34023b297c5bb9f7a28d53c513a12a6ea9b926c8d21755563546cb3bead5a807d85d405d59c91a9724af1ebf19070a522cf9080c71d5f4712bb3f372dc66c9fb693b1d932fa83fdfa2d143a7503f298dacf92a29cef77e287ddc6436e11ef10a3a457b373360c160a3564286940185b94a26bf9973a630b67c8f33cdc18c3a85ea46a03b7ba7298eaff26391a3bff03e98a5649c20af71a3be93b7229f85ac3710102b45afd0c8696d71182d6722d3cc111c81f779f1241198165ce5623dc18813ae5e76f087270903fd30bce9ef1090bd6c1230140fa8a3e8698c06a1da9fd3721770f9e9e62f3024973ae8479344325067b0c200d969d3f9a861f11fc46635724a36dc52ebe94e77626986d7b294dd45d2549f607276bd48583a78e23e027c7a2907557caabd144a2c15e54ebc3a2cd7d3e3f65e5df1a41399215dd1418dd37731aeeaaf75abe660169b240c1c2b59f2920c0feec2c3441457f114d6e1797ed1171d7212905e953d8c529cd054c0cbeb525200407093016450dbea2b471484cca8676ad351eed34e07c11435058a1588bb36112691c2a18be73de2f0a04cb91a50781e68a63762459e64c2592d29f1c2b3f7fee271a37517fbbf924149d54b20f03905900b60a5729e535994d3b719e2397667b14e525aeac20f3220c043edfa4149dd037a5c673c7d6db8b7dbef9f60c8e59f66ee7fe01a5ab858e89cd0645759b5fd0801ff8b58b168914af3aac7392ace41db2b7a91d67046e271eb5c0a780fd8f2107049158e71e720935952871da2fdf06ab974586c377bdb9ca2bff189fbd6a9f1958e0f0495afbd8e0af538204bf7990eed5b6f42e22160e6eb05325c8c6b16333449d2d2d31fc5121361ce85a1cb3db6fcb15dcc7447bd3f816a11b79ddb51e815d0b4753e18746a6b4db3f763ec6234df7710c0036c2bd96199700f47450184b013d3480fd54876e102559de0f94d7ecc2779381a8b55d7718d23f75be1c5129568c1d6867846a63c990bb9672f657fd0bdd54c1f6d0cce65457c8098735375459776f3ebfb2cb0775c2cc07823b7acf20143debd23502924a94a4eb553f5e2b4dd8a1e90b6ce64190d47faea9445a9db40d7b49b9673bfe95ecd731e0b68fbeea178f79d1050ea429ffbeef55338be4413f5092f1e0819693b3b6020ea7ffb16f1751b9b081aa6041f7c2856c1b4f0217843aaf1217ad81ec5ed1c600fba79083c076ebde0504bd4a8af09de9b5b95601956a9deafa0038560c09ccdcceadac9eefa2e05734b44e4e01e01df4f9d40e3fb3cfccd1ee67b5fa71bf6b751d4e55e03c7c70133bc11182e1be8f246d445913eacf8f43053e0e4c12cc7f1d6607d28b6f48d1adb4a294ba392816b9d91c72ca1346d64873547225197466dc6e259696794505f306bc6ea858d770d2e22de27cddbc93c9c50f011641027a8bce50fd451f841d66ba1aea1d23f610ee07e97fe75ff2152fd706d4c2fe374a716d4093d2be08a6efe12be4e295b7dc50b109e0a60042dfcc42121a4a39c2b3dfe0631b34051599c2e39238983db77c1dbf4cb9a45adf95869e85ed0f7afca9fbb4fda71121ab3b58219c6739639c86d586de7cc5c7dc9725e330fb73f9aee47ea980deb82732e422ffaab0fed91d087d6cdadc5a7f1d4d6a48d731d3403aa09ff6c590094c0c25300f13b2b542e4e222b8039a3cd43363aa139eecbad994ae5971e6c69d4d66b91dc29913434c3ceb44b1809f20a5448126563d5b7331c6e16eeb671a1dd99d17cfe9cdf740524be5223f25be26113cbb22b4719b1f9e1789bba762b9bdec73d8ff403c9110266a4dfe3a151c44b6a7b4e0217ed4dde52e8f672c4c998ee0d96004712c62a5b7d44f017e5cb35f720a"}, + {"00000000000000000000000000000000000000000000000000000000000012c5", "0087994a5a6f295fe6de45948f7f7a32cdc81c402c5b81a4cd74d85d12f74cf61ef8ba50d66911f9f24c0b0029042950555105a406fde0579256facc99a5a02d8e47e3346c357b90d9449e4a35d37ec0d67e93e708bc94ad1a0af1192a97f120d48919e22a473e2c3738d502ecd1a2849f76f70927f7510c3a9b51bebb470bf19c887fd53cdcedef428cf1e06141aedf3d44070fbea54c604863e6795ee53a163e0d11a08f1593ae02fa345d021a7eeef94084a2df65cc323bf4353c8d097dd257a472833fc156e4bccb57958a7d06be18a51079ffb1a29ec0234ed58763c076c0fa33a9daa5f5126bb479e310c6af435291f6106deeb66c705fb9740bb89947a38935995978fd7bfc7933ef6ebf5d3ce86d4183b25e229efbe8f3f713a3f608c5cf15d6e81f21459e917dc96ae79fd3b3b518af9f1726ff3d93d73a3f3b81319f7c0122db641191a57fe21f463fc32b00bc2e7272da77357acb225a903079923298b324110d3ab1304110e617199fd1bb03ff213970dc36f7bc1e88d95e0d27d7afd36fb49d935fe882b72a16618b211d7709f0296f87f73862bcccb01b76c1203eb0cf0739e9611097da8d728fa3003d55894d4a79d0dd391a315c5f98231759bc7e8cbcf4679e6fa2dd9e0a671612d2f6c04d4e137513c57d1f64751dfabff07c01551cfcac229ffa39dc9826c8a05b02d6434dfaa2ca02f52fb1cd943895eefcc0def0d0e1e200b57b4dc01b87ab77b25424c7f7bee3598a2e5c910e8c1bb042036faad9891655ceeb07aac87fe7e4a382d43ed1582abbc595ddb7c033f57d55bcff361387204e5f6c77043a2180dea28a21f68f9097566c45a209289f200c5c3625503c5d46bfe83ab644c73888421f9bf39727046c0f31e89c03719eb3b2bef8719d3b47bbdc80e70ddc76f20a4854b6b52f232266329489f870156d4e01ec93a418c1612a0c04755f9c52cbe171796f29640c3e21f67943fec682d4830362d03ccf01581d641911ba07ee05e7aa5fcd7aa1f79143dbb0cc9b7b8a8828b067c2f094d3e70a8fa4acd9b310574838fba8ee02c2ed32529411d537a08067640f05868a5c56893325cd6f576e70ef07c9aa2742efcf190b6fa37cdb030a07114df4da55898ec12412906de3710e2891b7df2beb254db4eea90183c7150222ce5b31ea317aed8c069fa5e11df5cf9fb1b5f14182b37f98898e700c5a0ab3950ed81b1dbf2a7dd508ffaec451b79578070a0a6a5da636865c1d4c923b7ab9e2511fc1fade96e718c7ad3dc4540cd1feee32e5d5d9ac828c8c1feebd097e7971079f13f9e91a115bc8213bd901d0ef6b284a5ac633679f9e190090568342f7802694875a3c0c15e822ddfe687a21eb19e5db195862b1aa1f1729033e976e03345bc691debe76c2336eb70ddb9c727e9902019e10aa29a21f9b77b3a327656599dfc319e5e10545e7d18249664ff47a5175a9e557194ad1bfbf2b08781c00fe07059bcf8381160617e9d9a1733c75f90d575cf8a4c5eb1b2431d4ae503629d562534e6106116d836e42d2465171ced42db4fbfee541d138771020fc4310a20b2bbdf0c643d69dffe1e144210e377614d1e638c1ea7eb9e46f44484eabaea9c87df59a0d39400dbe27b0ea5187cc76b75a3787f69fd615730d12cfd9383ee0a22b703ff4caa9c76d0162f9cc57d83c64bb0da711f70df343c500184509018f041a1be226ea3265298fece9783b739c6ef2bdbd6b28fe2b044ac2ad5fc82042afe6e58ab7b7675746e3d7fc05a72967143102ba4e69fb48b3c743a15fd982b64f51c33d91f65c8f90a40bbbd776ceb63a7aede7900fc1953139f7f69e95e6135b85a5e67fc6954b05c1de6ba14370b5a95a78da7dca03a4b9dd7f38e2a69a794a03"}, + {"00000000000000000000000000000000000000000000000000000000000004f2", "001fd54300f66545c4d2b0617a3422f06b167273ec069ee358bf07e974f29554f74cc79f7954c850241308720415ae5b6f77e4737135e890e0905fa730bed0157001ebe75992c2fd97316aa6681898acf669f1020aeaef862987f230f38bf3122635926b6bdc5bc77f4c0b7b0bbaaa6bc1a14055e9133e4e366c899b21571457bd54c5d2fe29ce32e5823d457b52907835dcf5162bdf0c27e4064d5559c292c0b2f6b95b720bc35b09abd1062ad40cfb7411e36e07b7cf5e444cdd566133a8db41959bb67148ea360d79b8ff359707166d33284db4b283cb3ba119c34360d83cef74f204de07e73427a7813e92bc55d7f493f254e29d6283035435ec0f01edfa0a18546576fc029d202807626fcc56b0b0168a8dd519c9ca8481e7420127aa3305151aecabb61af0857cc1c916d89d5f81d4772d07c9df4655b64e356653563c5b615bf22c635ffaa3bd4d4b15b19ff4021a98a42c908e070232e1ce239a4908dc73f1c100185e6f7caaa677afe86cf9a97df73142a6adbc5bda0afff6e0f2c4afe3e10dd40448fe10da7b045d6cb610b2b2f8e98c325267b39124ac141d4ac92c7fbdb909120be5fb56adc398b6b3686fbd0e1931fcada0a014713b18af710fadd2bd723ad9f9d709031d7af8550c52c89b9f981ab92e7d43040db064e4da1e771fda111e3f74f924eb5d4d4643fbeaa3527ecb04de166005a1316bbdb2040dc61e085113511a62281c31cd1e0c2ee8ad4a946b7b7d96820c9a51f315755cda1ae8131713bd40a99af7c613833941b023228d3e5d8c2f1eee58f8444a63578370377dd1518ee72e101fd45809219eac10c6eb81a23084d9c0e661adc861ba2f502142dcd057a7079174a19322ea6f628d02027fdc5422e69b0f5c4b867acf3f86c82b47f099d853101a8d3623236826ce51ecbf7c558d5535045388fafd23c8003e84ea666f8429c3e821cf4e96fdc5eb2d9745332f40465509994d64e6172bbeecf50d8ef3d93e25a017fcd0f96355725ded4be540b64e08cdeb5a352b1b2a0b6710add54875442599709069de0e9f1c5908fc1091e255e2d59cefa5d6c3d8e26c31990492d0e8f9232c95664e1cc45d5cd5e6c97e4c3f12e690da252b1b961d480a73cc63d94ec28bda1ac8b5aee3f56d3e1f2a57aeaf4a75dcd7fe35f0de5670562f25f260b9015c6701e7849d6bb9d6842bc8cedf45abb1d8b9d619b367fee7ca07da9e47a4ffc2f544aa0e157d1426047cc236d952336d491de2a732ea42c980de5a484a5da1fec14559fefad58f376ffd647c7dfdf8b64d4c0215b9d2d91bb015d9fa45e16c7497e18aec7eb81424b3663b5e496a3bf8c373becdb6032241a795fc2231865b4229ce43c5d3e2639aa7a96ef51a798961db410ffffc23e0fbd76ad23de5b0f52d7ba6695d3c4b00a5184fa115f9d949ba001b038e6098f54ede2b460b614b46750310361d59ba0d41e41a1f4c675caa3b0ae88b4d9031c9a1d03a1383b1bc3ca2dc3358236d1f4e0979b9de83594a6fc75e3b6a97731576bfc05203aacace1fe2d53f9b1780560bf8b1c4b4d9a99f351b1a6d87172cb177b6bd1599cd60976db2229d35980a4ad1cef3982a539a32c1dacce10877b2237f895f139956f9d564c557dec0f5290539099648e9da55b404a8bf6759aa010fac8371ec0b5ebe6cb7a1d1342718bb2c897a6d5267d29a12f8e4190c7d13ed9a001d15e6f745baf6165fd78907629fd3960ab62f3a3812447453af96dba4c3004a147101a6533d5446b332680647ba2c82679c897874d8a275ff7352ff377f89c015df317a8b93ef61b675f25439a4e0bdc2995e7bd20c0b1215d389f737eab2d381836299c1a139dc15730c4a87c50c9cdfdb955d178fd8dd749ade131edb64"}, + {"0000000000000000000000000000000000000000000000000000000000000091", "005d1c555b0224de887f128ef77f6038cde194cd79006442ce734e8c4aa1a016a2b4f26aea3b6e1951413587d464372fa22fad57f3c500bf03215fdfff16ec3c0776ede0d6593f84f93675d7ccbda1d7b5d4e9790f3ff58b88973c497e87e14bf0db63609464eedf90234b719a691e312fab3c53a8c271ad46155b3f8e0d14d2b74e7da5919bc35b223fe1cbe380a5b11449ff32739aa07c788afde06046fb834f373f72ab3fc3f304d587f5e38d1cdff58703971db24489a4096d46ff233359881d8ede22c8a43334e5b0213da0d13a68150790f5415092b9c36d515576747c75966fe13e98190d19954c1557c0fb3dde32bc40bcff865698bc55b308fc920cf79f8def1c565434efbf5c3fb8d91e0fc409434e5270d600e938ef82701b1724b9d48ffdd17110954554a918f7cf384ee1c2623bd6f51545777b20119eaed614e48c0d54b10209dea892f945214aafa20069c6839d4bcff128adc2ab1c4cbd16e97c9ee20e1492c446c2468b29f93bc312b03ae1a558471979290ed7d13534862d7d36c4e28059b34aab56e7fc1cf916b641c93df3afafa92e841d152c4f018197cea77b0d420ebea4c662e3d1c7c1ed9e79f7ae73493da04613280cf025d491e38bdde486a2f6f9b67c33f4ffb93e1a76064ad7f942fce0658889cb02b6f60edfc9755cfa66fda8e359a5fa169995a4e8bedae8895dcf2e07612f1d9dd5c4e9b25da4611d5358e2da02bc1cae20f2e720dd5fc72733df12738176c8063d4cdbe3430878fad0db0e2ced503151ca19acd984ca69ff0fcb0ec294912655974da8b2b55624c6ecbb03485ead530abb8a79622ba3818141d1f014f6eac4969dbff81936941bb3d02865adbd68f544e1aff8d5d0ba0f055317d0a79b6c2d0b07b6b4e1bfe17b04922368de7eb517dbf702bddc8643611bc1e14cebe0c5974d1ef68301134906538c4f9329ea934f92296e45259771d23008c4851d9c970977cdb42118baf776d9530fb4f421049d9682f40733c47b7ee1967ecf2c797574b77f4d04c601db4aa6437d95b9015290f09b5c72cd97dcec04a4a835182193fd7dcf406f67583560b0b5cee248196b64de52d3b6eb23e715f644ef090633ad5c4ba30ecfe70fd4f3b91fcb524172d6e72d9e15bd1457b032505f0016affff5ab5516c7e4480b01d51e3978dd04815ba4df13b49ee2bc62ebd57abeb954807bafde10ccdded200f25a7135c817616d8c195af2bd41ef906d96a28069156393d9093843d5ad0b980683587c51ba367e3b45d63afbc5de1cd692f6f01e7dbd5bc101e328696ebe4c5119f04632261b4a72d443ef0eb221eb7b56719151fd6c65ae1f4b41570ef13533c9a731ebd14d9c3f12c47da42e520d05be39d538bf1985e212216c258d42a2edb0d948ea62a191260d1b54331031f6fafbd220c69ed55f35d041b95c8f3175d16570c55eaee4532ad0dcbff1271472ae6ba7cdf945c8a089af3353e8b2b64f00d12d88eeff51d6582b2fa494799b4b52592bc549aa877f197e09f1ba8da1eba850c70c7d01093b99f9d7cd22e95a5b3a254ee7f93dc22f04f61e15ebbd1059e3589eeae276d735f3aa7d919af1d5b491f2a10fde34224a6e48649321fcb30f91f37ef1f640bd8f890ae563603c5937a34b17c60ab0bfb6b56b8edea5982d3027b90ebfb9b33951ea60234559b887f144db1ba55c459aa7317ff86533e19de0f12fa8cc808c694575d55764bf96893786e5bff2637f80699a4dc6069ff20649b7777603f4232dd2d630dd1132f10d1b5d6ee65637206f4c4aeb52796ffce268c6d5569d30719b3b1c3f1c6da1c7a0a9e182dc91f2453e2a29b93ef616d61f98575d2ea9837dfacfc343a323b80960c45f13c880c7afd9c9acc6a3ccdfe"}, + {"0000000000000000000000000000000000000000000000000000000000000c1c", "02557bcdc678dcbff3bd72fca4588f44ccc047399c28507e8cd752a3d699e766033fe77169efc0fec70b10ae069fc670e8abe346c2d6ea37940dfb503a213d16720442afe6a75d6928c2c931c7ee4e74171f2d0b076c49025c083a3ee05095bc9bb5b1c9b3f63556d12f756d51375ad0f904da56c9aa5c1aae9f5f7cf04c0a6bcad9abf819a7d41fe4388d51fdeed6ebdde83e318c4707e5ade813fa9d240919e18a8dceb91532de03096fb82d31a3b5ffe696b3fc3d32c334311db74e5a0cd50f0159c5ada02d0c4225f661c73f5c1c34f013aaba9c49c9019f5fb6d1a045f7ce5a429316b69f288b1c453b2f372ba5ee84f47eb0e07180075b81530a6ef1299c8c9ccbac3a71ca7e8e8c08caa339aede22c30bf6849f6d9f4ff344c2b3d6ba020f2d9410f21f30178b29e3ba81ea51c3eb5cd8ac19c759151a642e7555b69f99a484e35bb87745fac6aa5eca93d38103551520fa21ddad5d0e82bc062ff1d0bdea8e94b604e990d1f88c43a3638c1265832c368748d4dbc7b70acd24870c8f75dab53236353643e471eafa7dcbf96a0a7cf9cab62a13c81838fa7fff9892eb31b89ed014492d1a388e195d7298150f43be2efa651f5d4afb181a13ec720ca048acd0955e38df76c6ed6b9c54ae1be612865da88ddfbc3884d4762de2c99f32926bb65b61f74b8c2d294b8a04199909e62393272f3ea409047b0aa2ab48f4fc7842b08c2e670f5c527494d40c064c52d4f459d82d1fa4d7f6b857821a5d1efa95191e91a23a94573622e91df27c1ab07fbd540c35299065268fe8bb216de93a8aca52eeeac5f2f4fe9b56400b48845ff114cd78c95b826798cf825ca4ccb6fc6a298d6e6f59f7554fbf86f660d1ed30fe3ad835260c0f0f7b54142ebb59949b9747c3f329232a75bc11c61d655f572b537c2be8abd44971e250e5f8b8720a30027b3f94838ed3d13ce1d121df71b8e853e328518a0f6ca128b009e1338f463304c5631cdd3120f87ab106df1e1a48eaff85700a11107759fa0eb1379db18a1653290580d44a56d9f4a5caf2e9b105eca275f7ca070d731666c2006c393553a45a2c2ad59ef239fa21324d6fec2e50b63f9621f8507b65f09f7afc3bfb7b098c8534e92791218bd5f263847a2a88b1159bf3521136b4202c983bc7c828f478aecf557609e0bcb26a051599afa3f0c84ba4da958cc4bfefd7081cd90543109452b5dc52bbc91b84b9a44667f35addf05701aa1d9e2c54d14f21fd9227548b1b78aa799d012eb4773086da6403d2982f61cb53afff2b0d8ace9e5793080f4576c6048dd95b411782175f26a8e147d3597cf92d180da9a04fb39189faa499de5b9e0d81ea340c260fc9fa8b73267af1c872b1449b4eb6a19491fc86bf1894dfc0f02f12f7cb37335e154230b91fec930da704ebd68bb10170a8c61b85322d601c2ddf5095033b1cb7edbf9264e5b3f5a1d3a59471a3a2c2705635a139e8c6fbfea64603b4ef7420f550c9ee2345394ab6877ad4af9d6f2493f515dac19c718726cdc41e6ad90825f4a974ce88e52515d098bbd048c54ad73d72eb0dc6b911c0df1fe7eb5891d04a6b1936e032177ed9166e5421efc64be1a4e2030511558b60e812bd36263581cc76df218db3c6b223a2fdf8e6bd162129ab630c130870652374cbaa81c5d53847557b45d03fb7391d36ab1e129d85a373f304b4045dab5297e45e55ac0e297d5f18e6f1cd5639528e58d969d54dc9bb4134135b21cbd5c600fa656342fcae6e36163e167745be0dd5a14d9db65fafc3b714cb47d31a41d7b67182cd0f060370b774ad49f64132ffc450deed9d0a7566c424b403778251468dd3bf63c225a2308e615fb6fa513ebc4f77ff1080d186adb5744737257259b3fbd83c"}, + {"0000000000000000000000000000000000000000000000000000000000001030", "01c317634861b9b1fca5c39163a5bb6e87c91b2010381ce432a01f019d13c8c830d6dbc3a3149dfa7af804b0372bd30f2ef3ff5121ef076fd08cd89f9f5a94120384468faa486fb9f78769587a513f6ff45c125d0bf6ffdecc1e3b696a8981e0bceb08a91257b957b552ca3b044dfd5a6deaf7261a7ff1473213f6925713274d7fc3708dc90595ef445fb7e25b81b4c76f7cbc5109c61bf1d757234b24c5675266858664465c1ed9022dbbda9116bd678b5c64aa49dfd713fb19dff65216fb17b8f0d982514f67729a4c281e7b02bf1975b61db12bed72aa72898c230248bff0d81b5630fc46ce5fc6e71214a88441ebcab6c306756e02b8733afd0807284d05bec54dc1e21e6425395d01ba07ec90940c32e6ea80d9d31d96d6d42714345039c315eff912700d46072742d00229439e01c20e4c5731c2daf1e3c0150f29ad7f6898f9df3114c5cf5d372d318ef013ae01c9ef8f91b0919de54f608c3f49feda06669ae1e722647a82408f6768833d12eeab6c0c20c32b36a2a50c8be23b61e3ab831e7c3444527a6c5963a32df7d1209a3bb0aa10bb31dd0dd6234df5ca0ab0d75b3dfc060d6305fd6d3199b6a8b25e6b6d73ccd7825877010c10add642070175690cf358a75955de427a32742e1bddcfe2085bd791c6bb8515ccb4b7b294f5bf172d22f001c4c052dc834d3e32879bd51b16a9e7db879102132cf30949ed038f61aacc28fa535300bbfb04372f1feee3d28d6b36b5daa641ca624832d00fd725ad045f4cf094a606eb978b8149b1a0cb7b0f04ff2a5741c7827ff6fbc481f8fc34eb45335681fddb3b7dbd076d1d2721926bb8f1563085affa37e895fef6a5e523c1c3126f7b7941fe40796573f9254ab0a17e469a0ab77ae596163e3df7901204dcc603090689eba0663ae5eae742d0f38bc4743a01ae6222930da7fbb1b40248cd98e10e2e51a74255250a553febe83a1fa37724595b4f221f0d51594a251c04dd74575808fde2a12ce4fc68fda1852393d4b37b47a2e97d594e6b5df82e04755c54e36b8df01d73dd0bc0667ab7205ef794036d97ed4b924080aea6106a7c975d787d73a8f19521a07c2d91eb3bbf65b7c3a5f54d1205ac62ad8d4e081750f1b30b5f838ee5346b1f53766923a9eef5ee16450c189ad6d9c0e5e8c40ff03ca39edc5cbc1d97097f1e525545216135dd4907724a4666abb1b5aca418ea7fffb41d89019f2e3223eca82b9d011a783c9616b2675fe5dcf339b60eac42c87d0a6b7aaf5c591f31005e2875557320db2cf45d39416a0d2181f1b2f00ba60de31f501437519e60cc180d225ef4187aece54da766ac24ec724bf3a316687de8ab6a242153e5290e8abee03ddaf330e7bbc1489590a96e52c8dc89b0341544b22427cec9ad1418f6eaf8ef5f29c13a403d03f13c78391171e175b024af32f0fabf30c7dc89dd08b74b71e3982961c540b100ae682994f4d89e485a1bc7c28208e975eb9faea22f9cc15eea72689ef8982d7a59c9375b81c7de5db499bd68bec1346d9eeb8f21b72f8e469d8f0139f4e8c602e3377a5fb8941fcc45ee17e2b7ef418bbaca760de27850dadf95be04ff22aa417124639dad6254c3f6b4247e45453c701e222597d50d5ce103b1ac7ef6bda95d81adcdf2f00bb20445f79ef0cd5f11acf6a44cd4f244c66fab36af10699d6525ee62f78927d87b9b0c6d21574941dd784405b0c04cf989724df53211c3606e5c2ec44f162be1231895c42090f7e9968c46a7ca4c692dda66d2b8aa1a20c342f2f09203f8971cf2d56de4979a175e6d42448a3da5282cacbfa100793c63d465eee313f82ed43b876fa00165d453a3b356dea5fca6b65a1418c35f8599f4f49fad6ec77d7478a8e8d8daca8ffbbbd0eb"}, + {"00000000000000000000000000000000000000000000000000000000000011cb", "0076856d3ae68cfdb4b822234b979d819931cd99415d094f2465245f3dd879c8ecc14b6172bf1df6501e2bc755f8fb310133d94e441c02af1f72013432f1e035257cd9bf344199bfd675211f317ef2269356a8f906f1963d806226af5cf0a3340aaa07f957dc50259f30c49512ddaa1e35e35753a43ef34a921ad7d57ad61e77712f1499eb1cddba36ca2874c9ce76fe3d068023be468b9127a62bcf64d77d77c62cc68b9f7e7a0009d8d6787272844f9441ba88f55ff51f7fbe1e1ea1256b6ab05917a18cbf56d2e0507cdf1d581b96ac6b38916b1c3da0a9e14a4aa9da927b704afdff7c4df55a5f670c34a36957c9dcd64ac8dcb59b57c4fd358c0c8dcc3f700579a89dc651f217b4825df21c9c1c1d5a302f949167f6d5f156063642b3b3335315bb795f0d43fd6af4b12d3faed6e1508521f914a832fd7ccc0f06c1e2ed7b84a3dca4033c87232ad9346871f68f0271ba0def616e756650813ba83c589210e854705842876786cddc83bfc36b3a02c567fcabcc867fc8ea08427abfe686aa0dc315210bfda64be3f9739fdfe1199e879a4988e00bb1e556c5fbd83609b7c813bf4b0785d78002166baff8c0b6d14adf50ae628d76ce2666c5ee8b99e9356db7eb46e97f3fbc16c0c79d162516ae8c8b2fd9083d0276c2a7642b79bdbb5496dc161badec5c4a14e92ebce7530a26e31672b5fc5efb1e06ae72d0fe09c598e4adf13a72b39b376ba9fcfe5f07f8421d2b949fbdc71c044ca84f8c4a07f699e7bb2aae1e38239b3f630f06e4046b42d56d6bfb2cfa867e087fda13b9dfdde39f689cce6c1b4b49479bd9e20a1702e4bce94191627ee1a1bad733997af0ed9412314adf8323efd13bc7e23416d3eba66592fd1fa4a322efce24f88d7a0944e113cd60e718e1bc299135094b98548a67d89ca96db5e5e80dce962ed1b736e81100b3e1df9125decf69f9b1f1423276cd2a849c8f2d0e0abe3cd549c224d02516987257ea51d2db12a7190e6073e0328512112a4be291a2aae819548eb61624297d770312ea335dee75b34cb27dcc458780550f940f7df7b4b9cca08d8bda25fb4e662d470bb3fae39c26f2ca4652544baf4a9d6373a67465ae9eb3bd6b88108971cc0edecb39d81d58a725fd29d66737fbe9872d614c7370542a032607b4bd573b3f41d2917fe34d03f0c5233b956dfd527f25ce22eb61ee249fd61768054e24afdeacc191c31944f5cb72196a1cbff9021a0a3036969d0a023bbf1ff307d25608d9bb107ccab60a4a6bfe27570dd3df72a1c2df9975026904d6a7ae043900e9c92904095a8bf1fbcbab871a34067d7c200d2a3b78149760953a33e14a07759106337b75f2ca0b88093dd0536e69a2b990dc983e481c5c57fe91bd2747f434e54ae3b7b439e2b116b86e5b18acbff4ad01b7607d5ccabb41c9c8e1718be2d35661ebb3721e0aa874835e059373d5b6a453bc3ba4c13d5df94799245f37f0991c9bd9028fc5858bbf3785a5627d9ca22640a574fdddfd80f59863399d44e3eb59b37e87000961f6543b8b8f7beda37246add64cbca2838a562a165efca13f47c4cef1c865f02bd71dc67574974a910f1cb12d9896a27ebe5b4a8ad9f585fecc929eebc9132cbf328215a2b2de550446616133f350837c138f061a4301c2ca2f19225b31153bc90e41d4bcd47bc70784d6a41b096cbcd2db0093127d4bdd8aecbe879213b80f64fa7209e7987ee7396cccfaf7a3a13e50512e3fa17269dd75e9349a3385cd336ee7a121ff6a2912adf4eae589fcff331ed31f8da9a50127721181851a9d6a3b83b8f513ff6441ef51adc2d8cec975503a1fe39ea889a36ca5f09512d022e8bd40dc8b0ca78628c2d19a279b6d8cee333740455a8ea73bc4be64b1"}, + {"0000000000000000000000000000000000000000000000000000000000000c28", "00121dfdca6b4697b72ae13d0fb652ad7f799c4fe713a0873a9d4866f7a79ad20a6dcdfbcd008df204882d4fe37bd2b24f35e46503b310cb2a0f540f5bc9bb3080537186154de9be75065ab6336f61a1b5546c89012103a827588fdf6883b377fcbe33511fedcfbfd301bee584106c2f1db8cef1ff6ed8f9552f8e2dd2a70f478cff22e397f19bcca3b83ecc2afddc8250c5d43a96dc25de99756f3ed573cd40726e9e574ad3b6d21122adfd7ccbcd5a9e6bd42b137320e9a09032e7ec2b843bb9f594cf5fe2104413d0361f29f2aaf4932d24d5ae0c2314e3978caef58346ec45e9a269980bea29fd97c028d582f5ee64484927512e0afafb7ade1e18b9c9005d1425295ab39436704944f5d7539d7f8e1db3bf504b59010d82899684eac45971b738de20dc2052529ca8197e70cf7015862ddc9d019af9782084411936e1a29aaefdf5c956b3f47b0931ed85f3db32016fc0f6c25dfac9939163b0424cd98161fa192d3402e17b646a04b5ca74e4609b408aa480a2a0981b8f1fb8fbe8d3cc3b6b63ce770d2368a815fbfcdf862c353ac3a1c2953644d1c0c6dc37d59b99f1a296b2ed0985fdaf94442529ddf3f420cfccb38e31e2bba9ac63e91bfed3b16209f0f2a994f9e001b26b079ac58626e58eb0e215468cf3c803800744ce4ec61e9f8be92b6d5ea183189620ee78143364fae3aabd7a5aa6b70b7dc9bededd66e303e908b5d8e9262e8d889941d722753169e2ca3338690122e6b36f31f4f3e94e33390f6f4957d70e7615e833621aa5f571c10700f9ce183c1035daea1c7139d9be65b9c77d380da57c34d29f16cdea67b21a013afdb26408be2e0b367e97df01f5216bf54c0e4f1d4286c8725d32c1721e3fe95e184f342ab5d025e58aed2ceac6140e538332e0d1fcdc86784d9c09d3f1c025a9f14977f9557426c0e9d95ff901c1efffbf6ece83dddb828418308994ca1870dca20a3b10c72cdc35f376d9c0ed5175215903416e7f530722f5c860dbf595780261a270c9dc6c6e5ef295bd4dff22e99f9a01af229678c35c642daa750ef90220053df768de0ecec4a0b7a851435c32861795f198b10e9b80e6daa6cae3c2fd516c97f6008e87a79b965e23df15594fd2a081861c84cf974d60d1dd2fd55f5f3ce42a6b03e6db45acc5e47b96b398c31205b8f272065bec46fca8dd6db0b6215792dc30deb9efdfd0d62a0fd9dbfdcfb5e9ea94445e51e63c4e1206bb2ace0a59e859435b3863242cd1e5af730624a3a43ff1ff2c9a768b3b8cc2809004d385f875ed596ea918f33a167adf03decbc4ddf892e3992b29207ad88d3d6bd6184f6b8398e059a32d40e2461f41b745c2cc7325ef1db2e927f8d3553f1465d3fcb9c5a34227f6dba23d2788130feea6a30b684b23d0e059908d54c5d60ce2036bcdda278c7bcac30f872240c1c86e8334f4a08833bfa5c86ce1c9c5ba0728427cfe468ac683dc0e7406c26a8f42037728ee8277396c586baed9ec7c8905134d34311785124305106481bdeb010e3131b687290a4beaa143d437adae4100f94b9a209168284f6fed1a1df1234fa69fad4a82c41673b242427b1f1ed89510c5eb8f9761302580e2e1c980e808b0c15129d17e76b97757a0e8aaa1ed9089de7c76282e83d5f9b69a03c9d1a71f74effbe90803869c4b6681f28f3482df212c84b4d188e9f3947143c8654162ed6027fc876533235472b25763ffc6d2f917f271f26a53a59f48db7c4527dc6fe2de6546fbaa4f2ce9ab1a97e17c9cfd140577598206003b74521317839d84df251dfb428227957dfa7ddc136d9c58d437af2654c1d98d3188c31bf67c408f2bc10b630fc394ef51b3c9a9087700ad4f0fcd03196318493a47e5fe5766eb0b837b9eb214"}, + {"0000000000000000000000000000000000000000000000000000000000000163", "002ef789b76f32a1de8e320a975430e715fb3c3608161f620a1c123887a98e229ea2be70f178881a20791432edad02ec4ec376eef501457decda52d89355b5183ccf4873493067455b43fe26e6fc9e0281358f7100ade0fc8bc2b4586dbd8144ba5b1f699d855ac62b31dad6abfd5f1c49df9a559681f23df6317e3c1f070249a5c7cd84f8a5fb4082de899d0359e4275243b6028ac875e1aced0b8c187666c64c1a2f70fabe60100772db3bf8481c08c7ef5685dfe54d89a6f4d5afc436341c06ea94ff4d9ef897534ac34b364a063b089f0da9227a3c13ed32c629034ddc30d10176217a71f01f75d599c05c73b7c39c03a3c823eebed784ddb8740777c6ac04d2fc8cb141e539edb6bcf59332575536154328ecafe8f559aa3978198fdbd5263337fc1ed216be96e88f1390d8dba7ca4d79ddf78ba9b9bed0fb187700d473dddd13c395a2e1f1b4e9db1dae79ef1c01ae28ff1ba35a8bb9ef136e52622552f52177dc20253a9a1bdad8f6194437eabf05f503b2c8aa7798e5191b72a429102d154e277570026911d197a737f74447e705892bfe47cbf71d2707556b1346415f7d38be03142f020784c5446079189f5b4b67963e585f4f9d1a0cda7e057518a3e70b61c312b73ec5219adb6c9e04c3e744b35296b4fdd2325b929762b9393b36edee21c68eea7e53ff039135e34cb27e76527ac73af6b504c7945d5612b1c2ad8b22134d6eb328fdc2595f440bcf7ef9a9992171a273b0d820c6a7d77a6f9ec8261aa1643af365100d3626d338de3ae7f3124cfca6b6365103b5f5e50db5309116a1974915ba6e57142ab905039650cc4fd714f39793cd8a31ae25deb9bd7c8c07afac14a18a9342fc3cc52fee501cea792297d622136a0fcfb42074efdec7a2a47ead2f78fbaa5e13655c43e72978231451489846dc426ec661efbfb32980014e87406508b88376ebf55620db64f2681058e2c30cc457be91169693634cd4c97ebd1ab17ad95cbd3201540821aade34aba98f169e47f89631cdd7da4e740e475270bcc8ffa4e547e29fa2d63ce4ba3e97403407fac6965b6b2d49c1ce235747b312fea43995b12e237d0656d2f7e207fbbd82b899ad4aeac96bde404214b20315970dea85d3c006ba32658c69c1f45dfdbb1c2608f99199ec1f1520b5b46faebeb74be6fda6cb042945cc95d08d332ce883449c77bade3b84bc03a02bfa2da5051a3f5fdc2696b6f4781ce60c7cb9521e05d7a6b7138ef851f6eb82a71c4924a63ed11f64d725e6e43d119747732d43f5959db115a1e02b94d24321b88be33c3924b1fe2c127e0af2ecc187c21db4453cff626722e362bb9ea4b85d53fe8cb6db5e9905fc23748d90c7933e06a8bc657b1bc45489bec4bf78aa243334b2f30fdddc8fcef44ed9587f499183995c9505497f31cf4452f062f575d183ed536640979be51c121cf53991322cf5d6b2a68ad3618051c4a25744bb0972a63c33c7b2ed62e8f470afe17fcebc135bcc2416a2337b73dbd549c72b416e6de5ea3d13ed797aa52721ca5e10650be9619ed5088b6f7e7dab9a1311cd34717d5b3f5b4be11d02a5c95f3164523e2d784ba935affe6091dd69798e0d0643a8f1a97bb43b3f7ea548c0cfa0815434fb218dc7233ec38ebefa9b9b570f05f1d3d5f4131e9ff3df56b66bd75f463598d8c35e0b2a2b2948d6e22dec791251084adbfd9c3952686a08833ba71e3555b9b6ed739ee841300a274c3d94301effeced0ceb34cb5da1c40decb169bd4f639f5f27100d54957794145ebd9a31b417ee5f98d094f42acc26aebed63b3423ebc8f4e4d7f4c612dd40328e869b1eb3fa2cb9ddd173f55037af41f65886ca141f4b5b25be9b1b5a6ac9cf79bbf700c0559beef16c389ebb"}, + {"000000000000000000000000000000000000000000000000000000000000049c", "00e1a5e0394ce420ae4061336ca91a21121249e6ad0e07ce4df88bd0194a12e75477f9b663a6387f69760e9c4676922af3e187372322bcc9231ef359bfe62a215e8415501d720dcf85c41b0c5582f2103e7282690896556d73cdf594f19f4194cd173c766a0b1b9ef7394b77e50bfb2485ed31262742317b85aa7476ce040c76ac0bedc442e666cde1b54e13f92635fd36151b1066cfa02cd8ba9be890e367446440bcf436f9285604641efc5542d403221a72018af9bbdca8a9ebc97819d599d7b65d1f3b303fe51e0cf05259f9e6db907e0ad77467299ec1532f61a66ebdd806262243d22b2f191c66fa6cb0f063b33ed4b4cab0ca6144c3ac822f12d04b83e1ca2082757e62c4c57851194b1850cec72b0a77bd2a5bf941952f57891b5c8b3663107c1ca117b3170334063a28898ee32417a333e201329eb42a2081743b554cb849193cf419992e8d3250427e812807dd3b58a4377f85f20358d0cd5bd35f66937fbd140dc095ef74b6aec7dc7af46340401abecb3e1b7b6b1350befa0c1c34c91253719033ef0bad03dab54aaf1d376e1e3b966e93be3504634fdfad193a5a6a457609830d53f60898224c55710bf2b498088c924a39db0d9af1dfb40d989badfc05477f4d19d24d5912ae7713e5db3697a0dba1f9d661db9be81e6e315f9ef5d019fbccdddd1df2916d3f31c5534f0edd331d564ef60cc9a664039a10adbd4c4617094f4a0ee7a0b83b502d53024eb58e244b624e061219b4e58de3d2fc3c8d14af62d77fb495affee85375a6b13cb6a3ec7e511c423be6d37e636bff540db7053ee4143350a37b893c124ea59ec68e6fcaba7cd2ff5045bcb650e6b8b61c2d9efcb497a7ebad7a55247350ff0b21f29ad212031408619854081bfe6c38e39256c6042da7023980f2149d19d00d8e93c6866ef6cd5c6aeec678d1b4597701d859f0e91a00cb5c6d57d13d7aad5712073c0d882027b1cc8cacf82f9b4cf342a8d677ce38c379e4e546c9f3e4e82b7ccbc75db8d4b34b934f28d4faaf604983648d52b06b89b345851a845a2ad5e02ebdfa9e20dda1dc8d14b5ced37aa2d7fa341c5601a0515afd2d6f59fa8d79bd17f6ce05dd60d739aeec207dd6fa2210e47fab6f54e5f078564ada69b6fe45c5ddeeff41b77b4c8312650d8657769619d48cbeda0938522c01f65aa153c609f14def3178f32d5d1a3c5d7346de13951a55a1dcfa77b966a1e096263b0677e6793423036cc396d38b2669daf3a9dba46c4676b60837e9150df94556075063f5bf391820a7796a929bd4bf24e90b9b2cf33ec84190bc4755cc1363cd62e6137ac2492eff45527fd86a25c50ef542e363452b1fc1df30a020bea9ce3e52a118d6e91523c1c5bcbd57a02bc1ac3e70d5506d98ccd90c9be421f5a117dd42417f67fc0364aff55149056290e95145179f12f5a251f760e74e8a9f06e8d49ef9b103854323c2914596c6748208229173a8d25c5f0fb3bb6412aa2f5a3d27b3ba968b3974fdbc046755d98a0365144130dc0d73fe92ef3e0f2887185c1c296bf4abd477c6bb7e9d7db93fd05910c5a295b68ec8f1c74012962ab4c9e9bcc798a76322c0c1cd861f7101dfbe9462255bedb5501096291129615dad57dbd029535c5574e4d1e5c6d233da8cfc040448d8338c7fa3ee44416496b564e46a299ccdfd344d9dcd8e274f85b903d7b290cae87705341f0732092c34ee398d2b82a4eb9128cd4ce6ceafefd7b4c424cac95635946b72e3d59318399aa7b6fb8338cb6b07f639100f1e86436e5a26955d3fbd89dcddbf145212cd0adf35312727996ab2778cdaf2251719a97b9a1a1cb383a2d17a994e3e062062c8576a21b39d79de411e3c25f39960634d6085882a395ca67a711e3a5d"}, + {"0000000000000000000000000000000000000000000000000000000000000105", "01621e2772657f359ce952928433281953673e1b991895b966fb4d79c6d073e2ec46cc8fd2d4c9b7fb190fac5b9526c60b3684e0935f391e65a9eec9f66f542dc013422c294abdb04fb6accfde9e4a4cd4b6bdf6077e2f1447e978d9ac8676c41c54cb922e31b51d2c30925a59eb564aebfa1b99184fdf2722619c78cfb72603566e276e39d9c7de34e381f9070ac904bdfd30432a6693eba52b9f5fb539091054e15757fcfd4d6503fa6adfab010009a1c7c06096e60c6089cfa6f0eb33009243bb6955ffb9c565983b3e08f60dc556afd80708454a0055a8e94a026350113de28235d519cd2f0e1a1656dd89bc36562dc2ce2941aa2ab4563b6a4725e12968fcdd98c938c5b8097879fea33d7d5d8b813b8d3c0fe3112d4f4daa89c22b563723aff73dc46c28d422bd9a9036cadaae2711ddd5ecab0385f94cea331e2c7e32daf13bdf4605c434d7d326f6f917fe57032414e764353ae1fb89525d0f25bccb39e7dfe14c06258e12aecb9c954ecce166ffbc94dcbb7d5c39c00c6e6d7c1de96a0d4f21f328feede869788a723f093bd6958ee251dad1c05f886557f6b2266da55b44ba076d946434d901f5c8851ba5b2e8b23f3c10feb4750e8348be1cc83d3aa3a324dad5f3f589ab7b9e14b81541ab58a48ecd2538cc246492a58a41f46e5575b6449262434d256c3df34054efded09b2947ec1f9df50c977d6b11b5739fe1fa34d9cb4f43019797bd4c7e55843e035db683ade1a7d8dd38dff4aacbcc36d0b3404864b863977519d89e361a656557d3be687e16f948e9af7ab266c95d94aad53b78dc88c77093fe8d021c92f572648e91bb8f30d2c572f125f1c1e890c320214ea3e28c8de54960a2a2537462bb3a7c1d96587f2b028d90444ba9b2c2368543557e44bd822b766e0062399ec7539e478bbc1f18a10c4b1a2f2bf59d253c02986bcb892495ebb54712a40dab5cc170191a06b4035fbd98e9653fe1df9fb1672a3f5d106400a7255e044aab53df191106e58994f761e02fda5ea7b9bb5a2951c4d58a0d8ba68b15d453ba79a84d3851dde3430e34656d19add7f3fea0e9eb3cf9b0f744c3dcf1a430509d1bcbde79bb1202e3f4cd768cc910ae8e2cc722e72b42fd52c77ef063f3a3f5a75d4169955bdac92500235698ad6b1ffc52b28caa60422cfc7ccf273c06e674743ac28d406c6fd6dcc9d02e99bc98f2cd18339af34903da930df64678c337d1b89650e8bfe03c0e70fe6ec698800f2a0de1d8325efb952cccd6f04613132fae72455c876fef72168b211fe8ad744b9ee3159b6502a66946d5f83bd31648364551fb8cd8a2ea27acf9c16ed18813293805000bf9bb66d5665b95c91eaab395295a7c4b0a2a22c8079f9cd944c71be4bd327d726d48d432bbcec7b370b9b63129f7b3501372050df2486d4ef88aaa5b352f71b5e92e2b9dd50d114c660dd6792e0ced8ccbd4ff684a06796cab7d43b31f4076d200f12ef9907d05557df6b6c986654ca2f1290506fd18f61487c2ee683d7dc64a6ebf6fd7e8dc1a60e19001e98451955185ff0ae4d57f1c7cff7cba5a123384645b840b156fa83abc718bae63125d9a7f2c69dc80d0d7020f650674ff4648d432a802de184031bc1ecf44934f4f032fe5e8faedc937589f9af94409a7d17ee4592395f81002b3703c7a154291ed83950e53fb1e7ca5a095870df45cfc311e6ad16d9f81441d63aa231d0ab9638755e261f015f3d1bbe97f1ec739d23e52610f05ae8652d7acefbe6ac6d6745a1c1c11c87697db922d67d441f56084d9d12e0bf5f543db215202479838f723f8ded929c77382369653b940ca15d79277a5cf726fd983d77f2fc624aea74ffaab5d74d0f7e4932d980174bf9a9d20f904efc4a0ff8408"}, + {"0000000000000000000000000000000000000000000000000000000000004a68", "00470dc3ebdb22a58154b674fbb86a59fed13d31d73138a3b768df0a5fac7f95093fd612224b5f7b55960c38b3bf3890c2bff10180c39454751d4213b4078c194d4d04e52789d7de9533ac97385a7ac6ac56e1d70230c64c1f0fcc1eed83b1090f2f71c9d6d73503d80af1bc80ade27ce92e6636587d68c031b70ad771260a0642077c8fdb7bfd53e47dc97745aa55c1b94dd70cb4ea4ab7893624b503273a49ed0f7a35fbbdea840c44b11487982a5d3c6dd2c046df48c0f6ae0a6d002ae441a3715bd0abb47bf5d5d9d0834a16689a819712cfbd794e49a4664dda620525b8f29225ed1622be2b5f43837716016db28f85a4426ff3557e4fbccd611174ee8917f0d9efd9a24677e4d6eaaee7b7bf11c813b81bea2f59bbfcd4c6d3bd3daf54c668f75886521d805eb72b57fa6d642863732d2e80bd41123d04f44cb5362e8e20818bf41918cf2ef8128e50f8fb9b6f004984eab94cc193217e544069380ceb23f1d9c50e083ceeb204e37b67d92b43671e522269a9dc5b735116033c4bae97fe5d708a2588e4bfc585caf14fdf0f1df0aaedbf47ef98f294348ada5fac861fe11bc3680ee5840daf09a582b84ae2f5172a1995301cd1b0652fa8ad78bc5796271d0694f892761f86beaf1cf021424a6c6a95d9e3df5dc135427a3d7d16679a53408245a86c438d2b31397825879a04ec16a6cdcb196d5800823ad80300557fdc8e42275fc4f890bc8c8968d237fb46e1766154872d4563f1b165bbd1446a2afe6d19d98db0314dae4dfefa44316cea6cce601adeb3271e6ddd09414fabdb7dd8f202b149572c83272621a2027a5cafde630803931ee5c5a86832db099a3f0b8909552c4df542616780855189b36e2eaca48fb9f39214119a9bb76dfcef8011c864d86c70f7601dfe56781c5bd90ec2d0a78fb5b543090fcc8fc8fcee7cd7d100b886ad9c7b653feed6609b30bbc95d9c21facb9f3ec8e407b6ae7bf57bcc449ed8df69619fca8dc473095a55e5212f3045ae07e16c2796afda95d7db398a1e6a597818169d299f49d41c9fd54686c5593bfcfd03583e7a97557b5af19153769b27f791942b5d601b0ce66f82bf50fff4d5519a396c7a07bb4a54fc33232983ee3cd1536fa3a022f4b73cf7e1c66ee555eecb3425bb8a0b293d1d96e033d5deb95c6a540eb6d4370746f797aed367cca8bb0222b1af1469177c37a3b53452e9d7dba5006bd4203616783600d6bd707f59b7252b335718d359957acc928b10b6064dae699e7ac8303ebc1fc98dc3aff57554330638834303b85daab108227c61210fb38f23fd04972a4bdcb5fa401e39c01f9525ff534e2ccd7ee172c8e2352e016a2c3ce61710b4a8f8cf472ad2f190f66d0839561b014e1bbb5315333a6eb5951ca5377b231d91317c195a8ced63fb03b6e081204f0c58abaac51b8a2cd602304558a74e1507b2e2a3517af6e043a46a8e4770c73342dcffcc21e08650452b4747c434e3357368e2851d1bb1035b3f8e52befa2ce153cde754db035017bd800b3057f704b3e899880f97bb4a34b234bbf71851e8e0b764fb0aebae01e613d432b76530d02b86c561af43572a9f0be4b591d0937652c79673b9ac5911919ba23cf16e14874d899a6cdf3bbedec43842355285bc024e994c056003bbdc095593596073238ba965ee0b4534130313f322944ba5df65fecf72a84d25e7a61c561d5265070081b2fd4e1bf1b1fd823711a2561a2c6db9b00d120afa5150ce3544e22cc3aae4291d1219ea99eac724116a067d67c6ff8a0f6644257f7a3a7994def4ac2b43c69593e7da5f8aef75459a4d7d0a7be0fb3ddc4960367156946551041066f5c658ba1b1fadfa16e252994b3943da70e71278e9727c6bc5d7b19d7f024d"}, + {"0000000000000000000000000000000000000000000000000000000000000122", "009f527884aa1c5faa40875d10f4fe7eea92996af11548916d47ea11db63584551dec576a25f0f9dd65d0781cc004ce552316fd1e3839eac9778f0c21f2ae51ffeda75d30a123549e2139a65f42ca11eb6112a290566aedf9adb56c13249845be6c3dbfb967dbf0d880a1f23d2341313f151de1182f21466e47f85aa06e80b788c2d4a0e1b0d5718f0c7731a43cd23e7aaf4b51f6f9201451d836340bd123446cc886d4f92bcb1b400b5704b242b4b3d6ae3307f4bd02bd175325a0ec8023212d41092dde1e0c08124cf1cb8752fa6fc5e5f15ed5cf263652cdfe57532c15571a7595a7badf56e28972703f855d909108cb2b9b7dd7df0f1981d8ee834713ffb678f511597790721a970a3ff0d4df8a1f33ac0dd79361872f4e6fec60c3bf3d316264a941e9f3c224518bd5eb69d64cf057299b9afedcc1a985d7b5a6955b45bad6d6dfeadd7c7e5d2e35e665eb65c9100f581b99a9adfcedc1393cc3aecf2e65de057d3810ec897b1a69a4b3aef544274115adcc294037c1bba197f99ab328661d3483521b1be3b46dff0897ff8701cc172bc7150443d743bcb18735e8ddb93e9bd655e1b2df9c41469734f94b76207214d5792004db898bf2f0e6a5b8a6adbdbe4ad335b0b2edd6e1e91b1f0592f34e52634ed354b6bf763bc39ca05ad67a9cf175737429c4e2ee51a4b65ddc3f65b206dd937801fc6ed011558d38a4aa4e1ae7a924a426af9858da9be06ad0fbc86e57970e6ddc19638c12d4f0126f0f79ad4321514ea2c201b660b1351a3d75e37d5831d8ef8fc5916d2ae6fcd69c8037b4d53578db4e0b9e7cbbc92f305bef931b72ce949885946aad1ee170686c514598c091443d86998ebf4e47af4cd1ed2bccb52141e3c4033eacec7cddf088dc31f33ae237830e54575dbcf824f524b10f89b8005e6b07b7d9b7552cb4be97f1f4f02f6c50438e6257bbdbf1172acbfe4397dd61b56920c7a5a6de7060c4ba870c11602aa45693fd6bcf6280816d183c3065f63d3ca22291ca1cbf4b4583d6cf9081eeacfbb2346b99f97e354ce288e5161273b9f0b032831dbdfd6b0811ed0e654c075fbdb1f6eff59df22e5da52762ae7c7783704ebf7e26431942e93adfa09bdbb4b175170d2e2bea3705de50bcd57bcd668df108d4491c16586455ae96219c558307bba4ede992005ca0754ddd25062cde3d7240874c2464e0754b02b1cb1fb2f3793a777f1cc939117b546f99a05b3e8af1d5f1bb6efc8d70db2be67938873e53e50489bcf4837d965420e66e26172d223dacf58b0ae4612724be706b4953a46476aaccef22181f89ad1390154a923ca6aa26d36472500f18c9178c229fc1ef6f5a95b32a50cd85d70f6c810d96c51f19d9eb7b4860e8f366bac38d6ffb28199e571d9a0f465d97ae215cbd57acb8402fbeae666a0d24fad5f795931566df2b8517ce23e17b2a373dfd90e7ae2af825cbad7af3a2ffe7cda4f0449fc26b58952c119d473a7cf4021a9225f7a0568576b96f216228fffc62065b25cce422e749a7a5ac2078a48f8531239db79e372c35ade3c1d36b0533d74079b89fa06982d1af431e26efaf28585078528801a11b8fb92901ef94bd0f7c3237ba0a569dda670badc76220d47f3259a014765cb755c63b652f859397c070cd794a82c22e511417559f70d6ba7ff009fdb9af613e02f4f2289f1d788c3b14bd78e181d612b9e14b01a6830e6b547fa36d6a4477bf2cea1f2099657f0b7388dc3978a1a53e36209346c4241369d670d8f365c10238e8ea96b08c789e5d1a63a2170c4cac712d56711019265c20de4118b95c7ce095d93b2880e7e25d610ca0b8aff58c876e0ff4273c7f4aef1e33d5dc0c1258053d9e5e50c25f2dbb2ba4a426c6230ed165729"}, + {"0000000000000000000000000000000000000000000000000000000000000772", "00c4fd462fb3f84be84ae595d2662a1af62878f93a133c82be9855da4d859fe241bf3dcca61211d8917210c70f044f44c7acc75bc8e45f6a99b6e9e9b9e90a15c8ca66a4d63d10ec0374e8a5aa452e55ebb466af014f1caf1c52e5196543960513bc90ea2a24928f500c9645cb7507f8e9d27da22fe111b7c5d8c595779f16aa0df4e550a7f3d16564472ffcf6376bfb1eca333a3a7f2fbc0ed5252eb7b5337334d58f0e31391f8f02ffac484dcb98b68b56542d823260fdd8e57ce6c41d8e16046f133bd1708e942209dea481e1dfd2e1ba07ddb4bbeb0aa66f95326085615ee9653203361c3f1c25b6636c8b0f3f07a3c33ad3ddca067237d62b9a0b32deab9b7c645bec6072a6ac79d69b055c5a68b30ec719a73c149733ceec52d49df65a04ef4fce750b1b822ffe4b09c383f939b8c0d55d18428fb0df6c9628524d78891a91eaeb849acde658a587276e3ed3f800e44ae92603063a4d358140e05f2c506d0c0855ae1c494222b1baca81f74515d2976612a682e81b165b0ce1bb48e38a1b739e6c4247a0d6226db30a377ea01dc9c38b1d55e239bdd4e3bfafef82d6e76a3e29700229f0bf9a16efad32643518e2d613d6bfc09ab2700364138e4108f1a5e3b5c1722d95646894a40d7bc70609eff05b868d792613b45241e5f6861facffbb94340badde88ab62f9dd9a5612b3f8d369b5ecf8347e0a1a0c807625ad05b0b0512bbf2701c946e7bd21e80cb0bd56c555b9fba3abe103d28e88d44b5268938020fc328565e928b98494b4352eda483e1ada9885fd4d654de8e8dc2474f36a9883fafb368a941575b4690a2331cce1cc0d79feb993d1b9dd4f853b57cde9b93dd123041594029b049f885b0d7b54064aeed541660e66bd63ff22d3a1338e92c0cb4c74d2f724ddac3044cf2e01769b82c2ed14f7eee2f1c4e669171d4b2d012d513a6d135f51d97ea6d0a956ecddc4003ccfd82038ab38211e0afd709f25b2d25e73933e2c1d491107315c8533b09e3bead3a141a5af3cd91eea3535405818a40f542889efae7c58cb11f88fba96aefc8ccd0c8e065a73486953bca677b4edf8c3aadda75f2ccc5d6f4ceaee23be4bca47e8b8cf787eaece387e7e0f13ed19b4215f7f37dc1ec24900547f353b94fa7c7b4b3a7ec9d1e0e4c16d18a69ec04e14d5baa173c62a05570db3ec0b4b5ba3ed42b59d5c06194c293f2474105e9986bb75909bca519ae52aed000f13603e71ef14c70d73425fbc5d0fe80526ce6198cd5c4772913118aed67e22cb29b75eba5223ca60e9858f0fb2ff4709eae5ba1854876b6792c0f58f138ab0e2cef1698b0a44a1088a309dd3b11ef98b115e47ce7d29b9dafc0dd7aedb97a047d5887e140d9ac964add007d0345c11a2be2c040cdba977ee2372c34e1187341e1a8683032aa5f93b996149977eb16a77b6f357078a9fd39c06c9412fb895bfdf1ec011537f42cb85be9f8f74af0e394a53fe4e2262cc9414d065bacc6e49d67eaf510eb7d43b434b4f7ede3226d7574752362809153159032eb045f84d2cfa8b306387d81d632b1630fdd3d12a3b016608241057af5d541cbbc6487e09db99451d169636469708fffadccbe44ff9e31d5773b6dcc6322b37c431eacfd64a7f2dd33f53f41a3dd93cbbc0320a744684a9954d71d0a912db061ce34a28417999d01548233db3decb7b22a3c703a4ddd9320e72377de21c434272d45f4cd3f9d7e4a1cbdfd306beaffefbde3db39a9046a0769d191ae5d2a238c1c67b035ed1b915a3c31130cb3c89af4399d3876fffc67dcc94bd36509087ab239660d1ccb086e9654b559e5a4655cd972a81bd5c3567513da0f5bb21517abb3309dd1fd5b03e92ea2fb6971c64f09ee59f81ff60b2182d3a3867"}, + {"0000000000000000000000000000000000000000000000000000000000000a32", "002574ec32812a141e3904d3ff6682b95df1cda3313288b4481d6def93dcffa8fbb8d17c4785ff9de9fb09c4f2009a5d974b2649141ce3c7fb51196a7f3f253826e20d82e491337ef4d7b95bedc4c22e4fdb061b0b2c553b1a5ba119037e52c11017245747221ea78518bc2c70aa8edc54a7cfe4331e773ccdd4b75e670820e9af2369bad085eb7b423427c11f84e02baadb872ee0b6cdfa711695c2fa632b52cb2c0d1c455316b20a6d735db71b02b19c9ed11e1e7302de948d998b231583a7b65a66d6dd7349a50c16d68fca2d2b5fdfd0136be8a9856949b586f9b28b8e697808c46656544b13dfb45441867b310dccc183a853fdf24a741360180bca79cc1d2ccce9b506d0ca586d48b4f8727791b32f0fb6866590440cec546454f82e11922c4853c66c1680c144768eb8c09ffbe70e52d95cddec0270457b2c1ef5d6678b412cd87762f7dc64b620cdc93b099c009594e2395394bd22037122a2670d3458c2784de01064f57a8f280679820f13bbfd45de426400d71fdf0a288b9c84c64a303c1b2258fd4dc38126c0b4964322d7f285a61333d3fc8223f48079fb798ff84ce23c03b59bb00f9d792fc924e4ad75ba4a0ba2ffbd97361104ba4f5e090d1772fe11cbe07dbb35d55b1a471917f2417272a6de11ed43d191347c11c71633b9a01f1d6cf7982ee9e5cffbe3125507c814d6c3b23c16a000b80e42569685b4ed0b323c97f5936a269419de551f8e06384a14ba372611a80a7148f3ee2370d5161f00d25b71525125fc9dfcd2261714df0cd0de76cf210a9146c5aa0d615cca26819acef3d2519035160a9b02314185995553cb8a2e921ac9e3be12fab65928ea33a1477aca91a66fe62115ff45d1ab96ab5eb5790b16f47b9abe5feb2bf525d1bcbcc9e84d3d8872ea0c3f6a6f7535b2a103d75cf473caa68ae1349c0e55120074a29748615391dc6c6396dd3320f11fe58dfd77056d2f48861cd9cb59e5a5f64b408c3a8ffafce030076d117c4a21a9dd7dd950f4c5b0467c97c5ff772925bf1289329fad31cdf7f26a96b0054a1e75590b1a0ffe3ed79514cc71b58e63b8b43e6dbf2f957a654314f6c57ec0a538698cdf4608fbd57a9dd5da5e7f2c1403f36f828989f941b1f9e8b75245374d4ebb84e51d4f419ff55498dd2162820ca225ba90aabc165ee50212b2a34ed369aecaf163cd0b206cd1eeb1f889d10f7f830f2adda48f1840e31a2ad42da543a74b4dab1c358a50f9a6befdd107c5b7d2ef2c077a725c837f31038f8b68cdbcdfdab195cae67a0e89d517952486039697a3b84e59beece4c434c658d495f4cf95fff9109cc523a2472df46c4173e4e664ee1e05d854187b1bad1789613af8b5f91461d7ecba5d69413698762d215a2eb3027a9c4fe6aa861bfaf7e711ed79cfe594013bd647d154db9eb624f20744174cf8d58d3fb1e809c7763049e23ef7ae3161746ac6bfc9b18a8d9d64310454bed0ae51e993ef43acdaeb5041c184bbb11469762e0068715a17df08d78eacf8a3d2446b1e4a5a10af76803b51df839952e43cfcecdd31845559ac743bdafe9bf01dd31ba7a70872d46f45c71e42fd9e9f1ef304d3b1aaf993f37e4c01b66acf63692addb74c3973c61a46dc7f45be42e4ef1bd577da2e525eb3f001f2115d2051caad3cf44069ff3fa00d4de39242c61de8dbb9272de5dddad4953bb2c5cee20f2e71112208ca8e452f1ae21df2a974b28bc505956352de636213bd2d582ee84bd5b15234eb8b7478f65c8af7f15e088a93478907bf0f6b80b795f97361c2b216db379519b774de7cc7f317b525f474597a87f1c525bfdd55155c6fdd7e09bbd4780d63d05a270646dff17778c729f747bf52e9ef9bfdfd43e349688c36aa4a79ed7d"}, + {"0000000000000000000000000000000000000000000000000000000000001f79", "00f6991579e04d275ca2d08fc515e7943221be7d830167139c0e508f1d2489987696defcca448e797b0a0fefc8b4e6f6d523e2d386e139710331f9c7569379544a0fff4be6a781fc2315da0bdcd7a2b8217b08cb01aef8b1a4411cd83cd4e1d04b5bac697db07237d30345d7589f4d09dacf7192f1364f682a6706dece8307ec4529d2c43f49ee7d82bcf8bf5ff66d7b3948f03a920a8e1b9dcce7009103f9b2779d6d7b36fe6f7d01a331a01b604a3576db55058ac55b55511e14a6c2218bb9e1fd126960f66a435419d0e83bc2cc3e7c1241e40a47bcdfdc51429e1588b96ce9472f23db2834798d44166d9f27aff7da6a52d6e55aca9a3039015111bdf1870bd9f7318832d7004d78ca6f9e0e9d08d014a051c601d82fb1556012cc27f7c678c8ed934db94c27fd5588e2ba77f59187a0316112a702dbde68bc61b144ca709fa2e51cc158c84ec75a5a52b8981aba0124441c59c712730018597160e1a09b12a95b684d41e6ea420de073679ba175888170a6519da253cafc10e726780fcfa84a7ee3a4319de48449a332b5fb073f701505f023be85f38945dbc6db4ac6f0871d4ddd096d82fa478725290d71c21101fef22b6ffe5dd0122b83cd9a5515293aecd758d6b0cb312b3462dfed3c173b5fb3f9c884b59467d18dcb64e110b2df2684c8184f50ff90c86729d582977cf5cd7f6b6cbc7f269203513cc581449e2a471883f654d8bdaa08fc78c2e54f91bb33e09f2b1dac8727ad267fb6ba0c86f0d9be0863e73f29c60a30832969844c736cfedb07dd0812176894809750d5cba449e64640728bddf5ee9663df0374440679f7c08fc94020fc069519034e87bdb34819e4a527ab912b0a939934307cec8d75f47db2b44a09f3a78f4dde50f327f9418727a90421d881f727e4194bfe37504c9232d5f0b19a0031e870d815ee050c024d948b72850a8ed011187697da28ceb917d9186c13c5bbab72c6483a9c1c2141c21712332272dccb0715fd07795acfd113bc02f4d98caaa21e78a1f6d772300d4e24b36d5f2b8b5773fc75f6328b4e859d34e003253220f0dc3aff6d4bd9e7587c391f4fdf5ab9b911b0b1e648854d190d978793617904aa7ef598317608af80c8a68b48bdb06fb34b56e62c59d11c7b8979444c740279a7f595b31e94de7f53b84e4a3cdf657d02fd4a2fb46175ed211ca4d35a53dd5ac68a1e8bc72ac6b6c8515d6e279bd172efe5aaf461e8b1b1c2591a863cc1f9cfad278c6a754375b66abea8941837b427c71ad42d8d8279a55a351adc605a4d5866fbc64c0a96e115cc1e562f009fd209ed56cc18c35cd1b9f31e766a4f8318a89ff566874fe76927ba08c1dc040316380547ee9adc01ebf0871116d5ac66204d5b64243584effcc75af2f19f8cf4b5f8d66efe3cb3b3da3a06d2d69c3005da0bfb9920bddfe31f33759efeceb1297ce4ad27910b2d944543adb1b6223d53879ecfb913e3742950119dafe046d4e276aaf47b0a527b436c1f4ceddec948c586ecff02ccbbe864cadfd1f915fa121a6482100739404492714c750ebeef9738df5ce275b754bd94b22c95a1f5b8a00c721f1333ef7e0f8c27bb02607e6dab4b9762a3ac6e720b9d1fcdefd26a41fd056d8cd904d97753a4d467c2c95289711c39de11d818ece68c7fecfbc4e257ca3621d5100975c6351e8997ee738b07414f8e05038b491fda730415afa21b3edf908bd1c0d1466e02c378a5f9ba47a2dee4231df3f6a83ee74f3fd69815c92fe60136e9c69d8f1426f41b9052a9750fcc2702b856fbd432fc9118c1af423033e88aa5875be07a644f8af56da1bf7bd4011f43c7cb9c3eac8257ad1296267b3d6a62170efbc3ae5fd34d2a0de61d6bcc087616ffd050d779005e835b"}, + {"0000000000000000000000000000000000000000000000000000000000000740", "000826c9962434e1e9fcd316b94ad3ce4653987b8a2c453c396496244da84c642b08e2ea1db483919ac31a60c384d1e0dc2db7e7132d3c4a668195dc7b2095376b8b3874a5907da77a45a3e74d6d4e3ff33865950d8e7a5d07d5cc7b7e0a4196612482fd76ec8c57051002f0dceae534efc5fcb3136cf03d54ea0c6922082316d2752bd52f3ccfe536556de964a6748d777c112f8d06461fe6e9d3b0640b04c575287edd94bc9de00262bdf99100fb4165ec974a53cbdff37aba5d4d9f0534bee78385c86c9cf9b6ae543852ae61237881fa0a80724227ce4d2d62fea2746756a3ad6ff1b5496621e437c4ded4f37dc822d2e93e6928f6488153f1fd0f432e28c0dbc1e3753b7226902abd34c81a7ea40a54b9d6418b595be13435a6a74b6cf8eaec5c1cab2a207be43c8eb2abf5f0d1346dcbf9bd7a7e4696112d4b1afac53b9e4229aa192785dbf348864aa97a07db05904a4a17a43319d762337ccd6fe1fde9fd7125c550704b4b1cdd51af7272a69fd864af9ae73dbe2c6206a2b77c7c0ad8969ec5a07d75b30b3fe2d51f781a22626ab279532e5f0a1558f43fcbad3f3b5a7c2d06146ff7ab474bb96bc02291e9c5453298ecdead260528cc8605ee9a34abfe7fc5f239cac1eda0fc6f07501e455b0005d8d3012802f2288fa478ccc0142769d428df1c99b358b1a0f8a4d5c193b3ddc74237fc6c3c09fae4546fac7821ee460233342b6010b0ba46e8e94866438076b1db2fc2515522933420b2787e3bb0160c5261113e124625dc0233dac6639e3dde9d140b102b94a7889be1e7472049c2dd30d99d82aed136df100a5484019ae02e81e4ca40e723098552b630fe10da3ec4f5377838fb89f69de4cf054cf46599083d31ac0ca0c54ab53b3bdde4c22120a45b3c7223e69b280a187bbd13470b5a87a9eeb6e9575711071b5b1d654100b4101abd217a3bb53612f051e233dd9ab2f433f571a01c47e0223e2bcd62c773fbef41f5f029b2ee910a5a720665b13f6fc5149a439bd3904edcc93a48e80f3d170bbad31fa6a100179a2ceafcdedfc298ae1e0731f2312d90f8eb452cd6606841b6dea8edd5964620152115a01fb39d75c594b89bab66e62a20f21c9037a28bf3c8b63503ecf5e62391b5e98dcf2ed6e17441665df5aa575168ce10d4202569f19a8ee33751a900c8feb4f248a8851809a01496efd411473353858118e0f34ae49eb8155c8c03b1ab3da5e9091d7934500fbf50d548165676c462499b395f356704eedfe982383e0487225fe949ac3c457626b9a6123afeb8f27710c7d668cacdf3c123c5e44865a3af25db6a76b0391ec3f4c8a34fb7d71d2d5353fd6ef36d29b04a1f9f225db21be6cac3c30a5c126b9aa399fe550a3875475b9ab44afa5bb6ada80c05c55e3e1cbd9c46352cbd01a47467032fe367fe86013adca77b90a2b6cb087c210bc98b8fe69211542523a545f51767787cbc67aa07baa2b1ecc2ebff1aaa343649a7bf1a3bcb1ed67709574155eb95d2737b68b1066917f9e4e41f4c79680a26409509cb7e83210370bf3a73f6145c0c3aa2500ff562217b98407bcc6e82b61d7e136231743b5e421c3a32a645dcc0f34db3545383410231e26ab8174172e89ecc38b72cabe716e7e04e657b463b0b3a259603cbc4164125b5f5fb4080e76007f744b8aa3ceb1b1c627500cdfa85f1dcce523760799e754a021cd93504ff43ab0ec50f590b7fb72643f2cba245801802212a636c9a885765cba910b42dcb5e3ede4ec472e9870998553db1572273c7e9937e4332288d3f305f069d37bc5fb296cee00ea6ff7673357ad646fa757860151a0ac7e3a724b9fd500f43d66ecd39aea9d439ff785cccbdd8d95d7b573a5975fe1ddd0b0216d1195eee"}, + {"0000000000000000000000000000000000000000000000000000000000000446", "0038c83412c1c6ed2f265365b0c4148933bd763ef902fd42cd6ac7b6eb1f91b4c35d46a405b3a8599e3c091b0f8844f32a57adcfc1085d40032cfca2f4c0d51d54d54de1e392712e8f53b71de61e1183791ef20d0c22a3d7da6864c59719d4dc7179a98789093d53000dc4272b43c89f7cbd93a34eb8c18c419bf3b49c8a1ebaca79c81883855c37d8021379e096445ddfdf0b1f8b8ea1f8d51b497f68f81530d8ab5708f7b9718006621e727aa838d35d9bb39c58c4edb29e67dcf2400e4732c2e6448c4f925eb50d9fd6546226bbb7223e102c0b996b52b9b77560c5c4567950adadee9dbd932d3256ca501c3a96f915e52840d54ef2e35dba44640dcbdf0aaa128d0d9835b4669b24774630b519304312775f1fe8606897d1ea04ee6648f52e32f83f2eab2c1d6af8bb4c7848eb5522d0a3edcf2721e85bed383a185c3ea191e151c3beb3ce7630c431e6fe1e03a30440e407f2d62b10be8db18827d10a3cc7ca0d62ff0f539ee7290fae4b658e929585bd304991009cc76409ad4d91760fea2190daf4d5f9e16cb1a69c14a27f40e3ecc522ee5995b3b644fe0dbd8b262b735241aa2291b70f971049ebf10b148c82a60d2649a3b35fb4670fddd1ff333787ca25694b25f35b8ebe2c5f2ea22aa093f19e4cf7b6739462ded13a92c387b8fdefb031f0b647df5a79e3a10125deb4ebc585bd13d56aea0d27c7cb43c85e6ba89af1eb1974e80172dfd239ef2370237a3afaf4b9edfb032bc6d7b65577bcb005620ee09232960fbbbd36564444f9e3d636768c3572484f00078abbe83b198e12d69fe7460769e9fff5a37e12592271fb48482f0c47e1797795209ac81fb8a56d254a26a4880a3e35090a12705e3fca8d2fc75cad4c1df64466923482bfb838248948518015b89b762cda2f17dd01de9822d50e90493272704b4ea3b6da42d2019b1f9f5440bb00fcb841248573c57dced8faa6e01a16ab61fea0a2e3142be46fcb5bf32a0e913fa9710982ba0d434aa91fd916c3db14e8981e03ab55da8c3cf9fcd06e973e595fcff5389af324b3375bf9f8f90e0b28ebf84bc0f27941d15eaeecf250b88f1509a1301ed55770650f27dcab739026bef63f18bdbfa2d12f3882ed3ea97bc5eb24c43a6fdd3e79a3383410cb30bbda41153b39bdda48d447077c959b282bdc57ce0ef61b9412c5c0d28d43618a325a608b979d5fa6941772e730bac8447d0861e6f611b9252326471aa8c42cce722cb99d06234e97f2dc30f2b1e9ea9a3e4c984310243f045dee05bc27999dcc4e0cc6a35f777577115db1b299c7355f169882a394f7ebf8afce6f5ffd31f81d8e2e5d5c87fb6e46cbecc7de6a2d0592ce7b1904bb82668f66347de7122cfa9e0dd1e23dd16537383b1c2abfd8ae232d9575875c786c9d97db53609c0219c0fafd43b552439a326438b2e43729c95dd52f1416d3e63f055dd1e05572bf08aca6b2e92c3dc6730a99d6bc2257f68f68bde4b825bc597f24873d94322100b43673962055d87e9796f0cdbee7a6c9df0bcf0dbc7e3d501c9e2faa2dd5566f716bd69dba962a1d6441fff72b308361c673d9ba9c761a5a923e154df2167be1366e22dcfba626d2ca2743eaaa514396710d351f364e4764adcdffdc4a718de184b774af7fc6fc03cef4f47f42b4f053d7057b1ad3017e8c86fd3bb70a4cf8c120777db5ec74c354e4b6f6aa1d9e7aa0550eb21aef9b4560aef0c9136fdfdca2057d25f7cac551dc461bbdd64546bc9176224047ffaf3cdc7c10f808c92fa6e8564f14c123b5a8d943f091e42dbd6700139db2217b5ae3afa85902f4e43acbf9c3b4533c2d15abded1388ac50cf170d22af9c2fe3af0257b356a2bd00a3921910d93eab865c38449a7de08c5bb5e2e"}, + {"0000000000000000000000000000000000000000000000000000000000001acf", "0009f462ca78233ff20884f64fd05745882b5a5ede05ed5ad08441a665d2b3e7c57e7ea76681e9b9c4bf1b4c32810d1de15f9c020416dcf342d379b19f92f226110a3fbb1a9e0d8e34a465a5ee4171e6f6545d0d05819f26f9f8ecf3cf017140ac72dedda5b5baceb518042c917c5b9af3b32cb4dbdbf58056f1473e29a620a5a16784a3ba91906c4303bdd867d64026b5c99527320f31ae11ac27ba76941e30454d81a8c3be79a1031be5f1f1e86535e5db37387271b97fbf2efe8b3b0be4a9bdeaaa3fab5cced289f6dd8b6135e09f68f2119c6487828bf38f34921434cec165a310fe5c9176361542f99b8f99c17aa5251fd5429ab5965eee348e048ce7d29cc25faf280e61736c5fa7955e2539163b0b2ceea04f50d211896e929fcc40b008f875890ab614521f401c18278f3202ba86347f8186f2afb94a9c4955dd3c332780e553e744b743c99439ffdbf10a0005c57c2911502d1db8d081abaed66fe134030dbd041b003fec634faafb0cac33aba4b1a06211a9b305c40e9b6fc53886aef64daf730f2c65e084f79af785703843e34db9ae688fb593e3867c4b170a3911f30dae26ac43e4fb0c72d1623d633796d0fed3047ff9adc837c9a333221d2dd95f66a4d958ebf4ab0558bc80aa27e9047ba7d47394c0a7b41bfeec973995cb71b1885f312e55bca307fd19a367467f7a677e91b4bd31ef0966836abb482f9dd2b26361417adf81b8d2fa50d20b2286f4b4d68683aa9e51e621d67c7ab7f9b83af30ad111bdec878b10c3b8057b2ae3156db9a8bba8ae2f6c22566deb512d8387a742156385c60c8ef7b2671777430671d81389e733d36d81dbc158e605aa1d3a3d6e22a9426367b36e29491651d233bade9afc9a3f185ad116f88ad7050958f77865e8a551e1f6d730ee28c68b097214f05d81c1061d94e740c70f819e301601e772da31c212e0ac76044981adaf0638c338e3b92fe08db5c62f89ffbe84572b6cc01d37411dbce82f03f8f7ed2c46623d6506d3ee32fe4b8ea65718257c1b44e454b5a85b87efb3f5a638c296d1a899ff0ffa02abee93b143fea1c076c1336b13d77484ed98686542f0a22c0e12bdf3b33ea43d9f7a14a1e1d07088de1147f565e610f34566dc43896b799b1723cd5f333a1b8271a90328d0c5a995b7c10cd99dd22b6d3edb790ae49df217456b01e98bf46a80d2d79d2996ef16731566440c049a672be9777617217de2663585dadc8e122c45de855ef056fa39e44b91aa90599f772f65d91e28565e91ca851e82b1d4bb3bcb424b17c1daa8ab26fd77e61bae4a41a3ab1834987086aaae7c3a92ba3a95d221eb92cad54b7a84de1af7feb65e77d487504a2f4277ff688ff15ecd74b8b8d1074d7fc52f18597003fba3a42ee477fc2ea94d48d90a7fe2c73f7f9e027613b82d798c1fe712a07a1867303cda1e6cfd0a155962b2e1f09a57abafc51448c64519d903161e200c05db048b1b3ecd979ec2c9cfcc6e0d27141ec2030deb4708f51edf95670363cb9c408cf29c2d383632050e5e236452d5c1915ce470e256eb420af0d8e3990b537e6ef5c55554ba42d58392d831c25e519593260ee6ee23df0bbe43938357fa036650fa878c7e756d1e631521fa9a6969bde925d2a661f29f94d91dbaa6050ef67eb10e87a6c2953ac1647ac32f16767c135106dffcaa200206ca688e90f88c71ebd9b3dd1ab5aa3667a5f1ee95b2e52309053388333c7f273cfda27c7097e5dac5a329b1705a977c1077747a23f7b5a17509400f2f7554d8b71a091497e3511265782a70e375158399ded56e8c23918e718b43b67b9d01a2b30ef90b5707494d88246732d8e1b63e1f37f78c01de0fdc21ba748a745fe889a210e7d6f0fa53c2f4f299a515"}, + {"0000000000000000000000000000000000000000000000000000000000002cde", "00741514f6ce500f3b73702f3b6f4af44981aa913d125e6bb3c9d982792e40940708a5d859eef1faf49e247c69c8ba8eca7fd428e468fbcb7651f304f7481a307469f91d0f6a717d8223dca25eb649067ced99af0ab36a53cb075d9c738ce10fba5a34112e2194eb043f9877e03c52c710d48d4868a5dc9026815e5526c50c86ad659edd6c92f32886001cc3f9f5beed7732d122b7c79f294d5c5fdec5368555fd5f7e0dbe3efd9504d539437390fb81a8c281a7992ddc166b53d80a7920b657cdeff507cdca3bc377a8d6c981b7af1d2fb10788a7e8837484cba813d164774c23b5ded8771d910a4100cc4fda3f59f477417573b658e3d2917f5a8006fafc113f9c718968bc11c9f5d57c51c939af54ad1555bcdbe2968543b84bd33fe11feba128077b9e110a16d403ac32e039cca0e33f55ee9aa51024b561464880ea5ba75deaef992066a969e7c3b9fe96d45c7303437545e80bd0f52fb321e00eb327d1c067996c4916bbd11b3cc7e772eed761a5525261d975a67d3c691e0a56d0645b5ca58ddff6c1c6fbcf36871938dfa33299b21b6d564c56ee6b9801c341161ed373fdc0c90a1ce67091534804b03588f68dcba2f716685c05c111feb20d8fa64f17d312c675a7b7defa4b209b1d99125b3c41e66a26a9acc0718b851237d1ab6913a0c8175152e840557f6ddee6a42385bc5525323898c00303ff5c087dd267ab61cca14f7d743cd896c6e7b11714518f0b39571a34e2f5b3cc5cd0352701b47fb6ff0a78f19dd510f80510dc453cc72f7c91bf115a9c79523a27af019af81afe0a96fc57f4671a82c8bb14eb225d460fcadb52ad784d12c6c39e5e1535d82ce4222984b98121e34ae11d7303f35baef7961042d96f85279c1a79bba1da1ffa5e52de45a87f78e94bad9e876a6ba4884b692d61d82e2d98e67e2e438281dc34fe025959ca2b8efba9fe5721b4bac0bb8cf8f852e9141bf22fd0a2fad1abf3f6d3f7409fdd66639e13bca50fc4a9ab2561879b2fc7411d00efbeb08e1d7dbedb141af27fbf5fc569a289c2292d54e7db695a5e6d5707c4fd6975652d5f4409f0917c449ac0ccc5ea046f2509397d37741215ba4a22b33a9c51d22734fd457314f712270d19bd70e835a16e887edda146585165d29947478067e9ee9b7a9d9b73dd744e2741cb1e619604faa0463ad451a8b9a32608aa7fd085e3c4146ecc248c0cc95f91401c95cc8385caf76bbd280c7c70470c434bb3c54978abc826a25d332f7832e3f2fb1e912671a33470511e64dec4a59747540ea99bfb1661300948a0a444134c5df9c83c57dfecc2771a113b9ddc16606a35db07603e4b4634d19b40f9c6744e1e3d400ba94e00d7a49853892af4f80828cf69b209de441e116ad4c6aa98183b9032712203da5fe8983e1c729902df19e240931e7ed351666dde53e8738fb35f6ff714e633739738133be8ce42e5a7cee102eb3b1f1d580a2380597498403d87c09502c9f0ad9a00637f22a216a041c56e22d7197435216d6654072e2de9fe28f4145285ce7563102b1d7181eca8b1cd9146a0d7db657169a64e5aa276bd2dd45b8ad07daf4fc5cf7ea4171e2a3685430cf268913f6634fadb46ff316a5d971d1ffdef6dc1dc49bd5f00a3edd3c6f601a08ff8b97506fe6963fecf5ef6e1a5a17c79aa3a81a8df2fa36c2ed8c52bf74c2925b300d37a0aa23d0af448bbc937072abe0faa156bfd1d0d3119f820891dcdcc77f3430f346e18a8ce2802837818487269d06f06553e594a0e03e2d826d1a517832e43345affe3a9a268adc0c419fff47465e2e22de3830326fbacaecd45aa2e6b00217e2407c9e8712f6d6d36c54b441b028f175a67382256dc449b4bf4f15bf783543a61b2fefea51a6b12"}, + {"0000000000000000000000000000000000000000000000000000000000000c02", "0011831938eaf1a9e97f16a88be6314703d37dcf55134434fd4dd5a2b9c878ebd8f5ee9d6b6d601f436f1efeb99de8b29ba7b72203991eb4ee76d914da195a3b1bcd9324908403d39b451ad0299db32f3ff9c0e30686e59d8844e719e754634b2a5956d75a197e5b2f11fe6fcb933622c5be5286697f7899b1ff2dfc33dc08934f60d9926bbdd3123a5ba96551b724875a2efa2f398f416210eb353353e4245748765d68a54ed59a024560a55c580a7746d620ac009f1fbdd7466fa2e3290135b42298aefb539cb4458e4eda692f64eae3c00d1b83bd2ad6ec21936ca2fff2779c4338a45f61fb1a1c535ee2a15b25b280f28ad9e8131d643c5dd84e052b8ff191890a33369f51fd05f7b88ec1f1bf1cb11d17391018a28d816203e265f3c09ead17c11484a30fc1594ac8ceee10c65d63a257ff29b67a153730e311cabc525e07a85f3870c798f9dd970285cd3bf6d401169477f6f642e5dbf491907d9273d163cd2d13e10134fdd3f5a569b942323323181dca6e1feb79c9bc197dc5e6d2988e53c6b933888361054929ec70515e3cd0ce2be9de91375001c4a083ddf522e2897900c0107633003855a8cfeb7702c4a5543d0aad2a5f74bd65501c9f9da9b98da86486f684fdd57275a59e11b71158b8b24f866af2f45f3398f938a4a911cffcc0d04a4eac2296e3ec23d7485673d858709ac4d35ff92e0405ba7c56983d117b2a13aa4bf525ccef9169ac8f0f15953f710a47a31c0fb276259f3efb7ddcbe65031489a449e227b1e3a60936fd2a4bef7f143adbf8e934a02769c04d47f3089f034bb7f1d1d1207d97cdd2050a65febd854fe3292fb1752545f8c8b5203f062d0d6784bd2716a9c17e0c3185e219cd7924c8d9e42d1ff4f5d04f4b7cac6d06d39f7de591c59b2bdba7b446ea06753b9487f1624874f38a6b7e2a0703ff6a9900f59966f25c48e18c7131c5d0610180f246be177315191ead455b3079e2069b7724f7129ee20abaa54c030634b9768922508962f5ad99d368b16e323bc8b819df3e2500c68053e39fe1d119af8eb495dc517b9604d89e529ca18c0d3b88771f3670cc2ab2e6b6612736579d6a1c11b59116d495c9c1d2e382d4643de9210dff556fb586c3e1f3954102f29ca98cce1b6e818327736b58611fda5b5c2222d7be595fb54b2710d0d3025d9995ea9a9653d6e270e9880e73d46878a61fe91ec354335b8b63bbc8a2e4dc3f5ae4ca2bb419ec3f0c5b0b490ac3a33556ca54295a6bc5d712637dc759255912e5e1dec61f277c25c467ea9661ed305ec1bc0332bdd11cc5affd5c543358ada3f05de301fe927a2bd39abd8efd67bdef3fb50134ae1ef5809750e90e0be354f9b4a6b3b1e10817b7addd8772b4e67c0d4d2e97697945e8cd5fc97a33b2c4543ae923a3f7147c0682863ba6c4042681c364a3d2f48881a60713907d08eb6709c0ad236fe3477463f645651d8cc01290543172cdcdf78ca1dd0794a65383779735a18e0f5ced50904ce8b5dd396d6223a55f5c4bae81ebc9f4435809fd316324f4dd41e7aa6284a2605fc8b25e1d022c23ca4e3e71687f97a0be12ac747adcd990b6be98ba4b23ba59a8d8dcc7b8168838c44d394e85c136cd90514c9fff3ba0acf9a4f386b0e550d65a2047d81f0808655eed74096d76dde97404046576f9b63fba39b80e0aa611ef0b8cd941ba550632530c12681fd78afa21a55f6c87d1f9b16581437b7fd8840d2a832a8f9a4eea8ee554a214df964b9814e86f7d9ac7893844770bfde61d0f8fc59edbd2034c202bef3ad7a456bef52e7bea031b9259b8f73ce70500673525c364775f4829601ee4860f4c67946762a16cb55b8988aaed0f51667f2fe92c5d8be7d82696eeaffb572ea5d9d9b18f"}, + {"00000000000000000000000000000000000000000000000000000000000001a9", "00a2347969aa6a6fad52c0ae9834f9206181046da70d468516d7ceb5d3a4bd49f241d11bc6ba72b7bc0e01ef3149522f3f11ec5a004df2e8778298e6b87ae20e72dd1fce2fef7faadcd22623552e29825eee7fd806807efd37c263752d9b65c26f4377b691dbf6cea909561643f53855d7cb2aa3b96b2c8f4122802c5e3d25cf81a733eb17edf974b42ae9298cd9dc7af3f08d3a841f8059593f57af32496f2b692b0e967a7f48f1070e7f9cca8dde0abde6a1acfc1274d164f59c228e27955ca1605159c152846339179b861fc1839f0d4d1618166649def6b7996f03258dc20681b19dd6a3fb1a23d77acdce4ebbba922627def77ebd9fc252866f153787f577e67673446615a82c2f25be2d3934d19d2ed79b8370db2911eb16f97161d44cead7de783f731a5ce172a28dff8562852483dca4e93ea8c75a11941ce1924ffecea92cb631620fa1e45239ace0b6a49001f1927d03c36db98696e14ea3cff9acfff95f0aae073c1c70a0c840576900e9aaa0578bdea6b536d00606bfbf66c4a68f2568bf1224366ed1dcf274cc6c3527d1cac5a449ff61b8a088212fc8b426366ddd772c1799dbdcf5c7d3bff23703fc02fc4959a39f3f11ec56b13ad1de5667ab64737769c9d11ae29d11bb87832233fe7f822507d7aed6632aa77e5110e7a4cc75173fd2871eeed6e8f6b766d769e0550426929637d7f505010afae1d9f51b4b05d11a7f8f5df671233f6e2e0baa1b41698b1d196f84a53976d8cca55ae275986209591b88329f0643989960edbcf8254132d5ff75340a757c43cd92a216fc4f829e77e4f07533667a470705c4fb3e71534e63e54a18beebdfed6f8b9b7f6bbf709a0ed9a3e2fdf5b57b9a9f77df851abf42fdddc81c133c4b83ae5d5b8eebc3254eafa1f70143fc4716725af501e62650314844b9180b59478665d35671770495b159062145fb0ee6658e743b7f51ff385430c0158b999f9ae43a512499e6118b6c5fe250d5dcc1390a418fe46185975e868c11bf5bc9fa14aaa96783662d933fb426e0dce7b4e9451b9160bc315aec9534e01adcb6056b0fba39138b35caa8e32dc1b902d7d7914ed17f207ba48efdb6c8d745eee42e7b05077974f525093d81856101796e1d476c60d7d5fec85416dc202fdc6e66bd7099cb850f73a595b4fdee9f3a5d2bf904db02342a9dabd9c29c7a3e147992b6d5b87a4f280d1525f18133605de2cd54394ce9d54ddc25f7f0b106d8241175293a0ff54fda843e6df6f6c714fc5be639d3fed4e5297f4368c375856dba62b6a8df75a0a9075619be68426584307174d2fdccb8b55a27bbccb523fa2ed251275877b0342420827afe1a3c043d43841f83c1d827a1f4c9630bf4fcd44906feb6e9798359228117669e5856696908338801b528fd7d099bf9a004ce7a5b218797a2f99d5518d82cb055829e6ebe67228f7591a36ba4ebddc71607a0d221871aba1da06a1482e99a83c65849a9f2a5b9b04977ee6e7e7be7fe39794a0185522ce71b22150273da9aa6b4ba9d499a0ea7bd21521470dab00e249a69bce5b5cecf6fb73b6e5245df115efab1ca2899ec70fb5ec2c4669d68e212a20fe694ed4fddb5ff018dad9fc491afc4bd33c21dce6ed253477d3d29b01280d5c8289cffbe3ef1ca0ab0ad4b4a9f48b79d894185e7cd8dc076957dd3551723d25aab14fae7996072fd5d5ba600bfc2368f7015e8b61ed7310297b0c642e83dfaf9729cae9fed9d3bd4bd58be577b25b1bac4bbbbd5c12a37bb5cd2f7122887db161d7c7344178572cd4f0ef5731ad7fe1235e44b7e2425ca87515a88be6bcdb1c6d0d57f58932f33e7833cf34007a15bd44c2b38ab8a839d984492760a9f61582a9f4d710d379e884fa9ca962eb9821b"}, + {"0000000000000000000000000000000000000000000000000000000000000a67", "008c15305e21b0c9f5ba80ff6b5b1e89087b334a922b9a8980e6cd2ba94034d7ef86414a32857d9acc8312522c1c581cc745e12bc26a825376a5fdf2936d5f986e55f21e69dd2ddbdc3c377e6631d35b721e8f8d0e292a73d8893ce2fd8ad1dc2b7dcb84bef4bef5821dab3467d4d9dc790b4782aab242a7d54780ee72d71f2aaa2aac8a98f8cf9833754f3a9ab5bc5f1ae3d75a836cf596278ba551415893f7e3ffb731465e0ca0011f10a1e9a4cf575abbf308686a31426c5db875d925d38565a99e7af10fd37811a368394672829e52d0140e3dcf1509f09f58a794cf3cab1f15bcae5d1ac815c4fa15a9727a179ac5b52c442ebf53b3525dff0602b800ce1525f873602c09a748fb603b6882fe1a4b185c1230c06cc32dbed8923e53c889736e669c9d5f1635fd05bcc705a08afe52841bf03ca25ddc34f0162d81328f72fce6b3edb6e351babbc25d38271a495301a46d63b6083eb45915533a8eb787798ec455faf762067da3fe1f23279928c789694b36ae1ddede676704caf55f6163c2176b2b23b9e52fe86a0a6e968caa1c28b98374503aa7d91504156023f4cf58133d47bd06901402ee03121c822971185dd21c6925855371a349b73e69c8ddc1ebf09a251bae709692e6c7bbdc2e088f776f08f5fd19b8be05d65c6f2055b3a7742e834db02f5a8ae4f927be7ab706cdc3a04ac7063e6c1a076e0eb087da39a3a4c0f535abd4b1833aec3fa1a71528e8fa13a21ed37b2c023b6fa41cdd044a49a813155240b8c10e3db4f432b2f14a337185ba6418735755be02d7a119f919163496e8d4420f526b589a9af607be09ca064a7ed7c844a4cba9d1087fca63fe61f90cb2a347e84b483ea85943154535047b2a119ea8381a6d9d24b947b90334433665a2cb1d61dcb450a9112e0e87d5792b4dc778c912f6ba3fac1ab588d7d27f00d2132fb287238b2830d03b49b3dd431fe199cf8d0311caddebcac48e9cb6b17af51686c51466bdf5c7016a342b25659a8dae1035f4725443db54fcbec0250e2a6e6611ef0bf7a991e446c040414b55b01c438701e9f517460cff9307c4f13a4111aee8ba0677f88c1fb4872b6c8ccd508fbcb77746bdaa9a7b193729da2a74bbde2e509f7aee0609b929ea2fcb0794391c3a2a7b52ff631a1a8749eb07d77c5a71cb2edb398c9a03a5a97db1d704e2c70dd170e855fbd1fdaf1d6dcf08eb0dfe6435e9c9fd0f239825e28fbf1eef5e7d2822632d33c8296fa39c2886edf44da47a23783677eb3268fa11a6934f234bf078268558933ba1187f9870041948625a1acd2d52007a6dbad8e2aa9eb0bc095406d55ae6718c34612e9de4f7f03404d959e47243b40b250a22321e6f3926a000bbf8f962d17bef71e4c53966b5d97d175a4f29aa2606ec6f443e51723a7ff902182c6bd7d1a389b147f81c8ffe6d0218e45d557502fff70a0903eb756aa3c7ca31c1da4e61f413d8a10f5950dcde5ea75918655226985dd0a26ef655834f1f0bd74a0f8daf44e7aa73b5cd446a15db981ef54d06e1c2103f9cec27b11b942b47cb8b6debcf9571a70dd092fc020dd26f8d87330325ded8d516000af905451b665d4ea4fa3fb334972a513b820633defba5e94ce2dac83c5f81216297d8a00be3e92377a7dff43005716f47eac7c5775d76139a0762b2564d46fbb87e09fafb52e590a2228938f31167f7d550ed1dc81552083b91af92daa049758a922be0e0dcfed9f45e82551d5602acd99bc482e30c826a77ba3936c89ff898a02756bb7937bd1e83ea84b54ca44fe68684a77eea1a4e1e256a10946b2581d0a58287dc6511e3b093a4d332171a93118ce1eb6728d3ce8b59b0bdabe2145fa44d83c7dc691599d2f563d6b26c55b665b25b14b4e3"}, + {"000000000000000000000000000000000000000000000000000000000000058c", "00d573d507a5ec9dfc08c6503de2957f2f5c397b20108440a8194e2b9f3d07e7d18a49db02bcda1a9a4b1780f3aa648ec3e1138e8362ffddded11f79abd77b5b53e335b6999e14d314685fc5e3a6fe7bbc980266055819067b0912faa31ce3173a6395fcd6ea2a75c23b533dc123d45be7303c14219fcdd7a50ee1772421067c535d4101d21d443ac07213577ea495165368ce1b5ddce2fe9a4acfdc1ca1b7e6311ff52b1b5cb35503f03d5bbe6dcb397aa428b91fc9a2ca65b25d34d32c24c53fea73c18ba3d4b39b691d05f4f2c64e003b0ecc2769f6d70a61a79ec34b2d43eb81807ad13dd7469a74cfa398b7334b2a45eef2469b92b6d5bdd5530fad3b4a20d86675278473ed9cf167c1c0ae3f6ed22bff658b9fd90e12fd7aa2e33bb2c141452f99ea2718fc7f5487d1c711df3491e3bb2c86896336cf032946f79d5d491278bbbc1bd47c51b6b96f2e1bfa938703325e001c8dc9955759d093efc46bc598a8138f991513674b3910bd9d50a6e3bf2a593bc5fd6bf17c3408ef6b9c9246541bf08fc1924ad309f2bbe35a83e87d4ccd4b78628ffbcd6927ee5566308255205edf0b0a0832d3d3348ae9c0e3e29dca9d00ce855c7c340e4a2e5ea08a65290169ff39a71bd66f72c27878dd8f19170e1e7d4fd7e594f624e517c7112fb8a69e393d3061631252163ca3f09f331d1dde35bd9c7eb46a85052a436d319753e35f3ff714c2f223ea0a4a1f468614dbce6770578b6ddc447454ccf4b29d7089b4e5cb0d043bc1d7c733a996bd63c1074fefd9df78761a4b4097bb105259374321618446c6ce61e30a885a64310c846338a62171cb8951c245186cf71ce4eb146784273aba853697fd877c31a306c7e84fb515bab4d184190146ddef8f6a899f6187f7df6b2ab2c76cf873ff3a5ded74e71873654cefb46c7136161585481837c1027133b47f3032afed5441bf04ed1bf216e635b32607cc87b1e155f7efe62e6267d0184319130e4c7d3f072f83d65aec47fba755d4d68f3520febe699a40cb1d5a0cd015985ffbf105331718e98678f74f2d4001039d1cb1bfdc67c7ad464283c8dbf64945dd2eaacb33ff6c875fce6e08b091b61800f77e3ea2303a987f19627ae9831085bb57ab6839feeea523039f3fbef0287361bec516d115ec7d84b4617daa7e5a72972edd032f0ded2587656dc110b04f689c724d0f1e3466ca2098461c090be1269b1642877661927554e6fe076a190ee5474312a60de2e82416df45d633aaef9f198f1e6e97bb4f585e61aec1822d4816bdfd3d7c3318ad03af2f9844e78a17a4fdd2df515a4695085a4f79bb1f7e75dd1e664e4fdfdd72db8b68def2cd449d1ea50bb16f5ef7caaa59ad99918e847dcec0c99947d36514d366e3d450baf17b2a42cabb2aa230daaf15f8c403eaeb675e49b98c8647512d919e170341e4bb02d023da2172b72c4315d86e34cf7eb85b8936e36b2c320dc69207200da8167854a2494343577ceed091a0c62ab95261cfd93b09cf4485304332c18167c59c55820ee2d952fed1db0fa91a186334f687429cec1a44c610f2d8f354c7cdaf9d8b0413993fb5f18e1bef46411a324e3e302a5d1fa94345f729fc34859113959fc24ad0b26163f007699db4869865505f71e0127fe965066183a84ca5b8056140127f1c1eeaf2ce97dc031810ef3347df99322916aa468b0e3df6135e6e1c25bb200563ace7e15d25ab0623e67ea0fe7b3ccb7e8e443ae6de4443949285bc40e8aeb1d50952e92f7863b20a0acfa4d9b465f9c3750124c764f08c8f542ff4f5348ac25311e938b1a2147840095cfc6251f1f2b2a20fd7857f290d93d290d4f5df88f16755ad0c716d2d146196ada14ae30af46ee59294eba25da451bf8a7a"}, + {"000000000000000000000000000000000000000000000000000000000000002d", "01161e7cfc893d838060b6af2c6832469bb8776723123dcb3b59e41b85432ba5d4dc67206673047f8d0d29e24cf7c81a6cc51461b2c6162e300b32d5fdb6d23abb24d43cddf452f7b5d876f4ff2062f1bb59df2805436cd3f829ec515d7db14cae7a2aed185d6b84b334b64350be518641406b0381a4af4445fcd61789dd1b3e297aef94bcffdd3c4286dd28dbe1dc0078c3e74822e55ffb5216bcff1685d7ef680539acbc93b6b301d37c0f610e90a3b514420d8e6cb769770f8bd0dc194ca195ff479251b288c1b78fb65d2123ba98eccd09abfc69a3e255fbaa3573683c2951212ef1ee27f61f114939776fb46fae5c67445cecf52629267cf46a09c2870340df67e55f1180a60a6b9bcebcad7d7b561683365eac4eb7a5b28b65f1f76075b22c29d82a5017376bd0661f46095fc799460ddc748add2b1b83a139baaec65ea86907496147081b7f08120b78149e9c017a1416740279a03171416dc837340f169b9b93a9499526cf12a8c00fcc1314cc35c56756b71f9e2bea0bfe4276931e904f1fa8c233e5367675bc1f95f3721ccc7df145dcf265470341cfa4121046d6a01b24eb0fbce1d44b56f4b1682b3259541924fcb63a6a532415abe5089d4a230fe9ab329b93e1d75d4e9d3b977724eef625188b2d078ce7469d3f6b7fc20be1d325fe44cb2b0aa55cbfd3679377126552fb65e89070930303f0929fcbe2b78b1e02611aecfb73011cf6385b0614d52b512d634dbde61c063a225f1f6dc5b13803f30c4fa9699f0a570cfdb249e931ec9cdeffaa7a9810185e2623e592f01fcc59a192082a63d1ed28df830208538eedee4736eae54964968759c68592e32eb9910ae439c4658bdbb96d6c828cf2213ace74673d8ae50ffaca6fc917e1d12a2ba439d6e0d91b1f241d45e011b0cc9182cdc47b9cc1a27369db9259f6e9512dc001c30393f30bae819125b26f7acef216a6823ca540125d5b957f56264abd2ea8f427e5451ef75a197cdb024d117e8180cbcaa5f255e09c5895c6732b94493d18d381d3f8f95c0de7a9d3e3be2771317d9150810b0801279562dc4cfb890ca65b8a77265e2791730a273eefd782e56179550f2a7582495a7b72653536efd90a6cf2cbe229754f6d1f661b09c8e4fa52cd9f88b73e0c3edc65ddf7adcfa617b70dcaabcab266777a790b512c654c16e349a053f5cc787001be42853476ee2759a49171e83263ebf1b3782e6faed52aaa38a36514879b346816b0caf167c2436bc713ed0664b6692b2373fe6b8b62c92fc2de2342bdd8120d975db96d201f98224d241fabc1057d339aef61e685f77859bd6322ebb33ec80a8c938e3de2d50dd539c0d07ac83d6f23a38e3942e38e536f33779e335254c30a33fbb31e2e242a6ea155bda18013e6571b3e96fa44519bddef06addde96b4a18a0bf0164790dfa0a95901d307e6410392468f48f15a9e8401301bb3c33e54d79b2adee19ab85d6c05e744fd75c4357b3f57eff6445bdd2dd38b3472068610a678923e53d63e2a2dd8787546c420bfbca42b4903e45b2fed31f07d339494ce6bceeb40fcb10b67928be276f14428587bdec0e3cf453557742e92fb947d849f5d3849a02e1767c82e0247b2c884d89ef9386702bdba600fb4c77f066af760f7c07c00d6022c674d158519a6fc3e1a9d382869d5b1735ff150c77ac5831bd43a97c019aa9fabb9db1525cdb5c122ac8b2fb72b679ceb711b620e20492482c36526526a36de4818b90ded9afd6be5eff1c65cdbc10506911c91e7a9d4659bd748d82aff23605c9be1b35129618282b547119ee18d56c31bb541e07a0be09c7054939a9a9e1445d134d862475939ab381cb2cf8bdb0945576aef565ada1e7d26c06636f7f6bda43e552324d"}, + {"0000000000000000000000000000000000000000000000000000000000003178", "008aacdfd98ae286bb11711428e089e96858b4c7ca0277c1fcc3484bb653b6f429217d85c5119f0a78500142e35a0217083766df10e65a2b6f2465d1d3e463064fbf8007464216ca2db08674aa5435d97098c91304f3c94ec30d95decef7d4f4be79b705f0cd37f59832b27a90499f300f8d0715ca52ea1595e71812ed7f10eb35a689dff5937ab6d663f55d3145f3abbd7942142f29abba5c39214e3d271bd9e26095ff3c7d51e90125d3e30fa569d13a89512e6ddb194202e6303a3c1261c2d6da92ba336bfe61a3533fdb789d0965f56927578271867630d9bb653d820df07c07631e1e34182f81ac75ca312481de43230a37eb289697c2b5e1ac0198f33e8f84ad46f9e2d36b29377849476417f3c609a8798eeb0cd20f2dff5122f2cbf9e05accbbb265277d0e9c0d9b440725618b6c37e62aeae457fc447e4ed98acd04ebd767eec7a6f9d47d306b2b42dfc9b0047d499a5b71bd6bbd0365b450f52ace40e93d15ae83f6eebd4fe278cd67f2cb2de671900b347cf9a8ac0ced1ce2870484344fd46151180bb92f658b9dcf534c7c06847e1bd0ffb512d4d5e4aa2e89721bfde5aa0955bde246e4f58988f321061cb1f1216ce7ef194a27006fed262ce39f6e21d3af84ea57b530a9bff6df21b14b4567e004350541cab0d4f073372d83bd02d94db65d2842a976775458c9e91c5d6beab926bc4ef806ebafdbeadad3d5e267f1dec72911f50dc239d1e81153cc00cb537cdf7965e2c0ecfcfef0eba9feb8b417e2afd42c4cab1679bfa18564b23bdc8b5afa940d33425a1225d0cc568801b56c6837c762f4147e5b041bbe64db0ae5356357bf92f3adef0645c3fb7902a58416efbd4f2f9053e87e38a601ea6d365a8e3a2b541d09895e7acafedae9d9024bbf1333cac3d5fdc311527cfd0c15ba2deddc8206ebe7f64c45d7ca97742600dd88d807a402f946ba443998d0334921472b1f9022495f926f77f3b5ff52b3bf2bf42679aac8939a010a5c1d42cb4a498fa2d0880b8ce604ce0b34d23b980e57cad29487268d51da14329d7301cd11b4d1323b03119c9e5d9553ff9433f3983b4e25716d40140c55099f689e478f849da6eee908e3f4826383bfbe32520ed27f38012bb52fb7832391f91d231da5477702f820dc37dbaa6e7f25c7d7d4b2ba57aeb65e33361b29019a1640004425b435c382d4d6f0f3892a0a0cb0cd168a61a10196a103da0f41d7d51cd30cffa7dad4fa33f6c615281ca9a38339757eadbed17b21c69cf68545809cf15e387bc7edae446656409222bfe3fb8ac11de2862fb409087de500c259c6555ddb79cc9ee1b9342455e1dc92a819222a3c2ad879578b27efdd2df41f8dbe212620ef7563da7318b64e44023df3b3789a241a0ccaa0953e28b887756f4f369046513e1855bc08f79cc32c5bbe9b8c72561094de9c8768357c6e4e20b2f3acd4a75843e070a62b8beab74de177dd6fec1058d199bfd15d08a9e1643e417d7d524dda37809f1e9a3c00896c1913dfa1136692e6cdddc0573a0e4d0a1c2488e1652471aca90373a939f3e573efdcf77456c27ae1a45b5a35002485b5724572029d02bb1a1f0ecaafeb2570def5ec13e79cd2cf2a4207f178f9bf52318d68a496df08fd74055f876070962170b82e040a8cdf11c28e50d2b4112572efd7e1721b06772db32ee39c98ce24c517d6a0e40a5153ccb6be6fda49500fb696a1ab239f614ace315a1e60c9a4d4464cbcd1120fa64836b1e049fc8385a524b16a6ebeef9782f31182fb7b78de49a2fad945b6ebb4b4b9b4916e83846f5045ab0d240773c81a59b454d5118aa5f93b0535244a8431371ad7a4de5a5aab91e4d32f477a9ee83849caaeecf9a95a53650b54cc3ec3c8eeda479a12e6"}, + {"0000000000000000000000000000000000000000000000000000000000000ed5", "000db4f1d5616ca9b319a06f3db2094084e13f0a632476d7d5d654d607e17cf30016d2006168c71cd20d05855b7456aa3f1374b910a0f1619eb1a0855a4a66224261bbe95e7f2d17965424ac358ae6e7de3b268f181adc603e4d9cf4b933e21b70b04a04ab06741db229941dfc8cac6d63c13e5617d44a2e7a753b5e53fa1890495c7dd5c9cde65ea3041531eecae1131f194d5335873b4df2ea85aa0fc71f2272fe49f45c766a8604928d7a2c30b1b1aeb783507f5e891abcfe5ab106207b89eef850c08b4b4292c26db509e592eed507cc0f603aa777194f2359da631b2beef0d94ce99474d03379ee7d7b974661a43529474bfe09f324f09a4a200753766ec1b8a7b7e1cd61fe2d49442d76de8fed2a081e94579c5237338b3aa0f36d19005cf597a8a60c14ba11ae6ecc560274c5a1555435ff6c6663fc2b61192a7a2cfc86adaf1cfcc81fb9e34a8e5dd737289a01b9624a7f0645dad56d22b105bda51b42335c43211a3e4c7f230d9c0077eab4db0972106ae13a97700016c7edfbe5e69c53658f82793353a9b8f9269a4e191e642778969970a747497306337b8447acc9bf707502eba68aa32c60abc125854c22d957a95fd154646c161b6413e40701f0f423dc4d6af9281b20371cc66c060b055bc99a7bc1b26861385de6bf8631c4b1a05c0a02b72ec090ced8e81f65023abe77e99225ff8fa20293da44f374ecbdcc3760a7e8bf7b8e0c85fea0ae18765dd8c554ec66d53674c7a6b091a53bf7f7aa0e07887525ffad795becf7a0ac129008fcd9b18ad4f534a2544f54e32b1bc791842a5522b3bd68aedde5b204553b37cb94bc9db4353328c9485de790669ef6ec23561d7b6348f9513e26b9b4b7faa74ad9693ac0b11bb4762e3d91cc22a3d996cf91e69b371db139a9e3255f1d002a50f8fbbb25f35a8246991d5e1bad2e2c009b18c592ed95c591f76238ecfc0c65c7335164e004c8dededdccd5d74058b11d5a9a5b38f5a33ce1020e788fd1c10e5e7bc93a13f109b1fa5300463f9a9e140a6e1ce48baed70e44c24615adbd994090f09fb515e4e9d43b561f5bcf42e5b42ce0df9ddf61d0a408265ace83d59c689dfad2f4122d4117225092948240166f261e7cdb0861ee3052c37b4d1a229441f9afc52ee21521fb1dddb549836a036c71acceaabc1f1cfe00d6be712ca72771ecbda2de17b9acaa1999f931722888e4c2f00a90ee66dca4153b4252a58670d0c745035cea5ded6af8578e3b526ad8abae25ec657d96740b26a27245d02927225c331794262a563d2a1cda2c027bd95b99186d1bba6172a096dfc7493e1a5722c30e52870f9cae503b8eaa61b609fa02b172ad138aed04d03c5a412b0d37709201f293bf38809e2ecb0ffb082cc3b235864c9d0038c641f5e70e6a184dfb598901ed6f0f8086eeed06d310c733f1bcb8e96b4b2e1619af775684f44467ef41d6b28ec043576931dc97690b8ae77759e33143be7fb3a466c0c54957281ad2da3af40b522dd936e4fe75d46d13acd0268c45fb897b05228148df1a1222ff9c826ff9527fd4b83e4f33651a376f7ec3886173d7d2a28c389d7d3a6491f4383b08b3625738a34d71bcaa80ab58438d408259da5f751aa5926e8eca1ec77177d77e9643aa4aa15bbd54800c8631fee9e6eec94179b246111f25cd768055113c1fe6eabd3757f1b909153464e8f7693548b5b7693f1a823d77aacbc9ec99be8375175106d19bdfae3a3a4eb69303bb1544ced83995b215c0096dbc0a3bdf2d0efe488a170611438f18a6b023c5a6ede668bf94a8143be5330c66fc0db2992364a438857210b97764da2541aec4efe49587ff23e56b9c7399edc5dfb852e53828371e32178d87b405c903f8fe96d69f5879bc52"}, + {"0000000000000000000000000000000000000000000000000000000000000846", "001cc11d4389b2e48579f1b28bd379e8cd39c9ed163b4ef4a15070678fe742c514516c297d5d61da8e7a0676a4bfa5a5659b39efc16238ce03cce6a291b0f8124372d90cc5d6e443e31227ba33dc2b22ed5d2e1210c4f1a1f20703034bbdf2a78ad1177545179ec9893e8d3b939113c453360ce458f9c1acaed7931eead117888f7b5b8c031c8b7ea2fadbe9ca0b9c94fed2c8351e3d460776396bb5ff6765945be5a38109dcd4780039c1f1424ee3c5459a99770368806298ffd82f5d3c6196f0a05afbb73fe623ec6ebd436d7658fcb3a201ecf70238b4dffdc05d140a72a863760bf91667d6302b55ecd9a2bcf1dc6f266d98729da26728b74e2602c0489317315431c41531e2da4e25feaf84b8f3124152cf4dd593103ec5e144c3f677ac859c652ec69e0abcbfdfa8ca51db2158d1b19e21572e10209be41e186945fe98d22efda48c26ba63eb52e72a46bd460101b2ca1bf626cbe9997662e00f726da0e358b1a7670d92aac65ae4d04b3e2dc0eed51d72c166e69c96113202e27db9ad9337f9b4c54ed0ab78b215efb75bea3f05c4d1c6f885b7cf1f5642246e6b49a9536d540a06d32c1ae5a84b4588f449f42773feaeaa9d7b42d16a19d78eab23b5f3aa15d7c9bdf75d4e21a1d249be1b36c976d3508263fd72d6f95c56b502b9617bf0571e7f3ece16d3c2fabcfe0450ead56e5d7bec1d034204fcaa72401d5911cce178048b4ac7d6a3037eca55198fb3d17d1ac57714c3b5032833181178f4b64bfc07ff9ac5d1824989907413bc1b56134e684bd6837c0810f987375756d3b4fb26acb35ea979f118f9421b16e83e38ac4a06031c0223fa8b37e7c911de1f8c5d351a160759ac6531f52928cd94d5f1763cd59ff0c61e88b472db15199ed0cec22af7361df9a337d5c5d72339c259662bd579d6b426fb707835363e29f4773e0079ca3afe8c9dc96ed49212b07c5a34c7988f6fa50e784d35406a5c5f84fbc2dc9a4e2a81dfe717193c19de8b2768cb7107eb0941e34c2669ace3309809c42c6341d7118c553888d3344141d826df08edb97b7507822131d3dd25cda0fdb3cd6fcd768a7729dd8dde341b7cb205a4f433b28824fe925724534e289ed4fc2ab0a98a6651b68b69fe2864a2ef74b661515f2ab4308607e14df88ea9edf1c59d10c4a989d4483cc83407740a06f284a5da6e19b20ac114e4e9ab052fed5729bccd57f2a1dde12e6f8431e5cc910a7e2f79dbe90b0829c4ec460344c38aa27c92a856b344f13faa542c761b431195afd3a790ba0df665af5ee296583de608f7e2430cc89309b9bc91674ca3c38231085821361e4b0ae79bcbb510b7e09256476a73818ced5bfdfa25115d5fcd4bf75ba71494f72bb545c9aac4b0335d27c593def66a6aa99d5f86ac593eb54a98a3d674a70087116e5bc57e365cfd409d49f2c5ac69be36d47d4aeba3d40c575b3ae702b4f507489b19ed113460ad25060f986ccf0209f5f91406b56c5e72d57918a9a835dafe03f8db968af40dd651306b106e6c1d3be10503eaa4c89164a2adc8a5dc0682fd86eb22fc59e08421bc5dad04089bb3714784378c3e41093a84df11644561230e0a61795f3c21474192dab2de55c956796e621f87963b5f7341cc2c0c29a46311ef22789b56e405d29621341a8733809be6d0c440112a4cca3b51b307815eb91c476e63ca3365a411c063527323bfbcca23352a922ce1b58f599cd4a4d537ba5f134fdc5d7739d171eed79636f57e2613adaac9fb5d4bfecb6ef50cf3513ceb608015704f0357167b1d3a98fad7cdd033d78d7fcb57c0170877f6d42373af4600a33a30f41eefbfa18e0c8ad29fb492f2dde559e9830b7d345023b8a6efc0d2425bf7cc83cd10c37499964756d7e5"}, + {"00000000000000000000000000000000000000000000000000000000000002d4", "0190626c6e5014c9ad85408e0313c74222be37139942f0cd6fb095f275019424797b668a7ef33fdd651518b9af8536063082e5e1f2ddbfa02cf8cedaf5481231f0137acee2f3cd1b4bc3297e750fe1ae6c32aaf10f21d926589bc30bf57b34f3feaaf5d94490718aeb0fc2647e18e407ad39369768c84cd80e99489569a82c6c3cc25f6eeaf1f5dde78838f89f4a55221524282f9c5b987a4fbbc11aee53d4f0b1c2ad38dd39c043040196e71fdb53bd3c3d0623f55f4701d82ab208250c922280358b1de6e57b47c323c7337ec66bba1d8907798a4804c237d754d0b6bd3f68feae1dfe1b121713de81b274c57ae6faedc2e2bcfc0ad9d539beb8ee0c74ea40185b1e8d47ace37733dae995a638cdfadc20100f047659aff3c3ce93a073e8e73f14fd1e48c31c53b1f3c9ce6e2ade65034ca138563e41ac7cd58f37b9d65b1a5f6a2bcaf2f3d1ad64411959e54b8fc703ea76d925d474395d35c2c14c9c4c8202bc55d75c075e4f79adece9ddf64915b1184f25ca2bd852eae40b5f84ae044bac49aa5dd13386e5bd9d4715faae0417509f757f9d144f78463489aee768f9b54eae0dec093cb675da62b8a92b9793baa52801895bf4787fc94d6507e5ce660209e0d6e5e9994acc76a6391f2465100553bb2c9f729f776dd62c6b4c7dda5137547a8526624d9b00d25f0bd3112716b2bbd9c1e8e7f01bb209eda8cfbed27c05554e0189f9e26f94d3282e6e4133afeb860b128bc1f8a6b48472bef6b2e4bd779c7b14cf7ba9de8e0026f6e3da0dca6cc6f390bcdd7dbc30879c734f114fa1618654d00dbe038192ba90ab36129c44a38c115777dcea63e786ea399a341bde906b14ba39b9095644bb1cb8029d491fd2d13367d366b913a22d7ef446475b732e0485587b96a1cd83bf8cdc613d26ee51e734d58c41b7a11dd7a0bf0b81dc868201c3e5033b25b94bc0d6384f464a3333beb65f0a882984bf8feccc18da9876f4fe5939fec687bb3a27a326d723686c8d6a731325533ccb355a3e085b1cff4d3f410d3cd9a7b9c9e94689e0496d1c1ad8229db92d0ccf0bd3fb1ae2d39afa23f54c7919b669f05b30602b7281a627e61a0d3a2793a92dcb1f012ce8ca91e015c4ecae6f1a5a997ea881770ec4e7f6c3c39789bf34973b1002909a7bc6923660f53a8d7da5f85de0cc0a427b1c1c115ad6e447b61ccbebb0f9f40a1fa80015bf9ae4acd443538d5be3bd29e42605944116992b35297306b90e293beafdc4d6b2d51109cfcbb8060656476be8769658e10c255bcf347c7aef37191e54481faa57d0218f8fdcf980b5e95c67fe06524895540b4e52f3340d1f52dfc941365ba7e7205b14079bfd5f20be0a4dbcdb7b0dc0b002c1be3f91296d1a55cc8f248477acdad0781e8609d42015b6d9ad10ce16e30f0208463b860ebd68a09da069b993ed80f731f001a61407e9118fcfeaa7b842253241e763fb507f9d2a4b28e85f2e24d5202998d0268e1841a66a250e1ccfe82ebf020e7d2eb521c1f9580c37ee993a1ee91904ba039a05511a46f095a83de4c7e9daac56452455b9091a155c51ac908949619a01f900ac6491ba4b5190b4193c5c1026ef0cabb6da019b40599a9ccd081c98ae28f08416a0e653293b8dd4a0e8aa64ce43af784b2405d84e362f8ffa7b122ab42ef2daf46e406cb252cc20aa4b9168f52691ec65c2f416ad0b1e2254d5ebe1072ca735741e7e75a136e0c7cb6a44e95d56fdf4995001fc12902de5ad92cc8574fbce3b5a28b239b01e064bb27b2b0e217c82ad4198e0682618d839fa3cf0377e8c4b9393355578b8f63820e49862a5e1993996280ad45b09ee7e37cf6a25dc8ad174322b5bd1be54424fe7ec841f23076c465861a4d0886f86ec7fdfc6"}, + {"0000000000000000000000000000000000000000000000000000000000000ee1", "00101d8a8e866d991c5ef08ccf2e66ec8fb2fd344c1dac19072d1f91c79e67c904d17b548adfde9d6c831ff54a820e16c28f82df2348de6cfe5dbd6fce496a43f32d7d8ade44d1900e14d5b36bcd396cd5eee4f10114098138c31a567c4b53bbd56d92ce7e8a76a4927581948393a3a743bacdd79e47ee99f6727cd48dbd0170e0ce2553f0e9851a339d5ef36911cf71da8b4e0a452e9f60936763a8b40257cdd227e10c9878b9c60253e727b1af6895d707221347b180c8a1cd12296f0fa07c37bb5dcf975c5ba3ef71f56d596c92b52b170d204c8fbe107a3dea7d11ab29b079ecb81f3576ca2283fd1512954ef15fcec63365c97ff1c14c52e107110deceb22a68081532005f4803e520dda821f6cbd522583d55f1939d1183a75a7fab47d8a8fd45cc3c6142c244ee1553aede65791e7c2e1ce758b52dd71e955b2143f8c6cdec1ece6f5e7ede3530664abf6b6420331b583f5965da0c25de78c03d09806c314d67763095bdc040adfb9b90baff26f934d988cdeac373acd17ad09e494b1ae039ee4d6bbfff75646333ab96b91353f525478a89055de2983a6f1e23dd363001ecbdb0e9e1223e99d29578edf8a0c9653dfa283d87b8a7814a4d265ca466570fee6531dd3e4ba964c4377068f1444938bef062d399147f7253fda09130edbd9b3aa1cb7af00254e576dd1eff32f786b7788fad972ff0d07ab14663cca910fadefe07d4179e4b1582f6ff84439b9bf8a7f954d92fbc614067328c0b78dac9fa89d0a24df20dd98f6e70fb902308e38deea77f2b6bba7302f61f262d4f1ebc4d5732e7b5479cdaab6d49cc50cef2c47742a2fe3c7d583e2ed52b874ff0c4e18c540294b92d82448edc61ae8160e501b1b8fc4bd5b0e0eea35c87c9a46050181671f5e66460fb7b6be10b928d4de5bc39a379ced95c463736605d25261dfaf4e00112eb7965fe3877553845db1a5361eb7d697a9900c655250f933bb0dd13ef23e74bd499cfd8676b1cc0c8378d0d4691c8d673331f4ab28e296fe57194b743611df2ae20ee393037ed51fce73bf56d9da57bb4403c04fda3b0ca2339d4c0166e2e1fa747f94712f441da08d0a57efbe15f48356c7c8dad1a7164e9be2060ebeaa09ea8804fbbdebf43b90c2c8e57352d2ff085c3bcf40f39993351beb55d10b3f18f1a29e34cd6b026a428311e96421a874d1f95a52caa8efb3f46f55041aa3457c10e32b311a60924db919aa2868169f48396a84cdbb0f19918abf45361a68407a011bd4f0603b90467d62968dc0b7fb6515ddb2bf415b462d80b8112045f58d4cedbfb927a1d151e6f180870a0abddb2247447128731bc7b5cdb6ab3eca25a6620c5b3b993245fc290164f9bb495968b88cf35ecb27d87df7ae4cc8e52cbb56148b9a7bf7114cbfc14b8c7d9f165b07958f45545da3fd74f3e1d9cee2ca34d8a9c7636629628a718aa6dcff57036638bacb74ed9df6b4d6eb18ba36161cd6800fa05d561cd832325dba4c5520a83b60bd31ec19793feb8b75eb384b42ff27bab9879812bb837d5d49d4e5f8abe51752b619d9c9d2f0efd4384fe63bc1219755b2dc09165ad8503f1327bea37d20979cea09cc8c706c9462146dacb6068429de4cb94c77fffbd99aad60da56b862d0791af28aa1be0f9611b409f180697fa7f020712de489a29d5431b5e5f83130565a91d5d16f20e9d393a4451ff629a2940d762f4815dd6d5b647f10ad53ce47aacdaf2539fab911368a5e0a0a5e2d97a4ae93fd6fd5b7c102600af7cc12a420b5004ce206f35773bb65f99fcdf06d97bec930177d0be95144c15e25946b6fd46fead3289abe561f1879978fcef00498dd224368ce201e650a3b12632841f25f018d815eeb0b953c3765e1de35703a7fdd"}, + {"00000000000000000000000000000000000000000000000000000000000001a2", "00679dae38cccb579fb0816d039696c1062f3bb1af2fb94f0f51e86bd3a79893063bbe7d191f447e46411d96c1547760086bc170f21809f923466bb4fef4ed25c8e6b183db6906f5d4c2a0aaec434b30c6bdc00e0921292cd348c80ef4ef0263bbcef6f5d07bb9bc01094c84d87b53520913fa24a9cd2879496bc09c5f680e7a36a8286d5b676cb751c2d4e05500c2ab1296fb1b3853a0acb408ababe7c337bb668179a718174d590140703ebc138697dc39c56748d6933d944c5f91aa0330c246e30d04936fa67255e9aed2d994f73a559716ee80c4106b95db9c19834a50380e6992e12f7f4f2544c5fd122441b7cfa6055863318aa62c457ec77a0517da9d0b19496bea4b41c376534ef078d34e3840067aa8620398df379cd1c4001dd4b43783641c24821118f67e9ed0ae132902977216e6d25e4ffd5a6ee81fd3d64c6cee284be56774e5c86354e96e5cd013e8012887d596c09e9bb203f36680e5c7453a8839bd6802f68359d4d62aace2c3803b0fd8c410c7f656cd7119d77fb35f65f1f1490d028f3db0f355dc1fb8799557633b2859a3060d9c1798213c6810d2cfcc570e390b4188b9eb8cce83a5a6f1b441f4b9f1bf23ffa858184b2ce670eb25f7d8c85204802536891a71977d5e30089eaee7270ae3449e46a93abfcda27f991a6edc34422ace1c950346b1ad5700ff7a701aebaedec3c9018d845bb993487eef4c71cfc3e7dafdae5c9bccd25990fe982466b5919bda37620ec376ea26cf34873a257fc655474b6f9cbd26d56f4f3cd4a2f6c2f85c7a4d9c1528f617dd4dbf0fed46d37bc8a774a23ef0350bafd26439181ff7138a61d79760bd7a4b327e87a11514ebf6ac221e798c8b2568ece9f5c55da8f6cb571abe496a3fe08107616272dbdbbb732ee809fb23661bc8c11af1cb5b0db0bcd249bab9ed9b5a2dbbd1bd02053f1ad2c23f1f33219358913730721e33d9b34b33a8e2ca302cc85f7892e42a2224c52deda37c954f10d88c466719694f240c84194bc86eaa608455fa837880a76aeda62685c28ce80d3469c07fbcdcdf454509d799c493d2463990df9728ba3cbf66dcb85ac99d62a3fd3930f3e4b7a53ea89cb2e8a536c2affe7c070edf6965aa1f80ed6fafd28e2bccc140c37f3d9a11134654be995bf1894e2562125b51d7cb7c77bc6eb20228aaf93088dd3c5d71e05eb1b6a6cd36c79022d51d8132a895d61d73a3d18329406a21d8e1729af6310b5cbdfb4049f261f5ff31224c3754e8d27e4e9a56319674c3a9b5ccb1b7313577923e1917422d1fb2960c4e82c3ab5fa20d08fc29214b4b691693fad6c6d6407d640ce0f5bfd1b0f0249454f15a41ebf378286925c1aa8cbb4c3e9e8266d4b492353e99d833baea88373c4f08baaaab89a6e1174a07e1c475ecb3d3818a03cf0c54634a752969f0b08ad5b09d488303e5f41914660b59a8c9582f175ae200cb98374e219a5d8ddc21bd9d3d2f1db4a979db3446d8a24306e2989d53f34bec5522da6aa8e7e33f56952344a7320997b1a2c21704e4454c64a63f66d3243215d67911dda6d2eab14dff76e771d7db74dba49534cc793fb2e26d37d0fb1a8bdb140127f0014e15b87a964bd2ff15aabe82962d663aec8b791a4bef1e36226f6fdff300f238174e0657cb26ee4550c5cdb5bacac97c41c741b59f4a17295d2c4680e82f378ff6574e7f7f2a328b9b9cd8d32d410b37118d0e197d9e83db02a0b482d546b9c593421a13922513f7a789add501227bdb71480f75b8f123cda45efb561fff0318c2bbe245f5eb4eb0fb513129169d8d54d51fb4f306eb9354721ec792813ea89341d52d21421092a59767c57e6bf6b6c3240b5f9f5850a5a3afbd5adaa9e82436794c6d4bf2a79fd84326"}, + {"00000000000000000000000000000000000000000000000000000000000001a0", "0027bb018ddef8cd843d08274bc7d78e641b573d6322d54584b25244f9e65b4503634944ad99c3b77de20ca7d4b7eb626f1f25bb328c19c9f2ede5e3ef2f40415bda8380d463bd272d068dd6fe6df5aec64dbbda07c5e5a22a5e53d74520617c4effb734c95106df37092ee13e08b762d5deac2886c4450f1b2e619db3900d4aa3ae3b5e233309682277e4b8265b43f07e832a1464e5a179c8732f9ee4921efdfb5e9530c3171ba00342ed4f2518ee85c4947344e23c3b650d3caf72b51cb98efa2f49535da358a5499b4e4e9f1f6239cd0f35f91740c33617c1f4e2e44a30b6ee2376f5fc40ad51d39fca899d5fe517ba0bad11680adf7e3b7bf2a604242add48c6cae98780e1ad2f4e3fdd6b6f52a71d12e2e95f8c2dc32faff224b0f37d4c55480c10f26508c1127f9c04ddcd5185d346c45c2e2309c53f545830edaf4c449fd6c7bbff4616426088eaca6e5c0a1004b47b47ea13f08fe70e92ed69772899655c75b7ef3145a2c36b5235cbe6df041656682fb5944a94c5a1121cd5cb18372229ee7cd8cc41d666e35c0d7f820f36a69bb0fc2014b7e654372f1deef3a6c2019e01980836a9c859d15f09a95a00e412f74588897a1ed04f0e91dd3cbf44c7a69a09538e823dff8f1560fcf8dd0ea577bc9fc91f16d4e41124152dd930f51c3040c31f7f0e81eb15d5213c37f3108dc4933cfb0434cf7505fcc0973154d5e0d250d3e78534c4099b135c1ce70b22a5f1edddac695053246573e023014cf7d05b9b18eb1604e532bdc3b930d73696f7f102968afed2783ece17817d0ffcb72f31552e983b53a5ea7217406b081d1c0a06f16cd5d04a91190b11331c565cd0f4ab0b9d73bd140c940269a1a29d0a2da6d8b6d24f11190bc973a76505849baedfb1925cf29639260837402a174fd74c80133d82f27d4332c09ec71aeede5ad22d0090b0b90a119b4f9de7e420f8fd0372c93778af180513e0eeb0ccadd7293d20e10bb0a4c4b3f676218800f45f4ee143b855dd40c20af8aca80ed8a2bc7dba0970b694d05e03bf6342d4820b66502e0ab5587fad045f59e0e2970b33a6dc71dbf7c86a518c2cdf12d5136672f47a712db5dd7352b124e896c503365fbc9a0975e9d0c7a4d71366e862d33aad20de83e61e39c01378cb8b87aabd6da69fc5c5be749c0d9fb2af709305a45192b29cbb83361e65c3635c81fe23489558ac218f9fbde94e21e6791c142b11cdf681b7a6526200128f82321f1391a978757315dcbff1f8e6f3ce4d7818718f75174d39f56f527339324590cfde23def87f132095afe95d749fecd9a28cdad0db4568c3fa757a152ab8f87577da83de736310c1e1b3430533dab94e159ea72e6f258ac1e381279b8af8f21a25a7763f681dc0d90b6c9369490edeb66d05761b3e13621978c70648c70005c745fdce6ed130d2bae1b5b246df326f0b211b37125b57d34e9892c305642fefd6c91ef96706f049c2771078711abf71385f6407546bbf1bf5cf2c6f6a2b4be89dcb8dfab57f036852c5916b7cf3ab095d93b1488d39bd5276e1857d7cc8309671980d8f4877cbeeb3dac5f5e69245e9a466cc09d9fd98bb9a1752297ad1a14a11a1c5f1b545132f9cb546d851c8198709fc31a137099c34e308ae5e8ff9b7c7f902400886d212b747a30dea0c16630456c9ad9ba22fa6531a8a299dcbe7498172e972fc85e09dcd219290f05b1a6c8d29044e4f530bdda2a111aaaff0bbe02a3cb15a4be388f02ac815f02f55bf8bbfe8c6514f3de4a209dca528360b4670b32f532d1c34219eaab3be8aa93506d3d69263a2c16f5fb41ff9d52f0ae6ebd84a8235ab5c3e6022ec7b6eebc6e1bb60cf32690354ae0f3e0104543f6445a33339a62fed74d6dda8e2947a2c"}, + {"0000000000000000000000000000000000000000000000000000000000002eeb", "004ec78c041ef388fd0393418039119debff9e307901e89208582bc3c7e26e64d1643da213497edede6609662b2ba35a68cfc08c60e426d88ec4501a8e8fd247c487578df24fcba886178be0c514325af4160244064255488b897990e85a30dc528f601eaf3c97ee380aaef0fab2c76014c75c29fc6262548eda81b7604e1375a36439080fe90ce2b60dd5ef4dc5ec5f3bcaaf3c78cec1c59dfc4d13926513a1690e4fde611fc25e09245934289b1231fb68c31d86dcf34d9a2b9822a54e9da681731f3999c7e8b829ad6b4ea2b63edc9dea1847f3ee49189193fb40b57bc1ec676e1c9e9310ce25c9e22819d7f84b6cd433d9cb6e865dbc1e10ff5a0c50bdf19c1019f976fb538b2d1f90750ce26945ab5a63b5e4b4a7d6159de967a07cf4cbf2f9ce1ba75b0e984bae42e59c098143413a3b7a60d31899dd8d2311a980bc22a2edfd8eea632054afcfaf0f2e7cb48002cb9f1a4804379fc1ea31731e77647df5f5b832b6440dd4e2df5efa6340ec655d05f05e4ef6a87b598708f5cbfa83c3f4c82816a2b7c05a8ea8ba95bf22cd0fdc81dc7225cb5fbba24731564256ba6108d8cc9c053005d44c432593a3fa00c05bbd7e7b75c59bb0c01f552302191eee719043247da2fa6d7a5d577f4c6d0b6d2d15178fefdd0b12031021b9aac29b2afe61512746c3cb1c4d47c2a717963860ee0d2644f31424cf02cd694d6669e13d6cceb4b54d6f4eb52edc5c6842163f7ae7e76d064fa0adb4f027564419c29cafbd911d7d968aa61a6c95533c2a2cdcdcd3aaa6f05c8c812c7a17871d233e5fb01185b17d5730217e1652b446040717cd585a88ef271ae2ce3bc7a3eaa3b5dc9515457fbe6edde148e1a42a170dff52f0ca8c81b8f22509c3253a508c4c1475b8c44a5bf609f2033174e0a4108adecb609fbfaf7e9f23349e7e2db53251745ac100ab4f1f0fa677ed924ac03ca27f4072ff4d9fcdf0079523262207b0728f814aa3487b4f02e8ec5a530b0bd25c9a818cb66aa3e561bf09a64b1a9a0d5ecc8d21a5a1e649971631ad9b74ec224e4b699c6f123983055e276b558f441562f840d9e3bb0e86c2099b07190984021a6ec99d3d4c2d30d44da415090e23b1ba7608c0351a2496a4b4b695114b69e52098e8246a6555138554a60d306acdcbaf723cd0f8a588ebe607ab221070b51014c6c3f0fc7ff58dd431ffbdee81dde40f15f29c7230d04354b641a655cbfe5af302085a60e12e761f663ced8c03882c79c3c1ff688f2c1eddf2ba2ed5bbda6d93ae5595c79468da2a129137bb3f3fe6177784eac210dab901eb47e3bcd210f664603cb82f1865a66b95894b4c4f0dc2b7a1f324a2c4d33b33201f2cfa009e17502f1ce4552402e338b39c4cbce5044eb517ec2e94e31d9221761284c6596dd03536a429035e4fd353f4749ff77b750b086d62d5c6f354c05847699cf82666b2edf11964d481ef5719493e91eb2f1027b3342fccd90e9637828d59c4d2c5a9a9f2d4cb33b41b32aa9be8257f53971583eb9cc7570f9e481a10c709965d578aa7815c55740d651eb5ef68bf0cc5232687c852639cf981a4ee5ab2fe57d39f20ff2fa014bbb9abc952ef4ef91eb8eddcdc15f28fe5d7f3f93f472d40a8eeae09880be622a8b249e98cd3fa8c6d040a666c2922b933bb4804790dbe477aa4487bc9110fb70bffd3128ad75d33218929b76c80a4e6d259e10618f67540105b319d2b5404e444bc29f2287f943a14cdb72059e588edd3b9a222b7a9e89921af3739990746f9c00321242dbf81a2a882f993f5af9050f1e629f1db8722d4b4897e6845c73d53c8aa4f4b97e9d61f45392806c8156caac86401cec793d117a03668d44214a6d23890c1ef277455fcde594fb72d471eaae5"}, + {"0000000000000000000000000000000000000000000000000000000000000b2d", "010cbc4020611f379d57e4e2ad52b08d715ffdad0f08588a39d2c9a929ad3da305225228690e5b762f1a2df83d9e74d03bfee6f1d4e353acbc621ada3c3f4d37a9feff829e6f956273b5ff06ce1c2275f9f94a28091b6fd57c627e8551d0b794ce4fdecea376d750c71de1f0ef62af4a4fc15881f1167d55188e3294996f0e8315dff2a48db38a9c21de98c9c3c0fa07babacd3ec535d821358bf7fe82756c997bd6f6fb61ff03d002f417de36cfd217410980d000f78eba5a97fc132b80b814c443b8b6a7f748c937a0ef2aeb3d291a5c4c05f31a925232e915a6a380f2df38b89914fdbf3957060047b404d99a52fdafd387e7bfeea914d65047af0d47912d4d118a0f249e7414e65b9276589db431a223d5567cfe512c770c43a2caf1c02d72d1f937269e13c6c7976505dddb5124614867aaeb8d384d1d675149ca3d69cb188fdf5542668eb5f9a665b892ce75590622e7e2af063cc893bce147710a767d33a10ce1972a5c2e68db308889d31c52ca92e6c30cf1c75357b617fc02b037d5d4aee0e20341a56c66f508d96ae7e82381b4a7e01c8d07f2fdb2986acb797209e83cd8d309fbbe54a5d924e59fa7b353f39e6f9ab9a1b7759612340a503fa873b38de2213ee7eaf4b4f41fc8dc56117c40f85ffa1f67ef13a2b8e4d7608d85b4ef514826aa53df2d92d9a0ea75d644f2ed37830960b90e2107847fb5c694f6f59d98d498827c7f4267bc5a3b7f168bc5f65f539525b97516482bd2cfeea20d9c29700939b5a028eb871f766a821a4c707629954d54fc5911bd397a18f220f5df29c2631f4e779377369d0fa20c564feabb8ae07aeaaab48de24edf22ffe41b18163332d6d16d1c151fe1100863625c03feb1341b1a6127e6eacf6f8f4f42bd6fd39b445e57653bd231516f2ee57bdcb8ce7b677d2a93ebe5ea9cc6773ab934a101757d5e6a0e93ce787854a37d4698b14f2a3b1fcf0a5eb722f459818d965aa233e2e67d1dd983beac910bdc9dd4b327f965a05a976f04f710ff1033dc8c5b0e30ccac42348a59b1fce2f91a7d5d3cea9e6c6453068f68d36ad9c91f43efd5bc6cf104e2d6b49b7c610880250106dfa783ee2520eb00e7ee28c923d174a921901f5dc2e38125ab3bf22a894ccee63db1de9444364fe23dde22b6e7fe4f74ea58e01e328792b5b2480a63164e4d9f8a1785273208e693c06a4c81f4ee5d188a4ef133a5837bf756174d4f6a084a84e51b3d531b6c57aa88881253e722f6056749d0ae0d2e3de2a11ffccc2378112d8bb5f095ac83ae4b622a7154fcf90d1d9da935e59c5782c52354754c65f9b3d13b0896406d8320ae270d9b7c4df5046a76349677eb7b2d5b28a52ae0538cdd21aacd54bed05fe7b614f4df88722e999f4d686dba9b94da99a7935576829b40b518fc05158c97ad117de8c1ee55d1207c59c6466035a67534344b69bf6e16878686846a043308a706ca3e45c90f8329fff04c92fb93c9c638c751b489b3e81ae7f035f56c8ee4111feee2d4c3ecefd359e110adba8255070b055f8d042db78099f33007ffe75ce8e3b4ccde3b6725c82393cca74daad3ca86b41a656f11fe6e3a19de1dc15e175d25e8b02406da2c7361d1524f36f023e62377eecdc739bd51240978e6cd2da5d477a36a0ea1b12d74118039b97ec7e0215ca36a08da54472214511b2983a20da3a054231b35a0861dd6d53fab7811d0b7502494302346c8e16c924f95cd9fcc39f4ce197f42d01cf6dea7e3b7b2a26eea2e6da3a54eb6b52462ef7963d2e09cd99c33bbb8a546fd3ebc0eb8e224d5c4611b3078e9f748d4070cdbbe353dff9a470737594e16708f6de306e3b7cec4e2c8325cb0d9da685007b2bed7332bf7cc23a6b96ced34e9c3559ad798"}, + {"0000000000000000000000000000000000000000000000000000000000000346", "006fc6df4993f598b51493c91ec4edb51c57124896263fcc7c6d678a73c94c1aa999716ef32312fcccc113210fe4070837acdc52797ca9ec9e62c0b15682071fb56bf6469e095b3351a20414d1d30531463a347f03f6dbd5b89ac52b573651a68e3cb23d03d572c16d18a3c0f14420587f2d6da3ecd2ea81ef30a9da1c7a23165a15028d0302715aa38a3e77a333bc033de0be576c3624a01f6e41936635dbe8ffe671b7a5d1f06301b8c0490388a28ba0e3e10c7e40f89eba19deea250d620edf2f8de33ba9beb5ea5864a1664b5579e9db08b54395d956c5e0eea6e5bf566df565dde1f93d6353072ef8a6db12df4afba6c4505b7a79cbf358b19c030c7b199dc14c26eef3b894d553749e500517f1a72ef6d29ecf5874dadded034affbdd979081718479d229bc332ccd6a871e8b2baff2063114f49d7fdf70e4a7ef2ce1cd94f9b3cff94d3e7c9b0f300dc3bed4b0146d8d7b389cb974775c60910f77eb98a99af4d233359bc0d92cd0689ed78873753ea9c626390f9dbc80643d05b2381d486943b40e6e5d1f98c9be49ce83f38887a7280535884bf5353dc7623d78dde050ff1df07c1d84067422b3d2de3a46489d9d9c13e5f70b6942d037295cb8e5e82a6e8e4e4eb3780e9b09079c6d9167a22128dcfe2953989a5c844e7534ddf9e1df07e59f165ca172c2a4191d737567a7031263391583a180305409ce360339d754641d61f0ff3a8890b90637a23235fbe17d47d93e6dbe4afb76cfb6982e0b5911513548c160d454ba7030c027ccb4ce6e55cc39f02579becfed9fef7ee1bffc4fa90ce55af0ae3819d7f0c1011632914c98592b7014749645bf411fd4c95ab721b92ddf9b346fd4839f681c2fd6c363b408f9abb2813de5464e34cc82b377c0579705224b2b68717ce823a9fd78f6ced9281c22a35ad56c7090e360c1a87fe00ed796460940937375bc176b2d0e61708605bd40544712b972a164f0b474034b1af40f8ca6c9859febf25c43b6b4c4a133d479d06c7aa68f4b6846dfc4b474c34c4a7065c83a7f69b3a8a90f2baaeba153fcf9c1a0f1ea098075c25dd652a41ef6dd4d6ddd6b8e33b31be9aaec2195c39ccaef511046a97ba82f13c03432c2036241822f3f1bcdd86e7567d4d824b4ab96c5486b6d7ce8e29a789553dcb0a0a58b91b823bdc971504f2bd5c9e2bc1adbd5d56cfab68db6e80b71aea85056374e1c05ecc83515004154a3bc657c5e75fa053138db1f1fe8711395f275360346c999971970dd9252807ce941edb271df093760f00b3781faef17e22d92755d75941ea2dbba2ef73373646bbd31a40b9b5e031dd9b19364d33d5d7b9668204fc113784eadd14a92b798b70e49183d3d772b44b03616e4df3808fa18c3ee3e603a557050943440485d72bf1812f27dc8d3904bcc4c4111f02336ec3273d28bd0d81fcd5fe4e6c151a0a325a083e977d1fd2355124c445560838d0620ac991ace9953e5f0851a1b03475df70e0466cdce2448a62e026180feece6b053410634b1e2eb47698730bf959d89c09e160c021e7193bde305eb595f881ba2662edc7a013b04bc381446281682dd98f2e99cbeb0d865e0cef203045e565b24c3ace10a99fa32ed28d1a88c19746601d1b285528a40df53986e209d77abc04fecdd5645ac91fceabf2bd3a1693ba42a73ea02608efdfce742210a95832320cd7135160fee28c51f905615a9668a4d37fba222417f13766d135f835b34d30fe54e8e072a3dfceeba4e08a564236161894bc39071b8e7aa712d355e088d3aa21505671626cfd30953cbe52db4d96d101dea545bac6fb42da64dc745995088e730c8fc3644f1623f2df9db0890f6478fb99633e4a773c5057fb36e15ff521e9c3eb39b7fcbe700b"}, + {"00000000000000000000000000000000000000000000000000000000000007e5", "0018ef6a5fc206ffb829a4eaba63992ef1189affa608ea3b5ad166a489aa3fb34e105bf68a43ab5fea170cdabe61baa8c02780fc41dd28cb118797b4be08e12595bc8db0291a27a3b222770b7063b2103b1ba5cb015a65f0010ce815b938525a19fc30c9f6bcdf0bf017703fee950c59d989be733b316606e8f7fdca37a2183d6c989eca7659441aa29cea43c38d56e2d70b11253eb3b9f176e6cdd23d24fec7c16b5f91a87f4a2002fd21aaeb2a5c3f5e20d5cfdbc4a301e9ba5e6ee10d8f5b1e94ebbca1cc8ce681a1eec21dbb00357ae9087e694f6093325bf9154631f1c82e59d4a9d12fa30b2b228d7005d19e9931d39a7250071946e8115b991453c71b82a1bcdb86ef330ab35c9e54ca904f856a2dc19756a7ecc2b7a15a436b3accce315e410e47f41b32e466cd8b17b07d2122cb463d9cc17adc3e710e445b12becca726f1f6c397d173647c7640e058263c0116a3e90849becd1223e82c2ced094aeed83b05eb01c62affa68181ebef92520c23fe91b4f0e4f7bb4106f1c04cf0849e15b644e0a59f5b1ef4353ad693cc190bcfd9eb1f30a3f36cb8651cf03dfe42837b8fdd03420224be5b4d7fddf131eb4fbc65f53cc76e8a2f05b650b8034d923dc9ffa1b0907ed5d52c99354d4610ff9b54d1857db13700317fffba035e49d79bf299415f0f9954f7ce8de052160e4cfd6e7e109038ef6f06409a9e40ee26f39aa9f56ff34b0c8d70978efb321bc23c7b786cffb57417c309f8524c3a81b99a5b64094757320a0fd48a9dcfd286d46f7b862afefd960e331ce2cdac5c0f17398656b88a67fb9ab2cc7ddd5213732e40b553724ccb6b6842f9ec57fe38a5750405204854d1e798cd90e967b2270b464e189b008c477d1383a8d21098a1bf9f747143fdbb8fedb5d83ca306325f83e30c9ad606e77816b3d6b9f221bf570f48c60129f0344ef2a297ab351095cb6a2a3174ef6df24e3bdc620c3fac0789871c33f6b77fbb496aecef2a7608df2527badc8d2f64809236bb1d290b7400dbac0a584d27b861f43ae9bca4b6d38ee913bdf3b7bb5d590c431084f99186f2d0cb8260073fad0706951e13b61710cc0f755e949b9b0411bc28fd18c2d00e98bf1911865f5299847258806aa39bdac253ed2c3bb18cff4331948cc7eae5c1af92652a67ea28fe234739d30e04dd54194259604f5b5bb3987fadc09594803aaea521d2abc1c8de050db1b4e5a675f7552fcce0de87480f830fe3b266c451dfb889426c4cf3635436ff71431c6dc9ebd6d654d1d9fd453904519182bd2abe6cc8098952192cf67a55df73b65ccaf6789da32f116f6f1adfdbfe26d3bbdac23e8299b54a4fed41486cf1b50f3f81f78b2ef1dbd3b594f86bf6cc51679d143dc73047f6e0ca625403b51c0355b3bcd6f8e63b514a7c01d6423034e194cf91e86125437c8acd07ba5be2111d33ee7145995e2da4ff39f82bfae2d32c5a9982ba0f55ebfd8619f4b187bd3383b9ad318260fa968eaa158c529fa2150d3de0cc416addad51aa39c8be06ab0819f0fe29aaf53182ab30b369a4a4e925520e0c8f201efb534b4f34bdb44b54af08e8609e0d9a564c1f2449df16b389a1df01fb0633f7f3919a6857db11aa35659d5b0ddc06a1a421c7a35e7923224cc39589d60266cd9167079496f910433366f868e1edfcb752ca037a2cc1e244b358ba4b839a2f46452282db3a7a8e0489d2897a555213fb7bf522adf162ba5b49ff69112f08cf465fa65f1f8c29a818376a2e46a5c25ecfed24b40a6e05cccc28a58fd33174309c4d120df02458295494a4a36e0793c495953d39e0cae5548e3811ed2ab2eedd86d850373ad2f4211af0cbc10c044e35262cabb6a439576f1cded8e675d165737645711bb7fe"}, + {"0000000000000000000000000000000000000000000000000000000000001668", "00f890ed549232d2d87bf04428611d8022448f78d20d1731c5d411a59fb0e5370047c006cb5f255bfaab11bfba2d2598dba9ee410342389c6265ee8b180b05248f25f5f3d0d26914c67505b3b1a4870541dd40d804110429abd584a1a1e90057c52bf058515d6fa82f2a51c6a5a7633c8bc8c6a3bbc7deaab5402877fe6905072fe3748bbf9baafda115d48a53d4e06bbcb68d0dfb4ee30fda85c5491f121ff42601fdc2a035fb0c01f73d26e3c7b4f2ad13ba0be3639646ceb4d7d67b125ff154340dd3868e2308513ec9b6c64c82b772d617b47197a662c2a3cdf7e46d003fb1cddb8892d7111a099344bbda868b614cb275bdabdd714faafa5e16056a9ce5e78b7eed8991b381ddd47d5b705b7f35f447b1ea9404da1c19cc0a07dc1fbef0a7a96b1d7fdc1bccae3a4c4a60d53e0db61266fee9adf853dcef3d2e7bc9df242f9049d4cdea1f265bf98b07d07b55160293105360124a1b1dd574a1ed6d583a1f01fa0e97181e4ceaff1d7fd52963d251a7b6893a8cfb1b73970970afae39c8c8bbd89f616ff5752e1d8d9755e4a82531fc40e7d382f797a2131503bed7124b1c7fb9e816db5a9c9f8bbb946f26b2935742fdb4b7a08ea4cd5d3d74b7eba7bb8742545970377051b2f9d79f92a226119b15cd10da67753c0448876eed754f982fdde35575b5083727d64bcfa5165d12674e3601145b32fa03df2d04c08197c229b9c3afe53cd9de462bb9756d04a0176bf9030b95e7ea659f9237d2c241e1155b0026f627dbd21214d4918962984ba67cfeec7c97abe0273c23edbb4b2ab2ae394657a4f738bdd82f1385bf0f6e31d8c5abc377db137385d0e545b545fc53ecfe5621cc9d9ea84e5757e299f8f2e0a48ea2321d9d9144e40e58219ade594a2547ddddd81f5ec0219f6d1f50ae0e69a7375f8bd2596600ef75dc89cc794e884d0144b80fdd888c3b9f0a3a1d2ddb125f5a9f5e1505324cbfcb7bd4f9a70ddbb4422fd655a16a18ecaae319e69a6269d43a5ca9832201b3a2c30095564c897d1be49b61b50770e1624c61d8df6d34a5492ef4db9e01de44b85350381d79cbc19208e21669e6b2dcb79119d24d6a085115b52625a448e52514cd172538b2aa086f453200ea4917a0ce8106e47d5b2c788f75038e335ad354b515b0cdb345f9a6b055205ec1063dfeed058b1a156d341d23c744247a70c1d0399951dbc20608607f1af9a13ba54fc3f09c9bcbb58590ceb5c3221637c9135758a8c7b4080395f8701f9942a05485741df8ccff6ecd68b4e24535103eaa33f65db65e16a90a0de87e1f5de66905c105c8aeb48d0b35737ad94822d7acb228cfe1174d0e360232363495a8d57bdedc12526ad4094a3fd8e241958d07ad95d1d91afac9d116fb7c518693bceb05b7e67a926eaaba9363d9e58c01bc3199ed9226f7b925311d2a42030d0d254ccdca15ff32c59fd5f7e2fd8de1d91edb15747e89df9a350684fabe0b2116071aa4c0c76721cbc487727b6f121e8e1c75ec0a866d38a9b3282c2a622d5f0555704909fa23834fe4015935504677a05d03b704b8dea3250fbd812129df2ff7eaa9c405de2e2a322b2d5af5d10bfe807941ca3e6253c401b572c558a97dd2776b6d1f588fea00272a21c155e28793784978d2485115870349bb3d6a10e8b4fd26f3d3b1a920950915106edb05d6bcbb17a28483d86f4424876a040129f6f363541c6f4aab5bc8adae5483729f37d79358b2a88c0fb44e461349d0eb5c39a77a6570d5d528c28f76759857083646f5e2a7f69d4a5133a250b2390e543adecafa353a7a4eb79e0605d1d5083554c895d365d8df746115c239d1f62037c1c337f63b57d3b65313397a5e873b9d2d148d9faa0d46be43eee6e3b10661923b9483"}, + {"0000000000000000000000000000000000000000000000000000000000000814", "002dcbcea2067224e9a4e1630130bbc97e077a88701560257812e62b29c8ea85c1f5303309b1c1ded69e0637f4e009169165a8034574c1bb40a57d70fe34f85cbf3f35ead9f2df7753362c52d0a94e77bafa86c00624451b02834a90f23105ac6cd9ad9d8f3355210b068f47350be9c1ebdf1681948cfddd70ee71882fdb2a50a521855f46b3d75da42cf57f95b559ff3176de848f4558dff8d337cdd6ba285fe8e04ed04d9c263700fea86879891a197b981140a7956c9851a7c91e591b079d4f6f1ed45719dfb2de01f80ab97680acc90a1d08dedd2cd81742c0d272014c72ec1d950d70837358495c820ba1982d52d4a7cc407fadc6325d57e7cd016b300d8805f4e1165eb2386ee06545792f9d374b17080d7558ec3c61dac013e52ab2d1420e81938773074629de714da8734d41318a5913b6d171867c2181213544f53548e42c5106753a167e58e2462c1dea4900692eda02a1b15f716214cd9dbeeaca575a9f9d8004009ab567af93b7e3098449853a84d61bf3759ccc1776d916b81857d9707ce41b3dae5305ca6abb95555315e56f027da25ff52e35d21975539aa1eb35244c0792034d2569dd77a97f6527d57f6051a53cffcbb40e9b2303ae5df9f98dd9b3d51371052359267ae3ac1ac0bdd365b052fbf33865ce2965b29ae8c1599d2e5d0d75d8fc621faba67e2701acf3a36606ff33d69a037198937cc2b0f3f2d16039694cdfcaefc99e9eab0f551650405cd7971c8042239749e1c9f8c83bd589349e6e9dee0de41cba4e247e47eaa585517a0f8cb13e78d2a7cd94237ef70af6930076fff2141fbe57990440c328f7d0db41bae5181329651dcb84d33fdc03434f47f3fc60a2716152abf0ccf7ae0b461cbd47f41efd7202095aab27d3e4149c16b091fe60f2f6a2a73051928c886be397b534e66e4038795ac00a5c3a2f01745ccd0b16ba693ec5907d297b2da2900d9f2ab2213fe1348208b66af10a7538f67f08626b60f399e223db478af42b7a677e0ff301ae99543dada75c226e34060a8f64eaf745bff766bdbd413355f3d07d94b80669b0997cde9c59d06351f781e82bc8d87036d8402b6d1553b991aadaa3e8a56e355341d296ca779279074a0f986b8c04826244f45defef237d5f2bb0f9ea1041f9de51946ef7f63e11317a54d14668705570e5024da5f51745261e29bf8049ae8cb1cca4d53d2ecc09b0ac3c1791918dffce6351b8391789d0ffb81b5a17570b807da53eebbababba3f27ffe8f659b9bdac84b4c04e4e59fe4e196b889819cf94d6b1d47398fba095cda45d084530c4e99258c52fbe55982566cb6f40972f698d467269396efd20ad65e86471537df5f9a19cd15985354ebbba6ea84f312c6068a12dddafca41d148373b653252f3abc058c277ef39675d4dfef2f047944cd6802b533b719b1565437780f2d0d5bde0d155db5d3aad1ac4ef198c1bd90b4fff47b2dfa299d3ab3be4efcab08dfa3a5666ec0c2130e8c80d94a304319daa1ead1675da8ced6e5813bc30dc235ba5f8c06e846e64aaf574dd5d9c21693b502d266a2f9ba070d4d83bf3e973b4ec7493ad06edc422359bbfcc92f0f180d4e1a6d75cd9e81a134443ac8f4d5e35210741f8e660bf7e3dce73287662ac04c2dff6b9e7cb10a085980d658e0242fe41c62a395afe4e176c015e407367e0b9c69a64ac1f116f68a6b425cf27e80f9fd4f148c9a38504eeb60ac97d55b08ce0dfdbfd23fcd2f18ea4d692586adacbb0b13c8ed9fde2666f1da795f0928597b4721b7d39b460323b6a58a913dd395f8190f682547a41336e0e61d2165bed6617cede8bfd0830b418a45e5892a6ab2f5b1535f4103f6ce64bb157a0ecd3934f810af73242e41bc1b21c90af3811d6dce"}, + {"0000000000000000000000000000000000000000000000000000000000000ef7", "00799da0c101fb77a1bad0387967633083ac0fc38a2bbdf43cc3148a0b3443a3adaade32f9c9089ce2590dded8a49acaa0ab48f8c5173f3bbdfd830155c91325691c2c3da38fa3453a032ad91e14f1d0f8f879c50aa7d0b666d05dbce143a0f40ac67da44178b616460d3385b956ec234b9918c21c37b0040dc2f52f7e2b176df59f33705315b5c1596c14effda28cdb5f1fc62973d7a0c04b19f69ada445b48ee4015fe56732fd4067618377ce0c9597e5b65186ee5230b0096f9094014f8ea48c2c6336bcc91b952eaefb0a37becddaa050ddac878a1146ba7c88fc388679f615ddefa98bbf6101809dd6230f081aa1b339e60e42fb971fdaba0230bb570d0300a33b3d65d3501d16bd721e99b1103625b2353c8a0369f15d21196e1716a44cad3791a2a6517114f23f2e233b5632a15c967fdf5bf27ee5dd3cd4194aa1a09ad653dd6c0d47de9c57e9b6fc7dfcd2400f2b798804c7b4b30e1643cb3cdc0f1c9b1181d2f01e0cbade880f081e36f9112ad3c400b789d7ebd21128a8481bcdbc59d002ce4ba8e7ef9a5b0d4f4500768fcafc4a93974aff57ed6f2c4be36c244339be02c02324bf93b987fcde54652676347911119e13ba2fb17338637d8618701c6aed3f98542fe65c8b7fbe3ec04c7345acd6a747583c96321d6ec0c8fb9d35f01631b58029d2f4e16391be575334bd683331819dd357a04e15923b4c1971ed0bcb453abaaf01d68a75b55770c99be39681efa4b6938c3624eff4105f0cb94cc4f13fbba8cfd986d3df385616aeef15750dad43a1f7a21f919be9dcf8023303fd296cf3bfc08ae968e00440ca1c2280562649f1a2ba6b37135daa1e4fdbbd289165325307bb4a05be98c0640513fe0a3edf0dff6f012d75d0d43ae2ff1dfd2620a881ad44948be923512226e6b5723576779546933a55039e0f9c9563d9de600d5f693612da5d7a497f063eb8d8bb2e1caddcb8c2cd185f7ca0db92f2ecfd5da65b1b69e1b6614d9be16243ab572d99a436af15264cd4da90edaa51be1a074ca047ab32670f3a5f209f98b71a7969ffb1e842501ad88976280d324a9cdb7d565563782b4027c0ead056ca1aa9e491fafecf98717e45f45a201927c1a5209c20f9360f0717f916bb3547d502e0dfbacd14cb622b95976b091ac592705c3b78e45ecf5e9649a845a02eee964e507e1b293edc8894851f0526f6b3d64a7052ed1efe545bdbb1c322149f9483ee575fb94896d11ab04ecb71ec3c7304622365e7afeba3988dbf4461d9d4bd0ad161d7ebabc93e0c4fea8fa0f185dcff406429a959ca9f52fe19da2db25668c69272bbbf2a61aa5a91d454eb409a9e7d3774b6354158e0418a73026283797c19bd6470d47f638a5e203119fbecf6c532a62034072724663e38c1588b5380fb3f843bfcba704c253acc542ff8b41b791471a4c2c5476642d93ff3199c4953859006f2c43836187798a6b33965c715a0d5d7284d0264f3d412a75081de1060f22bc9a0cad35770bb7abd1dda55d24d3abdc289650fe745b2a7815c32250938f06d9fccea19735c4c5421ee7f10ca05a76344a57ecd02974e2fc6961ff952f4652de2ae73e226df5f15d4d25305db652de371da67aacb77ee864f6a7ea40302791a55849f5c56ac152f0ffdf682b07c579a167a0f781f158d3db72c49b953a56fea6aa2941e7ba60e6f5693e3924a190d6a3eaf4ba1a354c0967cb9bfee6f823bc05f1a62835d99e78f4fe9d421242745762108ffb540c41392cca187c75867cbda70f9489d3ed9be9f9907f855f33c8bec1909b10260215949a957a957d8b8d51f5613a31b5459bcbad819f187087ff3bb1a891a0d404f2f17bcb9f66ad5ef94137cf936b1fe6acdfceca156401391862563d36b994"}, + {"00000000000000000000000000000000000000000000000000000000000006ba", "0028a318f303919bb5cfd1944b19edb2842d1e9af500c222ba82317cffed8d948fe8ee8af55c6178edc10a2855c6528462c3c580411e067ed425a6e75f722d1e17af2cada25419e80c13607ce9a30d6233ac027c0794dcb9202e6289eb8772ffdf28f22540dc4bb63313c9d3f2b39a0feff75cf5cded6843e6b54afae34919e210e11456584ebc4705bf7b685eae2df592132f46f0767bd8dcbd87f341e4f31c659d617595f69ca700e56517a006a63b5a3ae2045ae18e797b524d7a3316b25e824f60dd1bfd27f451ce272e09f7039e65f51751bfda37891827a8a1623d57360a1e4ca41ecd2c20a5470089d929f95b7fb24bd02b7268cf9b8e6b5105d778cdbd47191b78df18b5c053c9eaece73d92250734ac98eb4a1f317639d196a1f3a30d275c154c950a66a44628479f5fc21030a77e0c855190bbf44d871c6a26d5f00edf19ff23c1d005e8c90de73952dbb2002bdaee051654dfbb37312b746b73b8a3803486220090bd84ff1beabdadf052df63eb8b81d18ff8161219c9d7179b0a3ec14de96312502807655d11f59bd31d3e472a9a4ce559408156ab58c4c3edcbf393984b05abad0add85d04d069c5292f129a6fb1c00fd583324f2ab24730bb836849423cb16d9a5850f68be85130aa872379f11330f119d944a382cea094f837b980f147ab514f2aa0d75abbab6d263dc75e2c3dbbd39710053ed301481d2447644868713c03a22c9317f347c0ae6e78be305fc83c304f719c4cfe0ddce71b2ae3307e9c8d3f6585c15508ab16ff395b2b738a33c3e8f4bf6af7dd65afd71b5466551d45417d33a275afe940217bd14eaf0016de606b2b493eebc22aca2d6886407586eddcb4d4fafda9bb29ca5dc6649018a8fb19b07acc362abc639dc8a2b90d503460372c027177f84157ca2b6e3572f23660da6692d6acd8e52479c4e7a01901821f88b99b74c7e448fd9cc1ad5316b2dafbf1cdc2387211c5966f3f252f039ae17c12ba54c57181f608e55b456f1f1cfb6e4b7d4ecea5d41a092e3bb2ce9ec171a9378e53f8f73aac8c26551706ab75dbb0cb9e8baf74d45d281074aef52749b33e3e4dfbc6c436f8f4e369eb33d1bd6e6ab21581acadd5a3c89bd1521992c036d36d3bfab41b848c6a8cd3615d4f5ae1add24c12b5a4dedafcb65bb3efc54aa78179d27b50aed3df7d457109ad32096cf5a3f97d9f478d05c394d888cf3c8db907d4cf7950de54fa5d1fafbfabbf415dd9ebde02596fb938211d6cea57dd95fd51b2a361939469406fe5ed9f328d26825968cd4c22d4d281e0e619db551476d104631c334f2c54071243555578445c3236872ad31cd8019f5f0bc38010a9df03b52eb49497697389734daf00445c268e81dbb0b0bbe6b9f5106d2a18ad79197949f768b3c3d0345c2d93de88a01f8abb0ea22f387a6cfb8870fcdbedfdc16df6fb4073a6458a8c3b3c316c18274f2a70011433afb930714a1caee292108473a44b60a3465e7aa3f09de6b3917da62c54dc9345c9cc1a4cd20dd297d69a55efb5d039b5a4cd9c5de963c41b0b9d742432e121f5c192d127b1d4b7306e0ee5d9a6540cfafe9d9a10f5b71032e23ef303474ded3cb0f4a1b0851159397d0de08d136bffdf9f314fbf560310691007b9f65f869d30502051147925aa65ab543a252bff72f1355f905bfa0590ef1dcfd4f53aaa9ea74f0f7b4f79eb61d4cd8754a0ac81fd4cb963431138e38162dfac39a8b74f7efb20ee6cccca94f2cffb821d905fc6a676729011c613f11677729825f31af2e23f28b4aa07f523ca71ef2d2253f3fad05d408bd307635c7d1d96805c4e38e2df819211f0c7a4aad05d88102142470c637d19fdf97101ea38be25c1858d923bda7f608dac8ef07e6393780"}, + {"0000000000000000000000000000000000000000000000000000000000001a51", "00acf2983b8d2b553b5f42f5a2e1e851ee843f1e442796d2f9515f600531eac55b673f1f116de6dfde3f10cd7f87ac45dd86e60bd475cea3c0d1644a53b7e372034710f16037a9864a37f81b7ca64a88b61dbdd712727c6860a20b2513a4617151714ce133c33eb14b24e8236559e02a29abd5a993c3f65f42a9e9fbc994130da794495830bb3d3d33b9697e9505a9015de3ac1e7bfed95f0f714168f3d71046717192a2551c191000e38f0b2238d65fe74b50bdab6f4a9932269204cf35334a825861de172c4fd9a1567909ba9f71dfa4eb036004b2b869b669c305d0b488b332b8aecff1e8bd5393a6b2249ac8f717ee78f2496468ab2fab5f36410666fd8e5f4d9a4c97e5e1a9237932fa065af27d7034203eea9c61314bda42867b2038a5b9e3e3904c6a082b18547ed2e210d1e9a0a3bf5ef5c4c5b6f99f6e387a3e8d8e28a5fbfcc7a4b985a75a86c7fb5b5aa10536c4597121fe1f1f1fd2e64d384355125a89b4b4428d1de0942acc6f9d24255496ba432263a8d8025431f0bbbe6f586db9271616991dee0bf7bb3a5f0cac3acea45696d7002320e153e75a68fb4e6270fec4511ce22383b191e1d1d4f8b3f100e565d906cb2fb5fd232f13ca2f6e4f9bb5b293ea394428d97c55d369fa1d659745f262dadf870eb7322defa11b7bb79e98982d2ceda0a09d6f7b61120718c43a7246ca2a5ddd240aa7bc6d1dda0259ea953e1c0179f8af9b35fde1b51d6ff13e32d4be35c2b5c1f8ec71474cacf2ec8b250d667b5bd1d0cf7497f91320a6e980ad2662fd48bd1b751d219187e81d2d809686b6b8d55f581afb2e2e177c7ca0243538a1f929a27ff3d51479015fd17bde33d66cfa3ed1ded58d6266f5a5771509d986f60ef3183f8d1f5fcdb67927b532842fc5f6f1533f0f305c186ec72a3f507ea1a523738604c515332ecc99afe901075343288ca231aefe27815dd724d6d36e5ca889020223b3d6668e5dfc9da03ab893a2d08faffe7c3f097ef06d705cb947a6c3656342609bd65724572b747a018c37b5aed3d9b8e4c9b4fbd928d31fa8de19691215a73c7b61d3997325e2ab4fbc1409c468fd0027333a8e83bb681135a5c213d9c23b170580c7be04e51a3ba3a83c11193ed16e94fa447c03e5ee7955663e1ead35372d323315d995f492d7c71fdebd03de8702018d9d1e2740b2cec397422f2a3bab595e0e1a801a3fa0b4484e557b2947ad745f47ab433e55801b30d904cbcbadc9ec3c1588a46053e799c49e76c6f5ce8215dc12e619cda222960a38f81d60274acd19185a9f0725271b1e4356fa9ce548a77759ffbab6637b584d3496d7e395656b2baecdd4a6982f60c599225e59fb24513273da167a97092338a535454e63ac575d71f93bdbbc67aae3ace121d5251182fe236d66ecfc39da033d4633eb48e44f22f6b1d3c8db50f97922b52a69225394631799291d35e91466dc3f0a561bf8d7219f0ae6f6d3f347a6757d10a2f19feb892eef7e594687176c21fd43c6b98106ccb223a9f1fa2aec71dea2400b277d9e8288902e9fe9625f2d934634f4888b4503179815f47651125acdfa82a678d83c2b32df3d4c4e0f01fa6faa54091179f8c1ffffb78e0603c3d32bfe32cdc5258ae810ff4fb656a99638b16f0f453aaa54038790df64634e655642534b4e2a148dd1c575004e12b3a96a1329bc87d586a603a65b03728a7854ed362563aedc8e545844de5336443436c78690837dc47127e1a7dfdfd861b9673932c81e96b3ad23c89761e81106512a65860d612bf521d05f12e0bcebd0de223d18f9b48b0ec9701ffb646835e37ff35a1855313c1119089522c60756ad25ef224dacfd883d35989b850d407e3487a3d1edc1e839889833cf55fa81eb5fa019"}, + {"0000000000000000000000000000000000000000000000000000000000000745", "00007bcd314866a1e990349f9368e78fddacbef9a91ad153a167269d979e2b582dd45739523d20b3d5b013e175f17b8b9e1d7052e2c8f030b21d94e4f153792d1e56ec4c0b684d93c76376d8523087b6f65f251e0ad9d2b33989cd9d0b68d1a9ca977f3e67e83e0e62114beed2a7dc216b82e584fc7ada00966a8a7dec2927cef4289f4d3a60ca8484fa3f4a29e6ab453d613e37d9a6ac1d4f3b9eb91a13a290c856931209fd85c0060dd3e111032ac30f88a2f77b6a0fe4db0850b2d31c791f5e1f622b4b401f82736f463c8192f7ba659d0bee61c39d125f549774d13c112986806303ef16fc0e97d14a3ad487a711c0d8a7c2d8e766a3b635cf661ad5ff122f15946b8fecd2c17c50a456785cd8e9da2ee32b0157b2066196627479e863123dc9d45ac0a430ee7e7a979905532471630f1d7094f92fe40a273831223d32c129bd538f9973e983318ae1a32f2f79ad009ce722e8040d1ceb2c22598eb8daae7467f9835d0a8b8b714a42c68c245493bbf17ebfb130aeddc74b1c98047093c8612976f538631265a59356a61e07cd626ff5226ef2542de72bfcd40fef79937c6d9f454a00a26cd7cc051694b557b8b210777162c7cb5d84383a90737e57e272ffbd8b55b9e56f88fa2c57f43da40796b5a8d905f09974bcb13d859fbdfb87573c95cd09980247d0d9ec6385ef2a0d17603b129fa09f2cdc0144c77284009f46957515a3d660573da23c4f0ee9105ddac45109547973ddeb1454e1ee07c2c85e562c07589cdfce5b991561facafa13792ad3472f7a6b5332182feb46dca3f9b49d5492add0e4ba23d032f4d20ec104ba76284a33a9e76650506891d302081d0ddd44ca94671ddce9c51a5506956cf03be70df37a4885100600f4ce245c33db252163cccd4da5d9da7abba1290ba5a9bc249f5b71d202d1176dcebf0324fefd9401c77dc2404bb60ade8f043b542ff1caa3e95f669705e5eb80e8cf170fac46430295fed38b2e21bc6591060952f6ce52368ac9a5a2c72676808cbd49d1edea0e585e662b493d3ae092b5253a5a094e350e52df0402b8595001475397e164b2e4c41c965d183efc97a53aca5373ea0fc42b376e14c9b22ce7d72c80bdfd6307451ee5fe83c2fdbc2371fabc5c9be0c36d067f3008ae7f0ea416f77bfe4e51dfa3ccade0c167e95c5c08e9b73595641b7deb0b43b7c4e1f036d8121ec4cc132e5c3013b4debbb3eb87a757c69f4abaa3d8079c14090ceaa1cc5d5492eae47dbe5f6b3292acb53d5f1c8dca0a700dfbba97cc37bce65683e617be1256f80ad91ca51f4c597cf5c280e3a6a9aca28f4b35820a49383a7589567b3906e0c66892d93005d434f189ee10ca19a78678d5afde28329ea8b17aff95bc7d4019193f223315ec9191838271ba9d7bc6adf064f4f8fd06c7ee897815e091ee57026a25ce6b4933f5fac4c231bc4502d01ba79f3a18141b0ae2b539c026b7fe063c0575e7c5536ec6b85ae92ba67072b282c29cd7516442479ea53b412bfd41c7351f78650dd7f51dd27307601fa30b6a5597efb726448dd4b15de9b07225c225eabf70cc1e9981f887f671e07b79d307b59956740a5a39bff4128f2b6d84d6545ffa407f27521b385711b96c9703e177a9472b4a8552e0cd5ad8dafb36340787251672958545f9b815cff43dd15a81b6d717781ed10a866cae4e1378ba34960f63aa65dbb2d2b797163f327a62d2abf6be24853058dbfb72de63ff7e25213ed44682c8e1c9242508d4f951f36a55f85be7cd27b81f965fd1a46ecdfa738e559c7e8e81fdbd3d9b4b7b7caa8e58576bdf8ec53371318e6972dbee0eae3ffb3d1b911aa2acf5225422e963d7196844ee3fdd6d506e198dac8909a23d78ba8d798c937ed2dc20b0"}, + {"0000000000000000000000000000000000000000000000000000000000000ab8", "000f33e1c616ae3af89e12cd04e6d35fc370bea3a70f2cf14e61dd7f04f31ec4498863e9ee9b5a58df1c06c6a5e7d14c695dbfa561be2fe9239df32c78841418f9af69efe7467d771ef555aa67b68aa202bb6eeb01f7426b6ad0715d87dcd0985b1f275c9a307fee650d5a25bf9ddc7021fd5d25bfbd4c476751995e75021848f917b647007a5da8a2b258a529d0b3280b398f4898b4eff159403d776837a22d4445af50a61d0d5303c67ba63b815d0647a46b86416d334fd1419f1c6d0e31f4d8424e5b2aed4d53e988bdc66d71fedf300b0497951bd141b44e9d2450cf1662683d1cf1d6f8c6145af19e55467600c97d025bbd4a484eb7b03c3062065f6c0c4848c106eed4e2ac04f46a80c056943c290a9530e6cf15af8315aad1c91942f5351955150856108fc2bc8e20ad777868674782c5d506a78bbaec641761bd9f390844c7ba40729448326a0ab542174a1500feb4e5ee8b65dcfb61b02220d93171efa1de8fa524a0bed9d0cba120d688c2dc27259f7d170d3990e007166b6767df6793e938e31505345cf31eb87e813a26fecdffb7d01bf530c2f632677ac341b30fb4b161019ddd1ac5d3261d365812028ffa342d7d0bfeabfa27b3947d9f4cad9cc8119436734a7d812f5f795bb603773c4f474ed3116b95c57ab742674dcf7e37406e0835a251e7460b30a85681ecf4114155004e4a37f701081a1e9754eb29c487e51ac8d8dac17064cd65911ba00d6fbbd9a1d3900d6398f672ffae665b75753601e3be99d630b92bff91b435cc2a08fe5869bef483183e0a4f99b3ed45c8be7253995115c5ebfa358460082874ea30c6652d71c39326554852258dbfefa22b19e62658d023dd9771c4487db35ae54772039e1d091f1ef66b4cd49707cd4a62a142cf9da186bc9fe9c4377f8b3db29dcccd12ad58b899fb4426bb385ef0830084fdeab76a0adbed8311d188f34a90f8b45bccdf3bb92473ea5d8f496f70e6f0e07b05a63199f2b4300cdd75271b8508fc30c5f3a1e921c2eb69db9c0c992459ce78f84ae85745d739fdefe1f6a2b2823b921217c1214f6e8843a302ba32213d3146c53a82f1192c3473ca017f140a0cc788b61b17c4335992251211b31e5b54d3ab1d0e4fcc6c1249344b2d91b4277c7af52391beb73c23699925532403c0f806ada1d6752a1808fa140b734ef128f6eac5abc2dca6b2e83d19dfc30cb5407121b6592fbdc3f0d34b143e9ce402f2f5970c891b22a58c936cde6972346e32335e2397bb602a143981117072f141ff0641f92a993930f2850eb839221623969320cefbc1dc346281d05739366e6ba94a335f27aae4a55ba186f1f83f52ef781a487c7eeb2843091245e9e8fe43a669f4a62abd6f3a2094383a2259941c2112ddf08be18e87b24bc114ffa2a93d632902425c1b3ce3fec9c5e12224f960bcade6b8754a3139e42f89eb62b551e828a3b7ae6e4d854da47cc21716a9eeaf0d5c8090f0406607dad26c55f3b67060cc26d4e46023ec1c2db8d81379c76790963ff2feb3700c1e532b33d62a6102de42affbf8d7c64795d75fed1d82bcf5fd1c8005a6749272a0e90ce1f199cfbd77338a5e354a8d66187393545fdfc6140e840e5a67f93eacca87adde569313b52c01b2f58baf46993c0d3f03315e571d163d1f0058c0e5974f1cbd258baa94d210b765f01b2b13615ed8b30ea4d628cb1f865a91b7083d4cb3ca07d55bf8c8c78aeae869323d4699cef01b8f642f20d137250d88936fa03a6c8d47ec1c68580768e347fad8fb10e64bf38383fdf6d8e110b9446c0fb7820da8087a9b553282a7c33c3e61f81f8fe8cd128d0638aacfdaa0ed3ee16c1896619a176d53f098195cf207976fba23d871c966e756e896a33b757fb6"}, + {"0000000000000000000000000000000000000000000000000000000000004c0c", "00a3d170da84e1fb455e10ea16ac9f286ee38e098611f81f374bd2f68fff1f01977633316e052c3596841ff3343626b61e11e03294e5e5aedb86899438b572289ab757541c86b1b6e22434e8d756b927db4d63a808ff3d695d19ad2b9dd895a8233473f64a0b196e8d1ff4e6ffc45fb4b552e652d113e8ccf4d852dfe8171ab7cc12d45b1c5f42ac81e2645b97d21b02bd997a4d12674c0966bcf1d1387826ca57ef7613bd30d19704660cbd1c2442d9eb89f60df2c3ea5990db30aee21c5a153ecfdd2cdf19dbd40d94fb4dd50430d63f1b48beaeae29e3becff8096712c9f5b9c206c1be0e255a9f3e3733de289bfd03f73d3aee176e68767b11221a413d79145c6b4f7c67b43fa8e68ffe2ca7b995072ea67a92776d2179f32d65bd6b794c570d927bfaa52ef83f0bb0128161203185741ecee7f766945e6b193107f2b6d2126249c03f032be3617d3f2e345a2cd100e16d528b4b56ef8a41e997ea5d72f77df35f4aaf074ead2d504934a348f9b26ec0ba80813327fec63623ca93b8d00a9df98dae8718b5648ade36485f16b05abab3e33ee90b255de37a0ab3644e5b68259b565d0c99d4e624ad8489fda880d5bce5af2524166c2e82159da32d42458cc9d32d54201bc5f711f9e77d2a951df7ec9306759f15b6c7e325612c8313699b3d2cd142de8e645053a0339444f95256d5fa2e80bad8d31f01ba695ff533fc61f944915d4c10d8885d12b7103d098c5ed5a0839a773f72f23f1d5ca5a60bdb11577d0c58429720c84799e72862c106485cb4ed87ff2d3c140ed1579197b648ec1fb167cb77f17cefe2ba5c8c0bcbd5353bcd46248c1c29b579740e5a84045b1b3a1ef4bb110294892703f89a7772f4d7b76bf5bc88321af506dde7993459fd00832d884133c9579a526021326eb3a18f541ca530503af531664dfefa305da10802cce6e57d5cbd4159cf16f873fc7c0f78573e4fe727f3b3bc9b4e01a6c43ee79f1fc0df6b09ef9f44ac06bc8cab0048bf78d57cc22bdfea8ac58c63b3b8ec179a750043d7b3e7e28104d7bd7edb59752175cd7e1256e62f03ea3635a278a4c024cc3215f146752ddd215f1992004adfea6f7d92b91db9a982f2d73e24391a9409fc7c749ed5f798729bd2a4dbe9cacf548b552ee87592570c0ce972b9872defd1e17ae82abcc6dc0300be9a5b9751b9bd5d92be23656594f112ede48218a84c9c2ccbf517602f360d71f61cf2e6a158e68e1d6c7aa2541ad611ce2e37776bc903a35d411fbc664500ea7a5fe074111f561781266d99ca4d9fd865810d50e8b6409d0403c138c6d0007514f31e96bbb761615846c208dd2a9d4453965755f4f2fed4cdfad5c2142625aee6cc9efb082b350b724526ae0c97d546841a5655386014d58fe6b1837aa4eb6dd1d84bdb74d20748a3839863cdc7e52dd351f99c7d46a5e997ecf4086d46258cd4104f044b1382e67be6d51d790fce9b09934678e50e501996275547b651a5156b0391cb801a5afea1378e3a8b3781b4baded155ca635cb84f081986bab872971fefbf1793df012abe175324bd09162e79366a077a4efdfc0a440706b903016576cd8a601d46b1b62ddca6136728a40dba6331c969b38e991f644b74f5611a252dfd790701ffd033e2291c1f1dbf08a5d58d3c523209e19643bfa03b2c592484b74d6e0d1eec4e6ba4372924fea10669f3953c85626b03b32cff1efc5e2b3e1160a47634b23a42a1b52779f4ce3a09ad685910a9b6ed42a5840fd3773aa07f95a8c60e7dcf4b8b049ecf28e6f2705ccc0309dd70d48222100e954c3044c37658b0f50ccef997d6cb465c229d24b14bd1734e801ce2906385be508d1272ab9c0d0a28fe94a04fcd1c1cd49d130378a456a564190d9a8d"}, + {"0000000000000000000000000000000000000000000000000000000000000701", "01eaa2a648e32f3786cff12ab514331051f1062aca0efec5676060d13f5d1674a13655f131ffbb1e9160087ce47e809261599905d6a02b43964746afdd469d20e01a1653603209b590c5d217c1d6e1e5f63a66fc0271f10e8947c5e790fe61288719cfbee4953ef3042c6a9fda61d432fbabeb967d76e4237f6f1edc94dd03c6c98ec54e6d3369be6482ac259846060a95af4a03dcdd1d57a083d55d3a8186ca62b6d151b972313603c167c2044a53d6b6ee537e64e8e50d6e4f5ebeb142784638ec2e5bbf7d7afb7800eb0b82eb0679c2dd292ada57d8560f412b411cede0f75bef982fdd83aa3b4f1c8c0f95fec740da8963ebe1cf4ea6173f0995076f787316c3dfaac6d8d84742fa51f6670b99f2e43a0dc9d723e7c6b5a4ad37be557c7e9706675b2c3f084b7e68194504a3e7cf7a7e43f7ea0f98b37e2c14275c66de680e05f7c1e376336bd99366bbd11e2c0a024149026c10a1db3f35b7c4f877fad1ff47d262ac42aa7a19d36ef597e1a66744b175f942cd725b876b0d48bcb187c68c1e363ed43ba169c02227cf37b99d181bd120054b37a87cfa23d592cab7a3982d1f152905684b7b6e023659db9a236ecce42ada431ad55a6a15a395368e46650f525f742f643a105db032529b2a11f6fe76aac8f23c8382a2bf3ebb02697de253b0082d3dc281dbe59c3d86d2b33523fb3cf75c845cd241045ae0cc59e0c5675418f1fdf8addbccd037bcb0912f3ffdf30011f9410666a61553dc5513c90cdeee771c01956ea1c8b2c0813a52ac1368448df02a3af42a3f996e3297f239c5b25767d9fcf6e87e39ea7a0dc006382d8f6cd4edc70645e3701ee430860f33f840e20bdc4148cb89cb9d0c6581090ccb8c82851d5c2520090fe3a9231c6a676809d8c2f4e6ff6a4bd75ff2001975597c888affd74e5f81af92eca995fe1c1b02d30235a786ed45d5b54682e0a81a876d0ea85b376a0c0b87cfc06a527015f0c5d593bc318b66fbda1b552404dd1a9e91b85b7be04095bb8c7d13b9c2a8dbd5ed183d9e6ca02655e1b84061e40d53a48a9ed69595f70638dd87f590e0212056f7a51b619bd20b3bb9412806a443b32e0c0c5ce091a6de1f7080be0c757b787c12bb97fe9ecc74f7a3a1068f6b6c3b72276271e5571bfe2b397c513588db9ea626a7e0c6323695f81fc0104bbf615c48b5894ac57216f664f3330f5dfc232b6a4ff681c31afe7be24546fb447eb0aba5153dbbf01325a93385491b4c76f077f93dc3226ec0669bbe742b10b9fb6fd027cf4720334a70f54f39b94d32520c12f32a5f3c925053d65bf313fbcb4c95f613d8ba17241c96c6b4cba0896dc00af472f0cef78a65dffc8d1c0fa70311cea769429244861a3acfc96642598061243cfcc79915aca9006e12b694fdde3d06ac8ee9a802a99095c9298debb0a9d0bb30604f6dc4b23ce666125fd47513126aed272aa6cad17c4357219cba87f5059f11fcaf112afa937a811236bed40d12996e9e210c8f2970c7ddfde7d6ab36eba9cefc9ea45f9cca3209888e3b3496bf6b80bdd4c052fc5a733a837cb9201094f3d1b84be21675aca305f0ea67b594866e63401769da98b3c7eea18bb8c5a886d7ba82c8433fb3491fb5c40b486a79a788b3f57ea0dcaf37254c7b01c0062e513cc25740d50d462545ffea75e7122f9d4d022b209f2ac9503e2fb75629531062967ac8dc5ece0908c4a9f5560c39b7b4b621e47af35fcd6421d431b9194283892e651bbff51982ee72aed29dab59cf4742086bc63397d6178ba8c323aed1fe4ced295ef3d69c5e40bd89ee19ec330280c6793eeb7c1e5f7dbddb0d2bcf9610e2ceee6d5144e77e0dbcaf9a7b7e3f66135ba707d851b55c97d03d0819365a82360d56dc927f"}, + {"0000000000000000000000000000000000000000000000000000000000000794", "0016ed57500cc589036424a36248b84317113a57e92fba0c386f3486bdb60bf62c593b636a643414c2272ca9a45519cf5f8ffeb7c48abce681b17a1c72d0804233c59c6c9e504d1a71e76bf765bc9290539b97d80f7b7a658358cca7010cf2b5cc264f50bf2bdf77e115cc36290f20b33fd65833f538bcf2496a6b5ba91b167abb92ca4b81d7dfe871e1ee2b49921426df612a18641f215d5e37efd3a55327dea9e216d4885bc60a008d94bf85c9dc2922aba05a737848c2c7beda244e10a28220e91bc6235e3de20155bcd72a3bce9dfe320952adc8a92480a5cecdc74a335eb77e3ef8bdeb1a2ab625f5edaecae7be2c02cb6837860a67f09abe69047e5e19af94bb88b63160d1ebd026e0609425cf6a111eb5c668197d91e54ff387ded37aedd8ddff1da20a562f29c5032f6846bdd19227159ec902d7757e0629e21293de6eec95e5bc430e1eb022570b17987cb000517843915103e172fb71dbfee005b36d711da1ff3b228c7ab19b2f2f5619960a2fe357c20e285cd52f0d686bc50605c58f2f1ec2c9d14f2258b98f7947be24a016cedfd7fbb7dfd263d17cd2dde9d67fdcbe500460d5bc830877ddf4716142b1ba2954e2506f90e9083c6662f2850f6abfe38187facfe04520af2cff6b20cebc59b2e12f434c14235f529c2b20fef033c90628b483409ca09ed7c063cbe9afe47b4efb0e7ae6e100967b0e25e041a1bdd912c39342967a5e4e78eed95855bf9e6259f6d38f77c6d789f8013a793ad93e64036aa0e0b19506ff71f28050b9f6ed75ee508fcbb4243349dfc10be800f641d28719f9d97d4c017fa4b605b8ae76d0aeeb5fcc2471c1964c265134422f10880a39f1fcf3437963724cf34cee20b8851873fb9a09147f8598029a8f3cd956920a8f551b2a8852545f66316a4f26659f1661867546d6a83f0d720e10d8d8ac00f0f0cf4700fcb46ea8d4582d32f8de75de5e4e5e9894a621382647f59ea2898d46f6f942b3ff3c364d061d56438695271ec32491e7b6524e4dabecf269ae246d39af6675f4e9ba1fd3919fcdaefe66ca36f8da01d741d459097b24d4913900347f5c6ab2cefc47000fa2d1f21fddb2cdab1ea33bb1c048c52412ab993705458b6596c1c25b6b3f10bdfea6096678067fdec7290b66709a54c351f1930438426d723a2dbaf8b3b4012e493e9a4ffa6941fcbb979377549b67cebcd43f176376ef4646e05d2cd375b99e7e2a220d871e762d07a936b972e2e18b74ed83e39c44a38149dc709c161b10f4b2bb8ab82da70172a3a8b5f61b04979f4dc002e92473add17c5cad4932e219ef7ef9230fb7e6d52a0453475e51b4151136280b9cf5e9ffbd5bff2acc24270631ae53fed2ba313702345bf86e9e2854f80944237425fe146a174603f8337cc1f8064997bdca8202874b615283c25feaaa22533617d45668b8da020f07a4f9e0a55bfaed7ecd8996616b9e67178e3b672c1f954503d7f53923e557a3d9c9cf9a096083dc7c8c211879d9fd4bbd04803c45db1dc13635c65efea719116862c869fbe983fdc4b255a9d0d6f3356abc632722de89792c0d8e14b8812399745282ea20b15d52f917a3b640c99d461f7ab2717f9b7184011f3836fa781dd18f05f20ab60be5b327247f4cb007050d99e28b045b917330865ba86ce0a1288a74e79dc2ef7990951dd8fc032060b8adcfbef4a39272a62a6f30da6e0f0aa6a2d4ace08c8d62a2686416f28f123717f6e50b1f645cf3a52155870ec34488b029e70e49875680ee05f87a8069331a95e222b086cffaeec56ba6975d5132326aa3b0ed351ded95346c8f3138621419728b56189e42dac7ee1b47ed4a3232495a353a59a4b8155319afaa2191511f37fdf9972b814639c21ec5f3a8cc"}, + {"0000000000000000000000000000000000000000000000000000000000001a50", "0026030a9dc9ca595a66864001d60d7629ba3fff8a13de9c25752217b5599341bd8a9b2144b577b1cc3f215a4d3aae0fcd6add372442e3c5c6d1313a1435274927ad3a8c18e58aee0617f02f70238af195def3b803ae376cc2cd09b8fc94111953f397d8b0058f14233a4f94f57f9a46e6e935d665205a3501acc87997d81146c538e7571140d0f656fffebe4af253479e8a482c840e60311ab60976b0e5d186d3044186012d0fe10772ebcccf2858e1867ef1072fbc75ca70367e5b320acdf9f2d7dcdd711eacf122194f4648b36212b9560fa28358a40eeb215a0493dd08c96572b75a9cd1c726219f81f661aa89b323a750137202824b9f7e349f121bad8ea0a74e4954dfb5be4dfef116b2ba9d7e7812f1befc400bfdb2ed2b97b46b7d6d4347d3df58ca1304d1527014bca75f043334b5d47afa73a8befceb25f083b11ef4ee71b4566846de66d5df353a9d94430034b1168ad77ccd1e0591cf20dbfcb078086f3a8205f911eb202cf9e3782d067b1cd8e83e89a81a214c06186ff667279381d94335d960719c899f5775ba642af74ce674a99899ebb91353f877a420f4a4f9cff70a810fa0d505ed39f83b130ee3227d6e4ba1d4cb41146bcb7bec8ab8e8abacc59a0be3458d95dcb4b9f91409ab612e5ecf19ce1475b482d829827453bfc4dc1897eef1c686f1c8cc30d559c2d8a7fe7b61bb88dd0243b3f7d1082f33c34584761caf0f39ae5f5082ad1b7d0d5cd10a6be297eb7497452b75920b1a1915c70f00d6107b616f9345494213f6cee6e8e4630a19dd13fd2e6d0dd8fda3d2afb1875192225deac3d1587b041caabc2a5928b788755789cdf1a73e829b962a4a0fdf58a569c6be68870456e1ded4b972637dd7169c088bd4018042f17fdbbf355db9cc43f703e0791d66301e148967d95cab691c59a781d3df22c1c47b4af003ca9d9f102f3a67e0e4e1eae7ea1440969cb78b4207265e766e0a8d0911efc6bcc5f18279f24d16f7d50daf34e68f4635719a0ad51548ae881d772d775fd9269325e84196500d8e3be53753c9e9b9e0a3b454a308daa15a3c23fd51f09fe27953de6af2c2b07e590724927dd7dc8b92f34490429ec6fcca58cb9fc772cd18f8cb47df38431dc33e4520d8ed4a636315ffc9bf2663b51ff56543d7b0de14c9ea3e4a72913b198e8a0b7ea8f91252badab3a32164d0fa2331a11c7b2df83e69d378d513a03ce512651ce7feba2593d296c7b9137f29492ff0f5afa6c1e385a020b5dd2a7d359cf21d8d03800cec0393ccf7f2fd094234b99bba5c67f21608f79f84cb43c3f2ef4566bb5d33ab23edb933e82df66209ace9254bbbc9579cb6cae6d6812dde715033a39fdfc0b73c4bdfe7e3772c27681f01d0bebab15ac674d4752eb12fb9bf99d1355807fb11e09b73360689d23a1b8ffeb323cf30ffcd541ce2906afc726e210b9284923214c9f792223150dcab3687d69ca30108d9cff78b6359e3944cc3f16ad7530a577df85cf610b2e1c5bd4579c2306a12ed456eb75943541124570d5935ae11723759f4b633b18afe95b6a73858c6c11e765ef03c9331d5a344d9c3eb501212bedd7d72562e7b2f2d92d269bde5e5e2f3eea0db199e1d1d2e2a5270ff1d0260d28dd847574131fb9462548b56f408074611ae41eec083d64440c5725f9528544c2ce82a1fdb473a2f130bfd5cbed201532e57b185b59021ba1a1362ed7b18addf384c330f4f392df4d9aa38aa372f94051971281f2bad49f303312f75215ccc2c4c73139eb359514965a6ced536c814385f261bab72a3ab2c626399692862b1684cd96e35636e035ddc3df31b29cd8264e2eccd416a30b8b7a9e93d0ed2955c772e38e0fc41db572337a8505980b26465db3c9cff7896"}, + {"0000000000000000000000000000000000000000000000000000000000000494", "00235ea5cfdbd6870a8a103bb61cd806b0099dd626038c2839a8334c61ebfb338e01b7fe556044b6d26f089a7b60e0d4acab94d3c32db5bda03d4a2b6cc5611898261a9193a555df6fd1ce9b104ccaaf29b9363504d38173e22bf7d3c25a07173d3b935ab97f57cde31edbf962ce12361eea1c533a494abe28f78c93961b07acbfbdee43fe973b0f1681125c3ac2dc1d785f5c239b4f03050dbb0a838282f63ade034ddaecb5d99603c7e2076d712bfbb6d490a0c16b9f1974521fb09e1aff5a43d096bfacb97ca2ed4c74d0870ac95c086e132199e769d78792f7d131be81eaaa819fad8df15643ef6deed5556210e05ef730e1fee446735ef560e115c2ff855d08444f3d5cb4de29ea8e6b0dce9dfba51a909121e8286ef9bfb3d34c94cb654584cd71d12917527393eddfb1ef65a02880f371ce5ef36a7d98ee2f2c5fbe413604d5e79af3d4d6adb7a96a533a0893009008ca8b02fcb5e1f3531ecb64c179208bb37cc445e905fb4e996209d06ec7bd996f7cd651f93459422092f49b3566fbf376e0d245aba616832fc17a01d42f0aa9daa5e6409fe12323cff34c265590e59aa2051b273b37d9c88f89db2a9405d462c1ae54f4342bfe552c65b94c2800bf845f5870b1fd0022c2c75a4bdc3e8e0dbea2eb1bf582df351036313f024a937785b043e287f9a1d83f13c234571fe2474afe05373af2bf049ba1eeee9b41c9c2b19491bee9bbf6ba8c9dfd2b1ab15996b61b27652897225bc747749a0acbf8a871054cccce0d8fb0f08a96170b1e69f5be77db147ac71fbc394eb050ca277a95c3e7402870d54fd331f6d212c6fe6e31fd5711f8ab9804d3d3106211ad3f3dc815775af2b728fa47c65691e7a41ba072810fb691bb2318dc0f999308531f8cc4fb9f701a89b82c904e1e24c31a041aab0b478dada418c069e1dd18bfcba788008493e1b46b0b91e264f2b42bfd019cdb2bde585818bcd5f40dd2a15ac05815c8083004add54ddc5a280a6e9cc321c333997aa64224903548b5dc5331bf114684c5dd3fa9f92b51972472ff6b4141f8ec73c74600bba10e421fb4ef092ce03de4653e78e617dd21d50a53c2bab791bac0a0f5d2b6c7a41db7a7adfdbdc3096ff1bedad4c685b40a733abb3ddb7714db9e22c40eda0e1bca9edbad9272d290852dbb757ce334686a01e09fb2131e80f173bd50d31dd2026b0a5e9cc5073f7d7584aef67c27b7dff59ccecbee870ae77fde830226242f2f8f2325953be2ff0af654d110ce2cc6514ce1ab93d516763acfa117dcff4d3dc22ed1bb595609c95a6d131aad59bfd361b0c4d25d3c802e54091b34448ae47aa05877b3f20453f02f2022b1f15e90cb0c437a8e8e8b588cdbd441d791aaa9c12eb0d00efe938dc69507780593d448d9a5b4dba56a99a9b9e77b06a3d39b7b92e191c19151d180cd0c78cb373e2881268777b6538df3774ff2b994d8f2a6475d209c9a1108a80f19d8612097513fe1d71cfbf28d459f71d1853d5b8f5daa629d317999f410d3f3c3e1e960b79255085205c18c6e4e0ff3cbe6014cc770d6d1a51a649e0e79de98ee0903337df2c1adcd290e24756254d6de22b539d0a11565bdcd45848fb5d5a1d9bdb3350ac92970a690771ee535604165e6bdc52842cca8b91b6e0eb999a68b4e8c6ff1d63109266fc94f3c563fabb267293d173ddec7156ecd36749d3fe5c65f8a7a5fd319e3f6e0e43345d9dca3e2e17c7b6d558146eec7cb25bb5fbce364437dd8a03476084dc7f797921d8f45117de3939bc5bde6fa39486ca45e005223c2fc625c1cc11ddfc22566475e6947a17ded63ab9670bde2642f5df2f2040de03ba573346a9d2fdf29d389f76fff488abe4309e3274bd183a4f1394db47a8ac1fba5a4"}, + {"0000000000000000000000000000000000000000000000000000000000001aaf", "0011862a8307991b94e5e3b15ffa846cf297cfa74d3710f3fac6b1a203eeb65939d3ec9abbaf711f01470e179301b910bc5fb3fca25f45d2500143dc8afc4e15e4b8bf6e637a9d879e07acd47a8b6e29c01994db06331295fc0edec1470030ee0e4f89c193a5576227125541feb3dbd08975e2e743607dfa023598fe242b16d4063880d50f6312e34923e6efd7b3251e7e9cfe1a93ff331e4ec886ade6a25917d5cfd5ac9e3ceaa10200d457ded821c551b531e718405bd58d1feda07c0536cf9d84e814999ce851817d150f9d6fb115180f37c2cbcd4e67b11d8fb9955448acadc56778f3c6b6392466502c96cb35cafba60787682c4e802c3a8c38093cdf33be52d57df68303846e61369d3828f39d5c0f49ee8a0b9dbebd2c3e44157c4054aa38729378990bf39ea0010b385f0264531f877aa8b57a640d704d1872661964e56db73ee8da26a37cedd3c81c9e50ff040d86cf54853291c209f2b036bf9aa674f2deadf7128b81656e4b0916dda1c1ef89544bb1424a328104148db7d89f1863abfab4e4a541fe5b1d2d1df6c03b1d88d161ede0f63112f56384a64247e5e0a0f393ec04c79472f182989083e6d251a1ebac8dc6e03b3f96296aaee5e76e12adced395421acdb636edc95a4e8e102e87d8f65ecce9d93c0999905f5f2eaf177765a011024ff335a269c7286583a49a1d3bd22d2dd8ad460a70bf8732433d71a98033b9bbc55842d57cb92bc66a69f531a21aec59578ae7075ef773fe7e7f1443510e35b479e7b99b5bd48d61c1523cd4f1d35c9da43c11fd2299775e737740e298edcedaf2b72656fe7eec0dfaed9b552bf883ef88e25d99e9a22e017ed8c1bb37bc073debf3850bae5396ce47fa0e665e04348a2f1022d0a96c09054dc9bf9260112a84b10e1b3f6f8a55f05f0db869d54d540df575416ffbf34d477d715c04383121d0c4c2aa905940dd3954e8aa00f0ffbb761d02677d68a63a5d591b6282c3e77a10ed2af8384b0d747d5d1be20cc1eb9673876d5213d2520b7d5e31359dcfa0c3ea6b6db76ec6cfcc452b464c267421d71c1f3f65790fd812863803f2cae43f2e68b5991e4a4daf1d0318f392efac479a44b9e4c60bc16b9f521f2521cc60c8ca9e209b56249d7c29d6c7d6a4dfd7632bc15e4d7a18f3cb763e8383e9cbac92c00f3b690d0b6808cb06727c39ccf8a49b8ee11d71a62cb9249c654bb74387e527cfb67e36af84409efdd62afa8bf111837e495528cd3d670931747643000a1ac3350ad13a351adf5e7283e3eed5f46153cda5bd393b3600ee1470710b1e097169827912cde7fcb2a2d83077c45b29661f1eead37905820d4758a4e51fd3ce95ffc159195df6836e76ffb5f5c736fd64daa40a8b7e3979ce52f42fa8fbe38c2d42df459b87b6c1f59cd85628ca04ffff593d27752da20ef659476e7886f050b9ad8c2aa0efefca156e7b6e60b481023b886e1465fda05f1a7d2e449c71bd8ff1e6c8a36cd91f3e3e257fdeea20f7e613924a96757e8496a79ac06adbd6f3fece6b0aacd7a91104257b2374b2023317527ce58f39b3dc30b59b60f74dffd57e78a324ac7fe875b027fddb990d3f5badece4d44563a1c39facfbe7f2a8dc5f7bc6288b1960cf1d6128eb420310284817c8ddaf1e2ec00cd1074a4e49537dafaef272d9dc22469d0ebdfb73419b63213e1073928e02e75e885d282609307f7a3210b5edfedb209b43b195a2402bf0cd28f482bd6fdd1e2c3b00f24b13af606723dce7650c2e380bb30e3b2cbdd7a7c821de0125eec2f5eab6337668e63b8b684d48de2b4dd7d7ff593c183f35e3bf66d0a2df1cdb3337dc56ace96aa5c4b5ab3274f0a79f33927df475498b3b4aaf15ed1b55bdf73be1752fa1d918b0a8a5"}, + {"00000000000000000000000000000000000000000000000000000000000004f9", "019182e89993458b19d985b3fa55a7a5e7e4fe7ca212e443c5a995b69ff09a58b6f7758ebe6814be50b00fde056e7593c3cf0dac41eff06e699ae369ba09513d85535070ea38f196dae53bc9ebbed5f0307bc0f008a678b65fc3e9c9d90a9be756e6ab5319cfbe3c320c9ee6d7d6a14819be79c30349b2813e53fb3d8e9c15c4ee45df594e81389ad416e930c1e167115506662fe813668a53ffdeee6cd630e5b63e49c5b07ba877020e9300515d7ddf0c8d4519e14a51d3209d9991d54d369e0af257a24169c3999aa6f5270a681ef9c8062cbee6a10dcb745c6cff7324becf95914e82aeba2144eaa3f02d28f53749d3f721f941b965f434faa25c0343cc3d3e3c10cdf503611f833044db4c823b7b644858f7dd31947fdd3c3d74a84c72df6d52c4d91d850795e41e2262ef45d28d7a450f54aa76e288fd7ae450ff02e360e50c23aeb4a56c1c6b49616ad64e562001ce0c25620fa5fb86d1b29d77b1f14ce5103b9b6d13105dad93c4cb42b8594632d8eb935b197e1b37b80d7bd6a038a546e944e082fc4fc4cc6e3736fb8dde458825b22761e9fd2ccc845a69371dd5c7504ef3e102ebb3ab87a17a2d4249803daa08fc704f6bb77bc30f7bcc273d85a055bdd0a4a7577eee59e88ddac4450cacbc675e6170c50bdec4b97341fef28a8978dfec36580764e01e844f7b6a64fdd4f3afcd55965f2aeb047b56dd4f1c55bf961b93dcd0206869181df961f109bedb81d78fac3d870de21bb671d1d5648ff8b9230592dc15ce138178ac93cc761375c79377c61d6cbb06908bb78d94b63eb4f10391c76e79fdf2d374fb5f06894b77f65c85dfbd8f045fe5b6a8fa73a9568004168fa34f260a715339e4755f1c4033c690db5af6290c4abcab92033ec0782b631f0d9a51aa98151df94f29bfc27be979ee7df286b4c22bae3ba20fe69bfee2022b0df09218e11ba9a5946bc1cd8105aab0bc214f0cdbe62633938dc4ff6421cbcd3e02521269b0df841d59a4de7bc8cc610f36c73816f134b1e2709d3f993c62864c986363b5a289949e32e64c495e91f1d827155d5ed6c448a92180563af1c5db1ac71bf2fbd136607dbf561c5ba796ee9146207c7edbedba7dddd2611b5d663c4d8d504aa1754c236d74b6279d38bf51ed3d835c364e6c836df86624bd39b050c2df8f3c770503269907195c569f856dd4d7f44602c5e6f3df358804ff9ec8aa842505f5e003df1845b5832791fc696d2c1e11793b308569f7ac3534726a1942a2ad7af7953978dc96c61c01bb7490555be2b8646d61c1530db603d161960c8373ed1eaca1afb65451ce404b7c172f0d96667ed6de4f612bd5b11af4fd97a091715d07850c564457b60a6d6ea9c463199f4c7e990c7add147a1b0fe7b8300fe7631c30454c09490eaa5cfb35babe054006505bd87c8189df269184da4b23c88f3fbb2d63ca6de82bbc2effffb8495b4ce8c19a619fdea684147b03e4b9d1ca4114db84207babccd950ac2b2c0315773b57b791cd2ea6e8c3028bc041db477ddd35da0777b5e7ad0a90730c1a231796b03355647a31554c25e51b804299e9539d34c94cd870f5f3187abbb64d18d325807729b2b7dfe0567ff3608695d4537d241837e1fec1e057a900dbf4f42496245a815e40bdacac08e8a8c7bd096988d3e532f7f3e84c68e29d074d5c2a3e5a7e79918781d91249066dff8322b633dd9eb01926d281c316f61dce6f14e24cc8edca1c133300ac4192e72389e3cabf2ff0442d42227349f2cf9105570f8adee5570ac0054256b2719fc64cc9bac7b293fb11dd51e662dcc67fa174d949577dad7aae5518970d104d1396862a4eafeb5802b427bfba66eb031d523a4fecbde72567278fe7d848f73bff100adfa11a30f7"}, + {"0000000000000000000000000000000000000000000000000000000000000cf9", "003da2b963538940c61b21677dac14e112c7be6e7432d9abddad13ddc16b9773af9dcc83056774ab8b520376185857a722f9930db6a94573af8a5ecf9fe75c0781a4313c0773803eca83d2e15d43cba04e9ef51206de18c320a35459c89cd15daa742100a9457ee671460b6d9f0c18e2753e4975213d6e19629541761dca089a3682e907451c4cfaa347e47c9fd6464abd7ab82bdbf4ec1fd1d206dc17e846a07a86c291e75c46090280c10b0809cb68e35aaa9dc15ada8304e0d8f72905b31fcfe5db5f673bed345871e4cefa44e15d453908e743eac765d2cd43062162bbab23dc7e847fb1cd616aa3412fa0f995ba8826449dbdf29612367dd91e03dceb0ce9139c6d9ba1272314ea9b2ea8ab19a5094dbd2b932b730895d6a7b60ef5fca1c9fa59b11c5c133a974a9a74ef13fe9784b1cfbd74f64bf39b0733342bf407c9d80add03228573a05d163261e81e3aad0040c15aa693170dddfd90d4c451dfc339795a83ef1d913598a4511839a2c70744963a2e4abe92b6c3030908fdae17f78247c59eb6bd3bed5f7dcb841b94a3386adc521d11bd5160db2448c8d679db32be9a9fcb0c07b9d64263769f901430c35f11bc711198f9c06f120f3ac4a313a1d9dfbcd80f8f7f81223b93dc876a24e34621e2effbdbdca8035e7dcf89ea6dd1d3e96245a10b7f87efbfc78634c4702d5295bdd8b71ade0308eeb8d695c5b2a7387962d6b9492530fe00d6f1111c0bdc51380aecd7bc98c23cefe09939371dba42e00a85d074f65e8193be6cf2bb05bf68b8cc98c690440eb38cde288dfcb0d508214f22734d389e0a5a266e0b44d7d1fda3b573c88856aa25f2e0fe8a795e56ff1c35afb5675e05430a18775bdd49a072bea45dd5f4278e1bc0a627058b4b52345a22abd2520f9fff390d30438e77b55c03fee2d0659eaed64e8e32c4fa38ee021e7fea2051bd0f67ed61e4efb072711b65eb26fe0656829e1e0a79bb411300c2e51093ad55ad973c8a27c91db5ef90288f2564e40f004f130109363d26616272e4640c1ee313d82a489b8afee4fae0c65d7e8521f9be58e62238a988079569f168efe1879c0d5d095da06d97651a9381932007d1b3490c22101e94b5d23a05227ea3126bb37607090ed4de65b6e7c358e3f94ec2b5f41119ee251f6ac7db35d2c1be319b3708dc0a63e87f474cf25b1a4cc11c309fda5d1b80bc90ce117661f4d20651dfeed17212da6ce59f46879f9ae12031ed096e556b61022e16b8b67d27d7257dffa3932acc6517d7190c5fad3018b2ef6c08f71b753e69af10a68da22ca967e1d130e1c52f39898f73df9d6cff16abf9b480d229cb1620f215c4aa1471b7427778f4114de88b6e59a33babbde6c961c6845e88e61a2f9a27e70cb8a7a32efb2c24672505792085d6bef34574032a8e043d897840d48521395df0ae913b9eaac74611751bd712ab2a03d9fb3318ed5e35b4e87d095f6d0b3df3df4608a785b889f4a40fb04dde5a51f8f9682831ca2c252216af89d2c60125b0413e6204540730103b4312f3e7b5955d8c63e71ab296c50633e890841912d3b43ee5db7b76bb65b825ed5962f125da8a231417d403364ee9a395992971f079444ed21a36b405181013732a168c7bc601a63b016033fbf46c7fcbe8071a493fce5e86e987f0b59338dcd3699c288d29aa28c33dbe351f5c51f8d7a5273436e5ef067b7d3105118041147f0d16990c8b0375113b1a3622de963d282a21fe1e5357ed0db35fa3cc1726fd41be6797b47e0e3430bd365f6b0bad6659b8e27a283eb396dd43cb3435ee31511bbf371eea84907d3c703f1ace9bc78e125e32fa2db771f9d086463450fdc0f260779d2c353149e669f2cfaf55d87107b179dbf9835be1bc9357"}, }; // Copied from src/miner.cpp @@ -148,6 +243,54 @@ class MinerAddressScript : public CReserveScript void KeepScript() {} }; +#ifdef ENABLE_MINING +// This is only useful if the test changes to require more blocks, but we keep it +// compiling to avoid bitrot. +void MineBlockForTest(const CChainParams& chainparams, CBlock* pblock) { + arith_uint256 try_nonce(0); + unsigned int n = chainparams.GetConsensus().nEquihashN; + unsigned int k = chainparams.GetConsensus().nEquihashK; + + // Hash state + eh_HashState eh_state = EhInitialiseState(n, k); + + // I = the block header minus nonce and solution. + CEquihashInput I{*pblock}; + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << I; + + // H(I||... + eh_state.Update((unsigned char*)&ss[0], ss.size()); + + while (true) { + pblock->nNonce = ArithToUint256(try_nonce); + + // H(I||V||... + eh_HashState curr_state(eh_state); + curr_state.Update(pblock->nNonce.begin(), pblock->nNonce.size()); + + std::function incrementRuns = [&]() {}; + std::function&)> checkSolution = [&](size_t s, const std::vector& index_vector) { + auto soln = GetMinimalFromIndices(index_vector, DIGITBITS); + pblock->nSolution = soln; + if (!equihash::is_valid( + n, k, + {(const unsigned char*)ss.data(), ss.size()}, + {pblock->nNonce.begin(), pblock->nNonce.size()}, + {soln.data(), soln.size()})) { + return false; + } + CValidationState state; + return ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL) && state.IsValid(); + }; + if (equihash_solve(curr_state.inner, incrementRuns, checkSolution)) + break; + + try_nonce += 1; + } +} +#endif + // NOTE: These tests rely on CreateNewBlock doing its own self-validation! BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { @@ -158,23 +301,30 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) CMutableTransaction tx,tx2; CScript script; uint256 hash; - TestMemPoolEntryHelper entry; - entry.nFee = 11; - entry.dPriority = 111.0; - entry.nHeight = 11; LOCK(cs_main); fCheckpointsEnabled = false; fCoinbaseEnforcedShieldingEnabled = false; // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + + // We can't make transactions until we have inputs. Therefore, load 200 blocks. + const int nblocks = 200; + assert(nblocks < chainparams.GetConsensus().SubsidySlowStartShift()); + + auto MinerSubsidy = [](int height) { return height*50000; }; + auto FoundersReward = [](int height) { return height*12500; }; + + std::vector txFirst; + auto CoinbaseTx = [&](int height) { + assert(0 <= height-1 && height-1 < txFirst.size()); + return txFirst[height-1]; + }; - // We can't make transactions until we have inputs - // Therefore, load 100 blocks :) - std::vectortxFirst; - for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i) + for (unsigned int i = 0; i < nblocks; ++i) { + int height = i+1; CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 4; // Fake the blocks taking at least nPowTargetSpacing to be mined. @@ -182,99 +332,33 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // of the next block must be six spacings ahead of that to be at least // one spacing ahead of the tip. Within 11 blocks of genesis, the median // will be closer to the tip, and blocks will appear slower. - pblock->nTime = chainActive.Tip()->GetMedianTimePast()+6*Params().GetConsensus().PoWTargetSpacing(i); + pblock->nTime = chainActive.Tip()->GetMedianTimePast() + 6*chainparams.GetConsensus().PoWTargetSpacing(i); pblock->nBits = GetNextWorkRequired(chainActive.Tip(), pblock, chainparams.GetConsensus()); CMutableTransaction txCoinbase(pblock->vtx[0]); txCoinbase.nVersion = 1; txCoinbase.vin[0].scriptSig = CScript() << (chainActive.Height()+1) << OP_0; txCoinbase.vout[0].scriptPubKey = CScript(); - txCoinbase.vout[0].nValue = 50000 * (i + 1); - txCoinbase.vout[1].nValue = 12500 * (i + 1); + txCoinbase.vout[0].nValue = MinerSubsidy(height); + txCoinbase.vout[1].nValue = FoundersReward(height); pblock->vtx[0] = CTransaction(txCoinbase); - if (txFirst.size() < 2) - txFirst.push_back(new CTransaction(pblock->vtx[0])); + txFirst.push_back(new CTransaction(pblock->vtx[0])); pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); - pblock->nNonce = uint256S(blockinfo[i].nonce_hex); - pblock->nSolution = ParseHex(blockinfo[i].solution_hex); - -/* - { - arith_uint256 try_nonce(0); - unsigned int n = Params().GetConsensus().nEquihashN; - unsigned int k = Params().GetConsensus().nEquihashK; - - // Hash state - eh_HashState eh_state = EhInitialiseState(n, k); - - // I = the block header minus nonce and solution. - CEquihashInput I{*pblock}; - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << I; - - // H(I||... - eh_state.Update((unsigned char*)&ss[0], ss.size()); - - while (true) { - pblock->nNonce = ArithToUint256(try_nonce); - - // H(I||V||... - eh_HashState curr_state(eh_state); - curr_state.Update(pblock->nNonce.begin(), pblock->nNonce.size()); - - // Create solver and initialize it. - equi eq(1); - eq.setstate(curr_state.state); - - // Initialization done, start algo driver. - eq.digit0(0); - eq.xfull = eq.bfull = eq.hfull = 0; - eq.showbsizes(0); - for (u32 r = 1; r < WK; r++) { - (r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0); - eq.xfull = eq.bfull = eq.hfull = 0; - eq.showbsizes(r); - } - eq.digitK(0); - - // Convert solution indices to byte array (decompress) and pass it to validBlock method. - std::set> solns; - for (size_t s = 0; s < std::min(MAXSOLS, eq.nsols); s++) { - LogPrint("pow", "Checking solution %d\n", s+1); - std::vector index_vector(PROOFSIZE); - for (size_t i = 0; i < PROOFSIZE; i++) { - index_vector[i] = eq.sols[s][i]; - } - std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS); - solns.insert(sol_char); - } - - for (auto soln : solns) { - if (!equihash::is_valid( - n, k, - {(const unsigned char*)ss.data(), ss.size()}, - {pblock->nNonce.begin(), pblock->nNonce.size()}, - {soln.data(), soln.size()})) continue; - pblock->nSolution = soln; - - CValidationState state; - - if (ProcessNewBlock(state, NULL, pblock, true, NULL) && state.IsValid()) { - goto foundit; - } - - //std::cout << state.GetRejectReason() << std::endl; - } - - try_nonce += 1; - } - foundit: + if (i < sizeof(blockinfo)/sizeof(*blockinfo)) { + pblock->nNonce = uint256S(blockinfo[i].nonce_hex); + pblock->nSolution = ParseHex(blockinfo[i].solution_hex); + } else { +#ifdef ENABLE_MINING + // If you need to mine more blocks than are currently in blockinfo, increase + // nblocks and then this code will do so. + MineBlockForTest(chainparams, pblock); std::cout << " {\"" << pblock->nNonce.GetHex() << "\", \""; std::cout << HexStr(pblock->nSolution.begin(), pblock->nSolution.end()); std::cout << "\"}," << std::endl; - +#else + assert(false); +#endif } -*/ // These tests assume null hashBlockCommitments (before Sapling) pblock->hashBlockCommitments = uint256(); @@ -285,6 +369,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->hashPrevBlock = pblock->GetHash(); } + // Stop here if we needed to mine more blocks. + assert(nblocks == sizeof(blockinfo)/sizeof(*blockinfo)); + // Set the clock to be just ahead of the last "mined" block, to ensure we satisfy the // future timestamp soft fork rule. auto curTime = GetTime(); @@ -294,9 +381,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) delete pblocktemplate; // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; + TestMemPoolEntryHelper entry; + entry.nFee = 0; + entry.nHeight = 0; + // Define a helper function to check the kind of failure from TestBlockValidity. auto err_is = [](const std::string& msg) { return [&](std::runtime_error const& e) { @@ -305,84 +396,93 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) }; }; + auto PrepareMempool = [&](size_t numTransactions, CAmount fee, std::function addTx) { + // This relies on tx.{vin,vout} having been prepared for the specific check. + assert(fee >= 0); + int coinbaseHeight = 20 - 1; + tx.vout[0].nValue = -1; // always spend coinbase in the first transaction + + for (size_t i = 0; i < numTransactions; ++i) + { + bool spendsCoinbase = tx.vout[0].nValue < fee; + if (spendsCoinbase) { + ++coinbaseHeight; + tx.vin[0].prevout.hash = CoinbaseTx(coinbaseHeight)->GetHash(); + tx.vout[0].nValue = MinerSubsidy(coinbaseHeight); + } + assert(tx.vout[0].nValue >= fee); + tx.vout[0].nValue -= fee; + addTx(i, entry.Fee(fee).Time(GetTime()).SpendsCoinbase(spendsCoinbase), tx); + tx.vin[0].prevout.hash = tx.GetHash(); + } + }; + // block sigops > limit: 1000 CHECKMULTISIG + 1 tx.vin.resize(1); - // NOTE: OP_NOP is used to force 20 SigOps for the CHECKMULTISIG + // NOTE: OP_NOP is used to force 20 legacy sigops for the CHECKMULTISIG. tx.vin[0].scriptSig = CScript() << OP_0 << OP_0 << OP_0 << OP_NOP << OP_CHECKMULTISIG << OP_1; - tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].prevout.n = 0; tx.vout.resize(1); - tx.vout[0].nValue = 50000LL; - for (unsigned int i = 0; i < 1001; ++i) - { - tx.vout[0].nValue -= 10; - hash = tx.GetHash(); - bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase - // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); - tx.vin[0].prevout.hash = hash; - } - BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-blk-sigops")); + + // If we don't set the # of sigops in the CTxMemPoolEntry, template creation fails. + PrepareMempool(1000, MINIMUM_FEE, [&](size_t i, auto& entry, auto& tx) { + mempool.addUnchecked(tx.GetHash(), entry.FromTx(tx)); + }); + BOOST_CHECK_EXCEPTION(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, err_is("bad-blk-sigops")); mempool.clear(); - tx.vin[0].prevout.hash = txFirst[0]->GetHash(); - tx.vout[0].nValue = 50000LL; - for (unsigned int i = 0; i < 1001; ++i) - { - tx.vout[0].nValue -= 10; - hash = tx.GetHash(); - bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase - // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); - tx.vin[0].prevout.hash = hash; - } - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + // If we do set the # of sigops in the CTxMemPoolEntry, template creation passes. + PrepareMempool(1000, MINIMUM_FEE, [&](size_t i, auto& entry, auto& tx) { + mempool.addUnchecked(tx.GetHash(), entry.SigOps(20).FromTx(tx)); + }); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + // We reserve 100 sigops for the coinbase tx, which actually takes 1 sigop. We finish the template + // when the estimated number of sigops is 19980 (since BlockAssembler::TestForBlock does not allow + // exactly the limit of 20000). So the block template will have 19980 - 100 + 1 = 19881 sigops. + // This takes (19881-1)/20 = 994 transactions, plus the coinbase tx. + BOOST_CHECK_EQUAL(19881, std::reduce(pblocktemplate->vTxSigOps.begin(), pblocktemplate->vTxSigOps.end())); + BOOST_CHECK_EQUAL(995, pblocktemplate->block.vtx.size()); delete pblocktemplate; mempool.clear(); - // block size > limit tx.vin[0].scriptSig = CScript(); // 18 * (520char + DROP) + OP_1 = 9433 bytes std::vector vchData(520); for (unsigned int i = 0; i < 18; ++i) tx.vin[0].scriptSig << vchData << OP_DROP; tx.vin[0].scriptSig << OP_1; - tx.vin[0].prevout.hash = txFirst[0]->GetHash(); - tx.vout[0].nValue = 50000LL; - for (unsigned int i = 0; i < 128; ++i) - { - tx.vout[0].nValue -= 350; - hash = tx.GetHash(); - bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); - tx.vin[0].prevout.hash = hash; - } - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + + // Test block size just under the 2000000-byte limit. + PrepareMempool(210, CalculateConventionalFee(64), [&](size_t i, auto& entry, auto& tx) { + mempool.addUnchecked(tx.GetHash(), entry.FromTx(tx)); + }); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK_EQUAL(211, pblocktemplate->block.vtx.size()); + BOOST_CHECK_EQUAL(1994255, ::GetSerializeSize(pblocktemplate->block, SER_NETWORK, PROTOCOL_VERSION)); delete pblocktemplate; mempool.clear(); - // orphan in mempool, template creation fails - hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); + // ... and just over the limit. + PrepareMempool(211, CalculateConventionalFee(64), [&](size_t i, auto& entry, auto& tx) { + mempool.addUnchecked(tx.GetHash(), entry.FromTx(tx)); + }); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + // only 211 transactions including coinbase, not 212 + BOOST_CHECK_EQUAL(211, pblocktemplate->block.vtx.size()); + // We can't check the exact block size because which tx will be excluded is nondeterministic, + // and they are different sizes. + BOOST_CHECK_GE(2000000, ::GetSerializeSize(pblocktemplate->block, SER_NETWORK, PROTOCOL_VERSION)); + delete pblocktemplate; mempool.clear(); - // child with higher priority than parent tx.vin[0].scriptSig = CScript() << OP_1; - tx.vin[0].prevout.hash = txFirst[1]->GetHash(); - tx.vout[0].nValue = 39000LL; - hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - tx.vin[0].prevout.hash = hash; - tx.vin.resize(2); - tx.vin[1].scriptSig = CScript() << OP_1; - tx.vin[1].prevout.hash = txFirst[0]->GetHash(); - tx.vin[1].prevout.n = 0; - tx.vout[0].nValue = 49000LL; - hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(4000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); - delete pblocktemplate; + + // Test with an orphan tx in the mempool. Note that this is testing a case that should never occur, + // because AcceptToMemoryPool would reject the orphan. Using addUnchecked bypasses that rejection. + PrepareMempool(2, MINIMUM_FEE, [&](size_t i, auto& entry, auto& tx) { + if (i != 0) mempool.addUnchecked(tx.GetHash(), entry.SigOps(1).FromTx(tx)); + }); + BOOST_CHECK_EXCEPTION(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); mempool.clear(); // coinbase in mempool, template creation fails @@ -392,48 +492,50 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 0; hash = tx.GetHash(); // give it a fee so it'll get mined - mempool.addUnchecked(hash, entry.Fee(1).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-cb-multiple")); + mempool.addUnchecked(hash, entry.Fee(MINIMUM_FEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + BOOST_CHECK_EXCEPTION(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, err_is("bad-cb-multiple")); mempool.clear(); // P2SH txn in mempool (valid, Zcash always had P2SH enabled) - tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vin[0].prevout.hash = CoinbaseTx(1)->GetHash(); tx.vin[0].prevout.n = 0; tx.vin[0].scriptSig = CScript() << OP_1; - tx.vout[0].nValue = 49000LL; + tx.vout[0].nValue = MinerSubsidy(1) - MINIMUM_FEE; + assert(tx.vout[0].nValue >= 0); script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(100L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(MINIMUM_FEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << std::vector(script.begin(), script.end()); - tx.vout[0].nValue -= 10000; + tx.vout[0].nValue -= MINIMUM_FEE; + assert(tx.vout[0].nValue >= 0); hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + mempool.addUnchecked(hash, entry.Fee(MINIMUM_FEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); // double spend txn pair in mempool, template creation fails - tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vin[0].prevout.hash = CoinbaseTx(1)->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; - tx.vout[0].nValue = 49000LL; + tx.vout[0].nValue = MinerSubsidy(1) - MINIMUM_FEE; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(MINIMUM_FEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); + mempool.addUnchecked(hash, entry.Fee(MINIMUM_FEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + BOOST_CHECK_EXCEPTION(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); mempool.clear(); // subsidy changing int nHeight = chainActive.Height(); chainActive.Tip()->nHeight = 209999; - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = 210000; - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = nHeight; @@ -444,31 +546,31 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) FixedClock::Instance()->Set(std::chrono::seconds(chainActive.Tip()->GetMedianTimePast()+1)); // height locked - tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vin[0].prevout.hash = CoinbaseTx(1)->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; tx.vin[0].nSequence = 0; - tx.vout[0].nValue = 49000LL; + tx.vout[0].nValue = MinerSubsidy(1) - MINIMUM_FEE; tx.vout[0].scriptPubKey = CScript() << OP_1; tx.nLockTime = chainActive.Tip()->nHeight+1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(MINIMUM_FEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST)); // time locked tx2.vin.resize(1); - tx2.vin[0].prevout.hash = txFirst[1]->GetHash(); + tx2.vin[0].prevout.hash = CoinbaseTx(2)->GetHash(); tx2.vin[0].prevout.n = 0; tx2.vin[0].scriptSig = CScript() << OP_1; tx2.vin[0].nSequence = 0; tx2.vout.resize(1); - tx2.vout[0].nValue = 79000LL; + tx2.vout[0].nValue = MinerSubsidy(2) - MINIMUM_FEE; tx2.vout[0].scriptPubKey = CScript() << OP_1; tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1; hash = tx2.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx2)); + mempool.addUnchecked(hash, entry.Fee(MINIMUM_FEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx2)); BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); // Neither tx should have made it into the template. BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); @@ -476,14 +578,16 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // However if we advance height and time by one, both will. chainActive.Tip()->nHeight++; - FixedClock::Instance()->Set(std::chrono::seconds(chainActive.Tip()->GetMedianTimePast()+2)); + int64_t newTime = chainActive.Tip()->GetMedianTimePast()+2; - // FIXME: we should *actually* create a new block so the following test - // works; CheckFinalTx() isn't fooled by monkey-patching nHeight. - //BOOST_CHECK(CheckFinalTx(tx)); - //BOOST_CHECK(CheckFinalTx(tx2)); + // We can't test this by directly calling CheckFinalTx(tx*, LOCKTIME_MEDIAN_TIME_PAST) + // because that would use chainActive.Height() which isn't fooled by monkey-patching nHeight. + // However, this should be equivalent: + BOOST_CHECK(IsFinalTx(tx, chainActive.Tip()->nHeight+1, newTime)); + BOOST_CHECK(IsFinalTx(tx2, chainActive.Tip()->nHeight+1, newTime)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + FixedClock::Instance()->Set(std::chrono::seconds(newTime)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 2); delete pblocktemplate; @@ -492,8 +596,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) SystemClock::SetGlobal(); mempool.clear(); - for (CTransaction *tx : txFirst) + for (CTransaction *tx : txFirst) { delete tx; + } fCheckpointsEnabled = true; fCoinbaseEnforcedShieldingEnabled = true; diff --git a/depend/zcash/src/test/policyestimator_tests.cpp b/depend/zcash/src/test/policyestimator_tests.cpp index 8b833c24e..add0215a3 100644 --- a/depend/zcash/src/test/policyestimator_tests.cpp +++ b/depend/zcash/src/test/policyestimator_tests.cpp @@ -19,26 +19,18 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) CTxMemPool mpool(CFeeRate(1000)); TestMemPoolEntryHelper entry; CAmount basefee(2000); - double basepri = 1e6; CAmount deltaFee(100); - double deltaPri=5e5; - std::vector feeV[2]; - std::vector priV[2]; + std::vector feeV; - // Populate vectors of increasing fees or priorities + // Populate vectors of increasing fees for (int j = 0; j < 10; j++) { - //V[0] is for fee transactions - feeV[0].push_back(basefee * (j+1)); - priV[0].push_back(0); - //V[1] is for priority transactions - feeV[1].push_back(CAmount(0)); - priV[1].push_back(basepri * pow(10, j+1)); + feeV.push_back(basefee * (j+1)); } // Store the hashes of transactions that have been - // added to the mempool by their associate fee/pri + // added to the mempool by their associate fee // txHashes[j] is populated with transactions either of - // fee = basefee * (j+1) OR pri = 10^6 * 10^(j+1) + // fee = basefee * (j+1) std::vector txHashes[10]; // Create a transaction template @@ -61,19 +53,19 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) // At a decay .998 and 4 fee transactions per block // This makes the tx count about 1.33 per bucket, above the 1 threshold while (blocknum < 200) { - for (int j = 0; j < 10; j++) { // For each fee/pri multiple - for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx + for (int j = 0; j < 10; j++) { // For each fee + for (int k = 0; k < 4; k++) { // add 4 fee txs tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); + mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx, &mpool)); txHashes[j].push_back(hash); } } - //Create blocks where higher fee/pri txs are included more often + //Create blocks where higher fee txs are included more often for (int h = 0; h <= blocknum%10; h++) { - // 10/10 blocks add highest fee/pri transactions + // 10/10 blocks add highest fee transactions // 9/10 blocks add 2nd highest and so on until ... - // 1/10 blocks add lowest fee/pri transactions + // 1/10 blocks add lowest fee transactions while (txHashes[9-h].size()) { std::shared_ptr ptx = mpool.get(txHashes[9-h].back()); if (ptx) @@ -94,7 +86,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) } std::vector origFeeEst; - std::vector origPriEst; // Highest feerate is 10*baseRate and gets in all blocks, // second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%, // third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%, @@ -103,15 +94,11 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) // so estimateFee(2) should return 8*baseRate etc... for (int i = 1; i < 10;i++) { origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK()); - origPriEst.push_back(mpool.estimatePriority(i)); if (i > 1) { // Fee estimates should be monotonically decreasing BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]); - BOOST_CHECK(origPriEst[i-1] <= origPriEst[i-2]); } BOOST_CHECK(origFeeEst[i-1] < (10-i)*baseRate.GetFeePerK() + deltaFee); BOOST_CHECK(origFeeEst[i-1] > (10-i)*baseRate.GetFeePerK() - deltaFee); - BOOST_CHECK(origPriEst[i-1] < pow(10,10-i) * basepri + deltaPri); - BOOST_CHECK(origPriEst[i-1] > pow(10,10-i) * basepri - deltaPri); } // Mine 50 more blocks with no transactions happening, estimates shouldn't change @@ -122,19 +109,17 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int i = 1; i < 10;i++) { BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee); BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee); - BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] + deltaPri); - BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri); } // Mine 15 more blocks with lots of transactions happening and not getting mined // Estimates should go up while (blocknum < 265) { - for (int j = 0; j < 10; j++) { // For each fee/pri multiple - for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx + for (int j = 0; j < 10; j++) { // For each fee multiple + for (int k = 0; k < 4; k++) { // add 4 fee txs tx.vin[0].prevout.n = 10000*blocknum+100*j+k; uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); + mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx, &mpool)); txHashes[j].push_back(hash); } } @@ -143,7 +128,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int i = 1; i < 10;i++) { BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee); - BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri); } // Mine all those transactions @@ -160,20 +144,20 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) block.clear(); for (int i = 1; i < 10;i++) { BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee); - BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri); } // Mine 100 more blocks where everything is mined every block // Estimates should be below original estimates (not possible for last estimate) while (blocknum < 365) { - for (int j = 0; j < 10; j++) { // For each fee/pri multiple - for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx + for (int j = 0; j < 10; j++) { // For each fee multiple + for (int k = 0; k < 4; k++) { // add 4 fee txs tx.vin[0].prevout.n = 10000*blocknum+100*j+k; uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); + mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx, &mpool)); std::shared_ptr ptx = mpool.get(hash); if (ptx) block.push_back(*ptx); + } } mpool.removeForBlock(block, ++blocknum, dummyConflicted); @@ -181,7 +165,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) } for (int i = 1; i < 9; i++) { BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee); - BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] - deltaPri); } } @@ -191,7 +174,7 @@ BOOST_AUTO_TEST_CASE(TxConfirmStats_FindBucketIndex) std::vector buckets {0.0, 3.5, 42.0}; TxConfirmStats txcs; - txcs.Initialize(buckets, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "Test"); + txcs.Initialize(buckets, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY); BOOST_CHECK_EQUAL(txcs.FindBucketIndex(-1.0), 0); BOOST_CHECK_EQUAL(txcs.FindBucketIndex(0.0), 0); diff --git a/depend/zcash/src/test/rpc_tests.cpp b/depend/zcash/src/test/rpc_tests.cpp index 81d9ccc0b..9e220058e 100644 --- a/depend/zcash/src/test/rpc_tests.cpp +++ b/depend/zcash/src/test/rpc_tests.cpp @@ -166,28 +166,28 @@ BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values) BOOST_AUTO_TEST_CASE(json_parse_errors) { // Valid - BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0").get_real(), 1.0); + BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0").value().get_real(), 1.0); // Valid, with leading or trailing whitespace - BOOST_CHECK_EQUAL(ParseNonRFCJSONValue(" 1.0").get_real(), 1.0); - BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0 ").get_real(), 1.0); + BOOST_CHECK_EQUAL(ParseNonRFCJSONValue(" 1.0").value().get_real(), 1.0); + BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0 ").value().get_real(), 1.0); - BOOST_CHECK_THROW(AmountFromValue(ParseNonRFCJSONValue(".19e-6")), std::runtime_error); //should fail, missing leading 0, therefore invalid JSON - BOOST_CHECK_EQUAL(AmountFromValue(ParseNonRFCJSONValue("0.00000000000000000000000000000000000001e+30 ")), 1); + BOOST_CHECK(!ParseNonRFCJSONValue(".19e-6").has_value()); //should fail, missing leading 0, therefore invalid JSON + BOOST_CHECK_EQUAL(AmountFromValue(ParseNonRFCJSONValue("0.00000000000000000000000000000000000001e+30 ").value()), 1); // Invalid, initial garbage - BOOST_CHECK_THROW(ParseNonRFCJSONValue("[1.0"), std::runtime_error); - BOOST_CHECK_THROW(ParseNonRFCJSONValue("a1.0"), std::runtime_error); + BOOST_CHECK(!ParseNonRFCJSONValue("[1.0").has_value()); + BOOST_CHECK(!ParseNonRFCJSONValue("a1.0").has_value()); // Invalid, trailing garbage - BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0sds"), std::runtime_error); - BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0]"), std::runtime_error); - // BTC addresses should fail parsing - BOOST_CHECK_THROW(ParseNonRFCJSONValue("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"), std::runtime_error); - BOOST_CHECK_THROW(ParseNonRFCJSONValue("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNL"), std::runtime_error); + BOOST_CHECK(!ParseNonRFCJSONValue("1.0sds").has_value()); + BOOST_CHECK(!ParseNonRFCJSONValue("1.0]").has_value()); + // Bitcoin addresses should fail parsing + BOOST_CHECK(!ParseNonRFCJSONValue("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W").has_value()); + BOOST_CHECK(!ParseNonRFCJSONValue("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNL").has_value()); } BOOST_AUTO_TEST_CASE(rpc_ban) { BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); - + UniValue r; BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0 add"))); BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.0:8334")), runtime_error); //portnumber for setban not allowed @@ -353,7 +353,7 @@ BOOST_AUTO_TEST_CASE(rpc_insightexplorer) const string addr = "t1T3G72ToPuCDTiCEytrU1VUBRHsNupEBut"; BOOST_CHECK_NO_THROW(CallRPC("getaddressmempool \"" + addr + "\"")); BOOST_CHECK_NO_THROW(CallRPC("getaddressmempool {\"addresses\":[\"" + addr + "\"]}")); - BOOST_CHECK_NO_THROW(CallRPC("getaddressmempool {\"addresses\":[\"" + addr + "\",\"" + addr + "\"]}")); + BOOST_CHECK_NO_THROW(CallRPC("getaddressmempool {\"addresses\":[\"" + addr + "\",\"" + addr + "\"]}")); BOOST_CHECK_NO_THROW(CallRPC("getaddressutxos {\"addresses\":[],\"chainInfo\":true}")); CheckRPCThrows("getaddressutxos {}", @@ -385,9 +385,9 @@ BOOST_AUTO_TEST_CASE(rpc_insightexplorer) // transaction does not exist: CheckRPCThrows("getspentinfo {\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\",\"index\":0}", - "Unable to get spent info"); + "Unable to get spent info"); CheckRPCThrows("getspentinfo {\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\"}", - "Invalid index, must be an integer"); + "Invalid index, must be an integer"); CheckRPCThrows("getspentinfo {\"txid\":\"hello\",\"index\":0}", "txid must be hexadecimal string (not 'hello')"); @@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(rpc_insightexplorer) CheckRPCThrows("getblockhashes 1477641360 1477641360 {\"noOrphans\":true,\"logicalTimes\":1}", "JSON value is not a boolean as expected"); CheckRPCThrows("getblockhashes 1477641360 1477641360 {\"noOrphans\":True,\"logicalTimes\":false}", - "Error parsing JSON:{\"noOrphans\":True,\"logicalTimes\":false}"); + "Error parsing JSON: {\"noOrphans\":True,\"logicalTimes\":false}"); // revert fExperimentalInsightExplorer = false; diff --git a/depend/zcash/src/test/script_P2SH_tests.cpp b/depend/zcash/src/test/script_P2SH_tests.cpp index 0a884eca0..8312c5ed0 100644 --- a/depend/zcash/src/test/script_P2SH_tests.cpp +++ b/depend/zcash/src/test/script_P2SH_tests.cpp @@ -281,7 +281,7 @@ BOOST_DATA_TEST_CASE(AreInputsStandard, boost::unit_test::data::xrange(static_ca { LOCK(cs_main); uint32_t consensusBranchId = NetworkUpgradeInfo[sample].nBranchId; - CCoinsView coinsDummy; + CCoinsViewDummy coinsDummy; CCoinsViewCache coins(&coinsDummy); CBasicKeyStore keystore; CKey key[6]; diff --git a/depend/zcash/src/test/sighash_tests.cpp b/depend/zcash/src/test/sighash_tests.cpp index 0455ffa78..0b8a7b55c 100644 --- a/depend/zcash/src/test/sighash_tests.cpp +++ b/depend/zcash/src/test/sighash_tests.cpp @@ -13,6 +13,7 @@ #include "test/test_bitcoin.h" #include "test/test_util.h" #include "util/system.h" +#include "util/test.h" #include "version.h" #include @@ -79,7 +80,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un } // Blank out the joinsplit signature. - memset(&txTmp.joinSplitSig.bytes, 0, ED25519_SIGNATURE_LEN); + txTmp.joinSplitSig.bytes.fill(0); // Serialize and hash CHashWriter ss(SER_GETHASH, 0); @@ -149,23 +150,10 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t co if (tx.nVersionGroupId == SAPLING_VERSION_GROUP_ID) { tx.valueBalanceSapling = InsecureRandRange(100000000); for (int spend = 0; spend < shielded_spends; spend++) { - SpendDescription sdesc; - zcash_test_harness_random_jubjub_point(sdesc.cv.begin()); - zcash_test_harness_random_jubjub_base(sdesc.anchor.begin()); - sdesc.nullifier = InsecureRand256(); - zcash_test_harness_random_jubjub_point(sdesc.rk.begin()); - GetRandBytes(sdesc.zkproof.begin(), sdesc.zkproof.size()); - tx.vShieldedSpend.push_back(sdesc); + tx.vShieldedSpend.push_back(RandomInvalidSpendDescription()); } for (int out = 0; out < shielded_outs; out++) { - OutputDescription odesc; - zcash_test_harness_random_jubjub_point(odesc.cv.begin()); - zcash_test_harness_random_jubjub_base(odesc.cmu.begin()); - zcash_test_harness_random_jubjub_point(odesc.ephemeralKey.begin()); - GetRandBytes(odesc.encCiphertext.begin(), odesc.encCiphertext.size()); - GetRandBytes(odesc.outCiphertext.begin(), odesc.outCiphertext.size()); - GetRandBytes(odesc.zkproof.begin(), odesc.zkproof.size()); - tx.vShieldedOutput.push_back(odesc); + tx.vShieldedOutput.push_back(RandomInvalidOutputDescription()); } } // We have removed pre-Sapling Sprout support. @@ -196,8 +184,8 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t co tx.vJoinSplit.push_back(jsdesc); } - Ed25519SigningKey joinSplitPrivKey; - ed25519_generate_keypair(&joinSplitPrivKey, &tx.joinSplitPubKey); + ed25519::SigningKey joinSplitPrivKey; + ed25519::generate_keypair(joinSplitPrivKey, tx.joinSplitPubKey); // Empty output script. CScript scriptCode; @@ -205,10 +193,10 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t co PrecomputedTransactionData txdata(signTx, {}); uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata); - assert(ed25519_sign( - &joinSplitPrivKey, - dataToBeSigned.begin(), 32, - &tx.joinSplitSig)); + ed25519::sign( + joinSplitPrivKey, + {dataToBeSigned.begin(), 32}, + tx.joinSplitSig); } } diff --git a/depend/zcash/src/test/test_bitcoin.cpp b/depend/zcash/src/test/test_bitcoin.cpp index 010b05c44..3b76e0bd3 100644 --- a/depend/zcash/src/test/test_bitcoin.cpp +++ b/depend/zcash/src/test/test_bitcoin.cpp @@ -34,7 +34,7 @@ #include "librustzcash.h" -#include +#include const std::function G_TRANSLATION_FUN = nullptr; @@ -174,7 +174,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector& boost::shared_ptr mAddr(new CReserveScript()); mAddr->reserveScript = scriptPubKey; - CBlockTemplate *pblocktemplate = CreateNewBlock(chainparams, mAddr); + CBlockTemplate *pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(mAddr); CBlock& block = pblocktemplate->block; // Replace mempool-selected txns with just coinbase plus passed-in txns: @@ -231,7 +231,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPo CTransaction txn(tx); bool hasNoDependencies = pool ? pool->HasNoInputsOf(tx) : hadNoDependencies; - return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight, + return CTxMemPoolEntry(txn, nFee, nTime, nHeight, hasNoDependencies, spendsCoinbase, sigOpCount, nBranchId); } diff --git a/depend/zcash/src/test/test_bitcoin.h b/depend/zcash/src/test/test_bitcoin.h index 4c1c060df..9b3c1da6c 100644 --- a/depend/zcash/src/test/test_bitcoin.h +++ b/depend/zcash/src/test/test_bitcoin.h @@ -86,7 +86,6 @@ struct TestMemPoolEntryHelper // Default values CAmount nFee; int64_t nTime; - double dPriority; unsigned int nHeight; bool hadNoDependencies; bool spendsCoinbase; @@ -94,7 +93,7 @@ struct TestMemPoolEntryHelper uint32_t nBranchId; TestMemPoolEntryHelper() : - nFee(0), nTime(0), dPriority(0.0), nHeight(1), + nFee(0), nTime(0), nHeight(1), hadNoDependencies(false), spendsCoinbase(false), sigOpCount(1), nBranchId(SPROUT_BRANCH_ID) { } @@ -103,7 +102,6 @@ struct TestMemPoolEntryHelper // Change the default value TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; } TestMemPoolEntryHelper &Time(int64_t _time) { nTime = _time; return *this; } - TestMemPoolEntryHelper &Priority(double _priority) { dPriority = _priority; return *this; } TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; } TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; } TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } diff --git a/depend/zcash/src/test/test_util.cpp b/depend/zcash/src/test/test_util.cpp index 1636ab6b0..9c80a5a68 100644 --- a/depend/zcash/src/test/test_util.cpp +++ b/depend/zcash/src/test/test_util.cpp @@ -94,11 +94,15 @@ UniValue CallRPC(std::string args) vArgs[i] = ""; } } - UniValue params = RPCConvertValues(strMethod, vArgs); + auto params = RPCConvertValues(strMethod, vArgs); + + params.map_error([&](auto failure) { + throw std::runtime_error(FormatConversionFailure(strMethod, failure)); + }); rpcfn_type method = tableRPC[strMethod]->actor; try { - UniValue result = (*method)(params, false); + UniValue result = (*method)(params.value(), false); return result; } catch (const UniValue& objError) { @@ -117,4 +121,3 @@ void CheckRPCThrows(std::string rpcString, std::string expectedErrorMessage) { BOOST_FAIL(std::string("Unexpected exception: ") + typeid(e).name() + ", message=\"" + e.what() + "\""); } } - diff --git a/depend/zcash/src/test/transaction_tests.cpp b/depend/zcash/src/test/transaction_tests.cpp index fafff1310..ea9ac0caf 100644 --- a/depend/zcash/src/test/transaction_tests.cpp +++ b/depend/zcash/src/test/transaction_tests.cpp @@ -35,8 +35,8 @@ #include #include +#include #include -#include #include #include @@ -309,8 +309,7 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact CMutableTransaction newTx(tx); CValidationState state; - newTx.vShieldedSpend.push_back(SpendDescription()); - newTx.vShieldedSpend[0].nullifier = InsecureRand256(); + newTx.vShieldedSpend.push_back(RandomInvalidSpendDescription()); BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state)); BOOST_CHECK(state.GetRejectReason() == "bad-txns-no-sink-of-funds"); @@ -320,12 +319,11 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact CMutableTransaction newTx(tx); CValidationState state; - newTx.vShieldedSpend.push_back(SpendDescription()); - newTx.vShieldedSpend[0].nullifier = InsecureRand256(); + newTx.vShieldedSpend.push_back(RandomInvalidSpendDescription()); - newTx.vShieldedOutput.push_back(OutputDescription()); + newTx.vShieldedOutput.push_back(RandomInvalidOutputDescription()); - newTx.vShieldedSpend.push_back(SpendDescription()); + newTx.vShieldedSpend.push_back(RandomInvalidSpendDescription()); newTx.vShieldedSpend[1].nullifier = newTx.vShieldedSpend[0].nullifier; BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state)); @@ -347,7 +345,7 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact vout.nValue = 1; newTx.vout.push_back(vout); - newTx.vShieldedSpend.push_back(SpendDescription()); + newTx.vShieldedSpend.push_back(RandomInvalidSpendDescription()); BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state)); BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-spend-description"); @@ -358,7 +356,7 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa { auto verifier = ProofVerifier::Strict(); std::optional> saplingAuth = std::nullopt; - auto orchardAuth = orchard::AuthValidator::Disabled(); + std::optional> orchardAuth = std::nullopt; { // Ensure that empty vin/vout remain invalid without // joinsplits. @@ -367,8 +365,8 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa AssumeShieldedInputsExistAndAreSpendable baseView; CCoinsViewCache view(&baseView); - Ed25519SigningKey joinSplitPrivKey; - ed25519_generate_keypair(&joinSplitPrivKey, &newTx.joinSplitPubKey); + ed25519::SigningKey joinSplitPrivKey; + ed25519::generate_keypair(joinSplitPrivKey, newTx.joinSplitPubKey); // No joinsplits, vin and vout, means it should be invalid. BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state)); @@ -406,10 +404,10 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa CTransaction signTx(newTx); uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata); - assert(ed25519_sign( - &joinSplitPrivKey, - dataToBeSigned.begin(), 32, - &newTx.joinSplitSig)); + ed25519::sign( + joinSplitPrivKey, + {dataToBeSigned.begin(), 32}, + newTx.joinSplitSig); state = CValidationState(); BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state)); @@ -551,7 +549,7 @@ BOOST_DATA_TEST_CASE(test_Get, boost::unit_test::data::xrange(static_cast(C uint32_t consensusBranchId = NetworkUpgradeInfo[sample].nBranchId; CBasicKeyStore keystore; - CCoinsView coinsDummy; + CCoinsViewDummy coinsDummy; CCoinsViewCache coins(&coinsDummy); std::vector dummyTransactions = SetupDummyInputs(keystore, coins); @@ -675,7 +673,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) LOCK(cs_main); auto chainparams = Params(); CBasicKeyStore keystore; - CCoinsView coinsDummy; + CCoinsViewDummy coinsDummy; CCoinsViewCache coins(&coinsDummy); std::vector dummyTransactions = SetupDummyInputs(keystore, coins); @@ -692,8 +690,9 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) string reason; BOOST_CHECK(IsStandardTx(t, reason, chainparams)); - // Check dust with default relay fee: - CAmount nDustThreshold = 182 * minRelayTxFee.GetFeePerK()/1000 * 3; + // Check dust threshold: + CFeeRate oneThirdDustThresholdRate{ONE_THIRD_DUST_THRESHOLD_RATE}; + CAmount nDustThreshold = (34 + 148) * oneThirdDustThresholdRate.GetFeePerK()/1000 * 3; BOOST_CHECK_EQUAL(nDustThreshold, 54); // dust: t.vout[0].nValue = nDustThreshold - 1; @@ -702,17 +701,6 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].nValue = nDustThreshold; BOOST_CHECK(IsStandardTx(t, reason, chainparams)); - // Check dust with odd relay fee to verify rounding: - // nDustThreshold = 182 * 1234 / 1000 * 3 - minRelayTxFee = CFeeRate(1234); - // dust: - t.vout[0].nValue = 672 - 1; - BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); - // not dust: - t.vout[0].nValue = 672; - BOOST_CHECK(IsStandardTx(t, reason, chainparams)); - minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); - t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); @@ -769,7 +757,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandardV2) LOCK(cs_main); auto chainparams = Params(); CBasicKeyStore keystore; - CCoinsView coinsDummy; + CCoinsViewDummy coinsDummy; CCoinsViewCache coins(&coinsDummy); std::vector dummyTransactions = SetupDummyInputs(keystore, coins); diff --git a/depend/zcash/src/transaction_builder.cpp b/depend/zcash/src/transaction_builder.cpp index 0d8cba327..3b074a154 100644 --- a/depend/zcash/src/transaction_builder.cpp +++ b/depend/zcash/src/transaction_builder.cpp @@ -457,7 +457,7 @@ void TransactionBuilder::SendChangeTo( saplingChangeAddr = std::nullopt; sproutChangeAddr = std::nullopt; - std::visit(match { + examine(changeAddr, match { [&](const CKeyID& keyId) { tChangeAddr = keyId; }, @@ -470,7 +470,7 @@ void TransactionBuilder::SendChangeTo( [&](const libzcash::OrchardRawAddress& changeDest) { orchardChangeAddr = std::make_pair(ovk, changeDest); } - }, changeAddr); + }); } void TransactionBuilder::SendChangeToSprout(const libzcash::SproutPaymentAddress& zaddr) { @@ -501,7 +501,8 @@ TransactionBuilderResult TransactionBuilder::Build() change -= tOut.nValue; } if (change < 0) { - return TransactionBuilderResult("Change cannot be negative"); + return TransactionBuilderResult( + strprintf("Change cannot be negative: %s", DisplayMoney(change))); } // @@ -617,8 +618,8 @@ TransactionBuilderResult TransactionBuilder::Build() // Sprout JoinSplits // - Ed25519SigningKey joinSplitPrivKey; - ed25519_generate_keypair(&joinSplitPrivKey, &mtx.joinSplitPubKey); + ed25519::SigningKey joinSplitPrivKey; + ed25519::generate_keypair(joinSplitPrivKey, mtx.joinSplitPubKey); // Create Sprout JSDescriptions if (!jsInputs.empty() || !jsOutputs.empty()) { @@ -676,19 +677,16 @@ TransactionBuilderResult TransactionBuilder::Build() mtx.bindingSig); // Create Sprout joinSplitSig - if (!ed25519_sign( - &joinSplitPrivKey, - dataToBeSigned.begin(), 32, - &mtx.joinSplitSig)) - { - return TransactionBuilderResult("Failed to create Sprout joinSplitSig"); - } + ed25519::sign( + joinSplitPrivKey, + {dataToBeSigned.begin(), 32}, + mtx.joinSplitSig); // Sanity check Sprout joinSplitSig - if (!ed25519_verify( - &mtx.joinSplitPubKey, - &mtx.joinSplitSig, - dataToBeSigned.begin(), 32)) + if (!ed25519::verify( + mtx.joinSplitPubKey, + mtx.joinSplitSig, + {dataToBeSigned.begin(), 32})) { return TransactionBuilderResult("Sprout joinSplitSig sanity check failed"); } diff --git a/depend/zcash/src/transaction_builder.h b/depend/zcash/src/transaction_builder.h index c38842c4a..6aaa64f0a 100644 --- a/depend/zcash/src/transaction_builder.h +++ b/depend/zcash/src/transaction_builder.h @@ -22,8 +22,9 @@ #include +#include #include -#include +#include #define NO_MEMO {{0xF6}} @@ -208,7 +209,7 @@ struct OutputDescriptionInfo { }; struct JSDescriptionInfo { - Ed25519VerificationKey joinSplitPubKey; + ed25519::VerificationKey joinSplitPubKey; uint256 anchor; // We store references to these so they are correctly randomised for the caller. std::array& inputs; @@ -217,7 +218,7 @@ struct JSDescriptionInfo { CAmount vpub_new; JSDescriptionInfo( - Ed25519VerificationKey joinSplitPubKey, + ed25519::VerificationKey joinSplitPubKey, uint256 anchor, std::array& inputs, std::array& outputs, diff --git a/depend/zcash/src/txdb.h b/depend/zcash/src/txdb.h index cc1cf9870..08147e28e 100644 --- a/depend/zcash/src/txdb.h +++ b/depend/zcash/src/txdb.h @@ -81,6 +81,7 @@ class CCoinsViewDB : public CCoinsView CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory = false, bool fWipe = false); public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); + ~CCoinsViewDB() {} bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const; bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const; diff --git a/depend/zcash/src/txmempool.cpp b/depend/zcash/src/txmempool.cpp index e68fc2cd0..39491ac03 100644 --- a/depend/zcash/src/txmempool.cpp +++ b/depend/zcash/src/txmempool.cpp @@ -17,26 +17,27 @@ #include "util/moneystr.h" #include "validationinterface.h" #include "version.h" +#include "mempool_limit.h" +#include "zip317.h" #include using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, - int64_t _nTime, double _dPriority, - unsigned int _nHeight, bool poolHasNoInputsOf, + int64_t _nTime, unsigned int _nHeight, + bool poolHasNoInputsOf, bool _spendsCoinbase, unsigned int _sigOps, uint32_t _nBranchId): - tx(std::make_shared(_tx)), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight), + tx(std::make_shared(_tx)), nFee(_nFee), nTime(_nTime), nHeight(_nHeight), hadNoDependencies(poolHasNoInputsOf), spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps), nBranchId(_nBranchId) { nTxSize = ::GetSerializeSize(_tx, SER_NETWORK, PROTOCOL_VERSION); - nModSize = _tx.CalculateModifiedSize(nTxSize); nUsageSize = RecursiveDynamicUsage(*tx) + memusage::DynamicUsage(tx); nCountWithDescendants = 1; nSizeWithDescendants = nTxSize; - nFeesWithDescendants = nFee; + nModFeesWithDescendants = nFee; feeDelta = 0; } @@ -46,17 +47,9 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) *this = other; } -double -CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const -{ - CAmount nValueIn = tx->GetValueOut()+nFee; - double deltaPriority = ((double)(currentHeight-nHeight)*nValueIn)/nModSize; - double dResult = dPriority + deltaPriority; - return dResult; -} - void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta) { + nModFeesWithDescendants += newFeeDelta - feeDelta; feeDelta = newFeeDelta; } @@ -114,7 +107,7 @@ bool CTxMemPool::UpdateForDescendants(txiter updateIt, int maxDescendantsToVisit for (txiter cit : setAllDescendants) { if (!setExclude.count(cit->GetTx().GetHash())) { modifySize += cit->GetTxSize(); - modifyFee += cit->GetFee(); + modifyFee += cit->GetModifiedFee(); modifyCount++; cachedDescendants[updateIt].insert(cit); } @@ -244,7 +237,7 @@ void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors } const int64_t updateCount = (add ? 1 : -1); const int64_t updateSize = updateCount * it->GetTxSize(); - const CAmount updateFee = updateCount * it->GetFee(); + const CAmount updateFee = updateCount * it->GetModifiedFee(); for (txiter ancestorIt : setAncestors) { mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, updateCount)); } @@ -304,7 +297,28 @@ void CTxMemPoolEntry::SetDirty() { nCountWithDescendants = 0; nSizeWithDescendants = nTxSize; - nFeesWithDescendants = nFee; + nModFeesWithDescendants = GetModifiedFee(); +} + +size_t CTxMemPoolEntry::GetUnpaidActionCount() const +{ + if (tx->IsCoinBase()) { + return 0; + } else { + return std::max( + int64_t {0}, + (int64_t) std::max(GRACE_ACTIONS, tx->GetLogicalActionCount()) - (GetModifiedFee() / MARGINAL_FEE)); + } +} + +// Return a fixed-point representation of the entry's weight ratio, where 1 is represented by WEIGHT_RATIO_SCALE. +int128_t CTxMemPoolEntry::GetWeightRatio() const +{ + // ensure that the result will always be nonzero + static_assert(WEIGHT_RATIO_SCALE > MAX_MONEY); + return std::min( + (int128_t {WEIGHT_RATIO_SCALE} * std::max(CAmount {1}, GetModifiedFee())) / tx->GetConventionalFee(), + int128_t {WEIGHT_RATIO_SCALE} * WEIGHT_RATIO_CAP); } void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount) @@ -312,8 +326,7 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t if (!IsDirty()) { nSizeWithDescendants += modifySize; assert(int64_t(nSizeWithDescendants) > 0); - nFeesWithDescendants += modifyFee; - assert(nFeesWithDescendants >= 0); + nModFeesWithDescendants += modifyFee; nCountWithDescendants += modifyCount; assert(int64_t(nCountWithDescendants) > 0); } @@ -337,7 +350,7 @@ CTxMemPool::~CTxMemPool() { delete minerPolicyEstimator; delete recentlyEvicted; - delete weightedTxTree; + delete limitSet; } void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) @@ -371,10 +384,22 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. LOCK(cs); - weightedTxTree->add(WeightedTxInfo::from(entry.GetTx(), entry.GetFee())); + auto [cost, evictionWeight] = MempoolCostAndEvictionWeight(entry.GetTx(), entry.GetFee()); + limitSet->add(entry.GetTx().GetHash(), cost, evictionWeight); indexed_transaction_set::iterator newit = mapTx.insert(entry).first; mapLinks.insert(make_pair(newit, TxLinks())); + // Update transaction for any feeDelta created by PrioritiseTransaction + // TODO: refactor so that the fee delta is calculated before inserting + // into mapTx. + std::map::const_iterator pos = mapDeltas.find(hash); + if (pos != mapDeltas.end()) { + const CAmount &delta = pos->second; + if (delta) { + mapTx.modify(newit, update_fee_delta(delta)); + } + } + // Update cachedInnerUsage to include contained transaction's usage. // (When we update the entry for in-mempool parents, memory usage will be // further updated.) @@ -416,15 +441,6 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, mapOrchardNullifiers[orchardNullifier] = &tx; } - // Update transaction's score for any feeDelta created by PrioritiseTransaction - std::map >::const_iterator pos = mapDeltas.find(hash); - if (pos != mapDeltas.end()) { - const std::pair &deltas = pos->second; - if (deltas.second) { - mapTx.modify(newit, update_fee_delta(deltas.second)); - } - } - nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); minerPolicyEstimator->processTransaction(entry, fCurrentEstimate); @@ -637,7 +653,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list& rem } RemoveStaged(setAllRemoves); for (CTransaction tx : removed) { - weightedTxTree->remove(tx.GetHash()); + limitSet->remove(tx.GetHash()); } } } @@ -907,27 +923,24 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const CTxMemPool::setEntries setChildrenCheck; std::map::const_iterator iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0)); int64_t childSizes = 0; - CAmount childFees = 0; + CAmount childModFee = 0; for (; iter != mapNextTx.end() && iter->first.hash == it->GetTx().GetHash(); ++iter) { txiter childit = mapTx.find(iter->second.ptx->GetHash()); assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions if (setChildrenCheck.insert(childit).second) { childSizes += childit->GetTxSize(); - childFees += childit->GetFee(); + childModFee += childit->GetModifiedFee(); } } assert(setChildrenCheck == GetMemPoolChildren(it)); - // Also check to make sure size/fees is greater than sum with immediate children. + // Also check to make sure size is greater than sum with immediate children. // just a sanity check, not definitive that this calc is correct... - // also check that the size is less than the size of the entire mempool. if (!it->IsDirty()) { assert(it->GetSizeWithDescendants() >= childSizes + it->GetTxSize()); - assert(it->GetFeesWithDescendants() >= childFees + it->GetFee()); } else { assert(it->GetSizeWithDescendants() == it->GetTxSize()); - assert(it->GetFeesWithDescendants() == it->GetFee()); + assert(it->GetModFeesWithDescendants() == it->GetModifiedFee()); } - assert(it->GetFeesWithDescendants() >= 0); if (fDependsWait) waitingOnDependants.push_back(&(*it)); @@ -1095,19 +1108,14 @@ CFeeRate CTxMemPool::estimateFee(int nBlocks) const LOCK(cs); return minerPolicyEstimator->estimateFee(nBlocks); } -double CTxMemPool::estimatePriority(int nBlocks) const -{ - LOCK(cs); - return minerPolicyEstimator->estimatePriority(nBlocks); -} bool CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const { try { LOCK(cs); - fileout << 109900; // version required to read: 0.10.99 or later - fileout << CLIENT_VERSION; // version that wrote the file + fileout << FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION; // version required to read + fileout << std::max(FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION, CLIENT_VERSION); // version that wrote the file minerPolicyEstimator->Write(fileout); } catch (const std::exception&) { @@ -1123,11 +1131,10 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) try { int nVersionRequired, nVersionThatWrote; filein >> nVersionRequired >> nVersionThatWrote; - if (nVersionRequired > CLIENT_VERSION) + if (nVersionRequired > std::max(FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION, CLIENT_VERSION)) return error("CTxMemPool::ReadFeeEstimates(): up-version (%d) fee estimate file", nVersionRequired); - LOCK(cs); - minerPolicyEstimator->Read(filein); + minerPolicyEstimator->Read(filein, nVersionThatWrote); } catch (const std::exception&) { LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy estimator data (non-fatal)\n"); @@ -1136,30 +1143,36 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) return true; } -void CTxMemPool::PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, const CAmount& nFeeDelta) +void CTxMemPool::PrioritiseTransaction(const uint256 hash, const std::string strHash, const CAmount& nFeeDelta) { { LOCK(cs); - std::pair &deltas = mapDeltas[hash]; - deltas.first += dPriorityDelta; - deltas.second += nFeeDelta; + CAmount &delta = mapDeltas[hash]; + delta += nFeeDelta; txiter it = mapTx.find(hash); if (it != mapTx.end()) { - mapTx.modify(it, update_fee_delta(deltas.second)); + mapTx.modify(it, update_fee_delta(delta)); + // Now update all ancestors' modified fees with descendants + setEntries setAncestors; + uint64_t nNoLimit = std::numeric_limits::max(); + std::string dummy; + CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false); + BOOST_FOREACH(txiter ancestorIt, setAncestors) { + mapTx.modify(ancestorIt, update_descendant_state(0, nFeeDelta, 0)); + } } } - LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta)); + LogPrintf("PrioritiseTransaction: %s feerate += %s\n", strHash, FormatMoney(nFeeDelta)); } -void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta) const +void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const { LOCK(cs); - std::map >::const_iterator pos = mapDeltas.find(hash); + std::map::const_iterator pos = mapDeltas.find(hash); if (pos == mapDeltas.end()) return; - const std::pair &deltas = pos->second; - dPriorityDelta += deltas.first; - nFeeDelta += deltas.second; + const CAmount &delta = pos->second; + nFeeDelta += delta; } void CTxMemPool::ClearPrioritisation(const uint256 hash) @@ -1265,7 +1278,7 @@ size_t CTxMemPool::DynamicMemoryUsage() const { memusage::DynamicUsage(mapOrchardNullifiers); // DoS mitigation - total += memusage::DynamicUsage(recentlyEvicted) + memusage::DynamicUsage(weightedTxTree); + total += memusage::DynamicUsage(recentlyEvicted) + memusage::DynamicUsage(limitSet); // Insight-related structures size_t insight = 0; @@ -1282,9 +1295,9 @@ void CTxMemPool::SetMempoolCostLimit(int64_t totalCostLimit, int64_t evictionMem LOCK(cs); LogPrint("mempool", "Setting mempool cost limit: (limit=%d, time=%d)\n", totalCostLimit, evictionMemorySeconds); delete recentlyEvicted; - delete weightedTxTree; + delete limitSet; recentlyEvicted = new RecentlyEvictedList(GetNodeClock(), evictionMemorySeconds); - weightedTxTree = new WeightedTxTree(totalCostLimit); + limitSet = new MempoolLimitTxSet(totalCostLimit); } bool CTxMemPool::IsRecentlyEvicted(const uint256& txId) { @@ -1295,7 +1308,7 @@ bool CTxMemPool::IsRecentlyEvicted(const uint256& txId) { void CTxMemPool::EnsureSizeLimit() { AssertLockHeld(cs); std::optional maybeDropTxId; - while ((maybeDropTxId = weightedTxTree->maybeDropRandom()).has_value()) { + while ((maybeDropTxId = limitSet->maybeDropRandom()).has_value()) { uint256 txId = maybeDropTxId.value(); recentlyEvicted->add(txId); std::list removed; diff --git a/depend/zcash/src/txmempool.h b/depend/zcash/src/txmempool.h index e596022c5..9ddea3d31 100644 --- a/depend/zcash/src/txmempool.h +++ b/depend/zcash/src/txmempool.h @@ -11,6 +11,7 @@ #include #include +#include "int128.h" #include "amount.h" #include "coins.h" #include "mempool_limit.h" @@ -20,6 +21,7 @@ #include "addressindex.h" #include "spentindex.h" #include "util/time.h" +#include "weighted_map.h" #undef foreach #include "boost/multi_index_container.hpp" @@ -28,21 +30,8 @@ class CAutoFile; -inline double AllowFreeThreshold() -{ - // 144 is the number of Bitcoin blocks per day. This has not been updated for Zcash, - // as it would make it harder to get shielded transactions into blocks by lowering the - // threshold at which we switch from priority-based selection of transactions into - // blocks to fee-based selection. - return COIN * 144 / 250; -} - -inline bool AllowFree(double dPriority) -{ - // Large (in bytes) low-priority (new, small-coin) transactions - // need a fee. - return dPriority > AllowFreeThreshold(); -} +/** Version from which we write `fee_estimates.dat` without priority information: 5.5.0-beta1 or later */ +static const int FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION = 5050000; /** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; @@ -56,12 +45,12 @@ class CTxMemPool; * ("descendant" transactions). * * When a new entry is added to the mempool, we update the descendant state - * (nCountWithDescendants, nSizeWithDescendants, and nFeesWithDescendants) for + * (nCountWithDescendants, nSizeWithDescendants, and nModFeesWithDescendants) for * all ancestors of the newly added transaction. * * If updating the descendant state is skipped, we can mark the entry as - * "dirty", and set nSizeWithDescendants/nFeesWithDescendants to equal nTxSize/ - * nTxFee. (This can potentially happen during a reorg, where we limit the + * "dirty", and set nSizeWithDescendants/nModFeesWithDescendants to equal nTxSize/ + * nFee+feeDelta. (This can potentially happen during a reorg, where we limit the * amount of work we're willing to do to avoid consuming too much CPU.) * */ @@ -72,10 +61,8 @@ class CTxMemPoolEntry std::shared_ptr tx; CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups size_t nTxSize; //!< ... and avoid recomputing tx size - size_t nModSize; //!< ... and modified size for priority size_t nUsageSize; //!< ... and total memory usage int64_t nTime; //!< Local time when entering the mempool - double dPriority; //!< Priority when entering the mempool unsigned int nHeight; //!< Chain height when entering the mempool bool hadNoDependencies; //!< Not dependent on any other txs when it entered the mempool bool spendsCoinbase; //!< keep track of transactions that spend a coinbase @@ -86,23 +73,31 @@ class CTxMemPoolEntry // Information about descendants of this transaction that are in the // mempool; if we remove this transaction we must remove all of these // descendants as well. if nCountWithDescendants is 0, treat this entry as - // dirty, and nSizeWithDescendants and nFeesWithDescendants will not be + // dirty, and nSizeWithDescendants and nModFeesWithDescendants will not be // correct. uint64_t nCountWithDescendants; //! number of descendant transactions uint64_t nSizeWithDescendants; //! ... and size - CAmount nFeesWithDescendants; //! ... and total fees (all including us) + CAmount nModFeesWithDescendants; //! ... and total fees (all including us) public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, - int64_t _nTime, double _dPriority, unsigned int _nHeight, + int64_t _nTime, unsigned int _nHeight, bool poolHasNoInputsOf, bool spendsCoinbase, unsigned int nSigOps, uint32_t nBranchId); CTxMemPoolEntry(const CTxMemPoolEntry& other); const CTransaction& GetTx() const { return *this->tx; } std::shared_ptr GetSharedTx() const { return this->tx; } - double GetPriority(unsigned int currentHeight) const; const CAmount& GetFee() const { return nFee; } + + // Return the number of unpaid actions calculated according to ZIP 317. + // + size_t GetUnpaidActionCount() const; + + // Return a fixed-point representation of the entry's weight ratio according + // to ZIP 317, where 1 is represented by WEIGHT_RATIO_SCALE. + int128_t GetWeightRatio() const; + size_t GetTxSize() const { return nTxSize; } int64_t GetTime() const { return nTime; } unsigned int GetHeight() const { return nHeight; } @@ -113,7 +108,8 @@ class CTxMemPoolEntry // Adjusts the descendant state, if this entry is not dirty. void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount); - // Updates the fee delta used for mining priority score + // Updates the fee delta used for mining priority score, and the + // modified fees with descendants. void UpdateFeeDelta(int64_t feeDelta); /** We can set the entry to be dirty if doing the full calculation of in- @@ -125,7 +121,7 @@ class CTxMemPoolEntry uint64_t GetCountWithDescendants() const { return nCountWithDescendants; } uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } - CAmount GetFeesWithDescendants() const { return nFeesWithDescendants; } + CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; } bool GetSpendsCoinbase() const { return spendsCoinbase; } uint32_t GetValidatedBranchId() const { return nBranchId; } @@ -173,27 +169,27 @@ struct mempoolentry_txid } }; -/** \class CompareTxMemPoolEntryByFee +/** \class CompareTxMemPoolEntryByDescendantScore * - * Sort an entry by max(feerate of entry's tx, feerate with all descendants). + * Sort an entry by max(score/size of entry's tx, score/size with all descendants). */ -class CompareTxMemPoolEntryByFee +class CompareTxMemPoolEntryByDescendantScore { public: bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const { - bool fUseADescendants = UseDescendantFeeRate(a); - bool fUseBDescendants = UseDescendantFeeRate(b); + bool fUseADescendants = UseDescendantScore(a); + bool fUseBDescendants = UseDescendantScore(b); - double aFees = fUseADescendants ? a.GetFeesWithDescendants() : a.GetFee(); + double aModFee = fUseADescendants ? a.GetModFeesWithDescendants() : a.GetModifiedFee(); double aSize = fUseADescendants ? a.GetSizeWithDescendants() : a.GetTxSize(); - double bFees = fUseBDescendants ? b.GetFeesWithDescendants() : b.GetFee(); + double bModFee = fUseBDescendants ? b.GetModFeesWithDescendants() : b.GetModifiedFee(); double bSize = fUseBDescendants ? b.GetSizeWithDescendants() : b.GetTxSize(); // Avoid division by rewriting (a/b > c/d) as (a*d > c*b). - double f1 = aFees * bSize; - double f2 = aSize * bFees; + double f1 = aModFee * bSize; + double f2 = aSize * bModFee; if (f1 == f2) { return a.GetTime() >= b.GetTime(); @@ -201,11 +197,11 @@ class CompareTxMemPoolEntryByFee return f1 < f2; } - // Calculate which feerate to use for an entry (avoiding division). - bool UseDescendantFeeRate(const CTxMemPoolEntry &a) const + // Calculate which score to use for an entry (avoiding division). + bool UseDescendantScore(const CTxMemPoolEntry &a) const { - double f1 = (double)a.GetFee() * a.GetSizeWithDescendants(); - double f2 = (double)a.GetFeesWithDescendants() * a.GetTxSize(); + double f1 = (double)a.GetModifiedFee() * a.GetSizeWithDescendants(); + double f2 = (double)a.GetModFeesWithDescendants() * a.GetTxSize(); return f2 > f1; } }; @@ -237,6 +233,10 @@ class CompareTxMemPoolEntryByEntryTime } }; +// Multi_index tag names +struct descendant_score {}; +struct mining_score {}; + class CBlockPolicyEstimator; /** An inpoint - a combination of a transaction and an index n into its vin */ @@ -362,7 +362,7 @@ class CTxMemPool std::map mapSaplingNullifiers; std::map mapOrchardNullifiers; RecentlyEvictedList* recentlyEvicted = new RecentlyEvictedList(GetNodeClock(), DEFAULT_MEMPOOL_EVICTION_MEMORY_MINUTES * 60); - WeightedTxTree* weightedTxTree = new WeightedTxTree(DEFAULT_MEMPOOL_TOTAL_COST_LIMIT); + MempoolLimitTxSet* limitSet = new MempoolLimitTxSet(DEFAULT_MEMPOOL_TOTAL_COST_LIMIT); void checkNullifiers(ShieldedType type) const; @@ -376,11 +376,13 @@ class CTxMemPool boost::multi_index::hashed_unique, // sorted by fee rate boost::multi_index::ordered_non_unique< + boost::multi_index::tag, boost::multi_index::identity, - CompareTxMemPoolEntryByFee + CompareTxMemPoolEntryByDescendantScore >, - // sorted by score (for mining prioritization) + // sorted by score (for mining prioritisation) boost::multi_index::ordered_unique< + boost::multi_index::tag, boost::multi_index::identity, CompareTxMemPoolEntryByScore > @@ -432,6 +434,9 @@ class CTxMemPool } }; typedef std::set setEntries; + typedef std::deque queueEntries; + // Type of a set of candidate transactions to be added to a block template. + typedef WeightedMap weightedCandidates; const setEntries & GetMemPoolParents(txiter entry) const; const setEntries & GetMemPoolChildren(txiter entry) const; @@ -459,7 +464,7 @@ class CTxMemPool public: std::map mapNextTx; - std::map > mapDeltas; + std::map mapDeltas; /** Create a new CTxMemPool. * minReasonableRelayFee should be a feerate which is, roughly, somewhere @@ -518,8 +523,8 @@ class CTxMemPool bool HasNoInputsOf(const CTransaction& tx) const; /** Affect CreateNewBlock prioritisation of transactions */ - void PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, const CAmount& nFeeDelta); - void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta) const; + void PrioritiseTransaction(const uint256 hash, const std::string strHash, const CAmount& nFeeDelta); + void ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const; void ClearPrioritisation(const uint256 hash); public: @@ -582,9 +587,6 @@ class CTxMemPool /** Estimate fee rate needed to get into the next nBlocks */ CFeeRate estimateFee(int nBlocks) const; - /** Estimate priority needed to get into the next nBlocks */ - double estimatePriority(int nBlocks) const; - /** Write/Read estimates to disk */ bool WriteFeeEstimates(CAutoFile& fileout) const; bool ReadFeeEstimates(CAutoFile& filein); @@ -656,22 +658,11 @@ class CCoinsViewMemPool : public CCoinsViewBacked public: CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn); + ~CCoinsViewMemPool() {} + bool GetNullifier(const uint256 &txid, ShieldedType type) const; bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; }; -// We want to sort transactions by coin age priority -typedef std::pair TxCoinAgePriority; - -struct TxCoinAgePriorityCompare -{ - bool operator()(const TxCoinAgePriority& a, const TxCoinAgePriority& b) - { - if (a.first == b.first) - return CompareTxMemPoolEntryByScore()(*(b.second), *(a.second)); //Reverse order to make sort less than - return a.first < b.first; - } -}; - #endif // BITCOIN_TXMEMPOOL_H diff --git a/depend/zcash/src/util/match.h b/depend/zcash/src/util/match.h index 79aa972b8..e285149dc 100644 --- a/depend/zcash/src/util/match.h +++ b/depend/zcash/src/util/match.h @@ -21,4 +21,17 @@ template struct match : Ts... { using Ts::operator()...; }; // explicit deduction guide (not needed as of C++20) template match(Ts...) -> match; +// A wrapper around two-argument `std::visit` that reverses the arguments, putting the +// value to be visited first. This is normally used as: +// +// examine(specimen, match { +// ... +// }) +// +// The return type is inferred as it would be for `std::visit`. +template +decltype(auto) examine(Specimen&& specimen, Visitor&& visitor) { + return std::visit(visitor, specimen); +} + #endif // ZCASH_UTIL_MATCH_H diff --git a/depend/zcash/src/util/moneystr.cpp b/depend/zcash/src/util/moneystr.cpp index 87e94fd6a..dfc9ed0dd 100644 --- a/depend/zcash/src/util/moneystr.cpp +++ b/depend/zcash/src/util/moneystr.cpp @@ -33,6 +33,10 @@ std::string FormatMoney(const CAmount& n) return str; } +std::string DisplayMoney(const CAmount& zat) +{ + return FormatMoney(zat) + " " + CURRENCY_UNIT; +} bool ParseMoney(const string& str, CAmount& nRet) { diff --git a/depend/zcash/src/util/moneystr.h b/depend/zcash/src/util/moneystr.h index 2287528e1..8ebabf6e7 100644 --- a/depend/zcash/src/util/moneystr.h +++ b/depend/zcash/src/util/moneystr.h @@ -16,6 +16,9 @@ #include "amount.h" std::string FormatMoney(const CAmount& n); +/// Like `FormatMoney`, but meant to be human-readable, not parseable. E.g., it includes the +/// `CURRENCY_UNIT` in the result. +std::string DisplayMoney(const CAmount& n); bool ParseMoney(const std::string& str, CAmount& nRet); bool ParseMoney(const char* pszIn, CAmount& nRet); diff --git a/depend/zcash/src/util/test.cpp b/depend/zcash/src/util/test.cpp index 9d14748b5..8f0b16fe1 100644 --- a/depend/zcash/src/util/test.cpp +++ b/depend/zcash/src/util/test.cpp @@ -11,6 +11,7 @@ #include #include +#include // Sprout CMutableTransaction GetValidSproutReceiveTransaction( @@ -40,8 +41,8 @@ CMutableTransaction GetValidSproutReceiveTransaction( allPrevOutputs.resize(mtx.vin.size()); // Generate an ephemeral keypair. - Ed25519SigningKey joinSplitPrivKey; - ed25519_generate_keypair(&joinSplitPrivKey, &mtx.joinSplitPubKey); + ed25519::SigningKey joinSplitPrivKey; + ed25519::generate_keypair(joinSplitPrivKey, mtx.joinSplitPubKey); std::array inputs = { libzcash::JSInput(), // dummy input @@ -64,8 +65,7 @@ CMutableTransaction GetValidSproutReceiveTransaction( // depend on this happening. if (version >= 4) { // Shielded Output - OutputDescription od; - mtx.vShieldedOutput.push_back(od); + mtx.vShieldedOutput.push_back(RandomInvalidOutputDescription()); } // Empty output script. @@ -76,10 +76,10 @@ CMutableTransaction GetValidSproutReceiveTransaction( uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata); // Add the signature - assert(ed25519_sign( - &joinSplitPrivKey, - dataToBeSigned.begin(), 32, - &mtx.joinSplitSig)); + ed25519::sign( + joinSplitPrivKey, + {dataToBeSigned.begin(), 32}, + mtx.joinSplitSig); return mtx; } @@ -143,8 +143,8 @@ CWalletTx GetValidSproutSpend(const libzcash::SproutSpendingKey& sk, mtx.vout[1].nValue = 0; // Generate an ephemeral keypair. - Ed25519SigningKey joinSplitPrivKey; - ed25519_generate_keypair(&joinSplitPrivKey, &mtx.joinSplitPubKey); + ed25519::SigningKey joinSplitPrivKey; + ed25519::generate_keypair(joinSplitPrivKey, mtx.joinSplitPubKey); // Fake tree for the unused witness SproutMerkleTree tree; @@ -193,10 +193,10 @@ CWalletTx GetValidSproutSpend(const libzcash::SproutSpendingKey& sk, uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata); // Add the signature - assert(ed25519_sign( - &joinSplitPrivKey, - dataToBeSigned.begin(), 32, - &mtx.joinSplitSig)); + ed25519::sign( + joinSplitPrivKey, + {dataToBeSigned.begin(), 32}, + mtx.joinSplitSig); CTransaction tx {mtx}; CWalletTx wtx {NULL, tx}; return wtx; @@ -334,6 +334,27 @@ CKey AddTestCKeyToKeyStore(CBasicKeyStore& keyStore) { return tsk; } +SpendDescription RandomInvalidSpendDescription() { + SpendDescription sdesc; + zcash_test_harness_random_jubjub_point(sdesc.cv.begin()); + zcash_test_harness_random_jubjub_base(sdesc.anchor.begin()); + sdesc.nullifier = GetRandHash(); + zcash_test_harness_random_jubjub_point(sdesc.rk.begin()); + GetRandBytes(sdesc.zkproof.begin(), sdesc.zkproof.size()); + return sdesc; +} + +OutputDescription RandomInvalidOutputDescription() { + OutputDescription odesc; + zcash_test_harness_random_jubjub_point(odesc.cv.begin()); + zcash_test_harness_random_jubjub_base(odesc.cmu.begin()); + zcash_test_harness_random_jubjub_point(odesc.ephemeralKey.begin()); + GetRandBytes(odesc.encCiphertext.begin(), odesc.encCiphertext.size()); + GetRandBytes(odesc.outCiphertext.begin(), odesc.outCiphertext.size()); + GetRandBytes(odesc.zkproof.begin(), odesc.zkproof.size()); + return odesc; +} + TestSaplingNote GetTestSaplingNote(const libzcash::SaplingPaymentAddress& pa, CAmount value) { // Generate dummy Sapling note libzcash::SaplingNote note(pa, value, libzcash::Zip212Enabled::BeforeZip212); diff --git a/depend/zcash/src/util/test.h b/depend/zcash/src/util/test.h index 171b98039..94b16b642 100644 --- a/depend/zcash/src/util/test.h +++ b/depend/zcash/src/util/test.h @@ -33,6 +33,38 @@ class AssumeShieldedInputsExistAndAreSpendable : public CCoinsView { // Always return false so we treat every nullifier as being unspent. return false; } + + bool GetCoins(const uint256 &txid, CCoins &coins) const { return false; } + bool HaveCoins(const uint256 &txid) const { return false; } + uint256 GetBestBlock() const { + throw std::runtime_error("`GetBestBlock` unimplemented for mock AssumeShieldedInputsExistAndAreSpendable"); + } + uint256 GetBestAnchor(ShieldedType type) const { + throw std::runtime_error("`GetBestAnchor` unimplemented for mock AssumeShieldedInputsExistAndAreSpendable"); + } + HistoryIndex GetHistoryLength(uint32_t epochId) const { return 0; } + HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const { + throw std::runtime_error("`GetHistoryAt` unimplemented for mock AssumeShieldedInputsExistAndAreSpendable"); + } + uint256 GetHistoryRoot(uint32_t epochId) const { + throw std::runtime_error("`GetHistoryRoot` unimplemented for mock AssumeShieldedInputsExistAndAreSpendable"); + } + + bool BatchWrite(CCoinsMap &mapCoins, + const uint256 &hashBlock, + const uint256 &hashSproutAnchor, + const uint256 &hashSaplingAnchor, + const uint256 &hashOrchardAnchor, + CAnchorsSproutMap &mapSproutAnchors, + CAnchorsSaplingMap &mapSaplingAnchors, + CAnchorsOrchardMap &mapOrchardAnchors, + CNullifiersMap &mapSproutNullifiers, + CNullifiersMap &mapSaplingNullifiers, + CNullifiersMap &mapOrchardNullifiers, + CHistoryCacheMap &historyCacheMap) { + return false; + } + bool GetStats(CCoinsStats &stats) const { return false; } }; // Sprout @@ -90,6 +122,9 @@ libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey(); CKey AddTestCKeyToKeyStore(CBasicKeyStore& keyStore); +SpendDescription RandomInvalidSpendDescription(); +OutputDescription RandomInvalidOutputDescription(); + /** * Generate a dummy SaplingNote and a SaplingMerkleTree with that note's commitment. */ diff --git a/depend/zcash/src/validationinterface.cpp b/depend/zcash/src/validationinterface.cpp index 21de7514c..599f85ed0 100644 --- a/depend/zcash/src/validationinterface.cpp +++ b/depend/zcash/src/validationinterface.cpp @@ -37,19 +37,15 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.ChainTip.connect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3)); - g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.AddressForMining.connect(boost::bind(&CValidationInterface::GetAddressForMining, pwalletIn, _1)); - g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1)); } void UnregisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1)); g_signals.AddressForMining.disconnect(boost::bind(&CValidationInterface::GetAddressForMining, pwalletIn, _1)); g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); - g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.ChainTip.disconnect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); @@ -59,11 +55,9 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { } void UnregisterAllValidationInterfaces() { - g_signals.BlockFound.disconnect_all_slots(); g_signals.AddressForMining.disconnect_all_slots(); g_signals.BlockChecked.disconnect_all_slots(); g_signals.Broadcast.disconnect_all_slots(); - g_signals.Inventory.disconnect_all_slots(); g_signals.ChainTip.disconnect_all_slots(); g_signals.UpdatedTransaction.disconnect_all_slots(); g_signals.EraseTransaction.disconnect_all_slots(); diff --git a/depend/zcash/src/validationinterface.h b/depend/zcash/src/validationinterface.h index 0d6f395c0..5ab13eab2 100644 --- a/depend/zcash/src/validationinterface.h +++ b/depend/zcash/src/validationinterface.h @@ -90,11 +90,9 @@ class CValidationInterface { virtual void EraseFromWallet(const uint256 &hash) {} virtual void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, std::optional added) {} virtual void UpdatedTransaction(const uint256 &hash) {} - virtual void Inventory(const uint256 &hash) {} virtual void ResendWalletTransactions(int64_t nBestBlockTime) {} virtual void BlockChecked(const CBlock&, const CValidationState&) {} virtual void GetAddressForMining(std::optional&) {}; - virtual void ResetRequestCount(const uint256 &hash) {}; friend void ::RegisterValidationInterface(CValidationInterface*); friend void ::UnregisterValidationInterface(CValidationInterface*); friend void ::UnregisterAllValidationInterfaces(); @@ -158,16 +156,12 @@ struct CMainSignals { boost::signals2::signal UpdatedTransaction; /** Notifies listeners of a change to the tip of the active block chain. */ boost::signals2::signal)> ChainTip; - /** Notifies listeners about an inventory item being seen on the network. */ - boost::signals2::signal Inventory; /** Tells listeners to broadcast their data. */ boost::signals2::signal Broadcast; /** Notifies listeners of a block validation result */ boost::signals2::signal BlockChecked; /** Notifies listeners that an address for mining is required (coinbase) */ boost::signals2::signal&)> AddressForMining; - /** Notifies listeners that a block has been successfully mined */ - boost::signals2::signal BlockFound; }; CMainSignals& GetMainSignals(); diff --git a/depend/zcash/src/wallet/asyncrpcoperation_common.cpp b/depend/zcash/src/wallet/asyncrpcoperation_common.cpp index 6ca224977..e2a907042 100644 --- a/depend/zcash/src/wallet/asyncrpcoperation_common.cpp +++ b/depend/zcash/src/wallet/asyncrpcoperation_common.cpp @@ -3,6 +3,8 @@ #include "core_io.h" #include "init.h" #include "rpc/protocol.h" +#include "util/moneystr.h" +#include "zip317.h" extern UniValue signrawtransaction(const UniValue& params, bool fHelp); @@ -40,3 +42,182 @@ std::pair SignSendRawTransaction(UniValue obj, std::opti return std::make_pair(tx, sendResult); } + +void ThrowInputSelectionError( + const InputSelectionError& err, + const ZTXOSelector& selector, + const TransactionStrategy& strategy) +{ + examine(err, match { + [&](const AddressResolutionError& err) { + switch (err) { + case AddressResolutionError::SproutRecipientsNotSupported: + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "Sending funds into the Sprout pool is no longer supported."); + case AddressResolutionError::TransparentRecipientNotAllowed: + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "This transaction would have transparent recipients, which is not " + "enabled by default because it will publicly reveal transaction " + "recipients and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit " + "with the `privacyPolicy` parameter set to `AllowRevealedRecipients` " + "or weaker if you wish to allow this transaction to proceed anyway."); + case AddressResolutionError::TransparentChangeNotAllowed: + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "This transaction would have transparent change, which is not " + "enabled by default because it will publicly reveal the change " + "address and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit " + "with the `privacyPolicy` parameter set to `AllowRevealedRecipients` " + "or weaker if you wish to allow this transaction to proceed anyway."); + case AddressResolutionError::RevealingSaplingAmountNotAllowed: + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "Could not send to the Sapling shielded pool without spending non-Sapling " + "funds, which would reveal transaction amounts. THIS MAY AFFECT YOUR " + "PRIVACY. Resubmit with the `privacyPolicy` parameter set to " + "`AllowRevealedAmounts` or weaker if you wish to allow this transaction to " + "proceed anyway."); + case AddressResolutionError::CouldNotResolveReceiver: + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("Could not send to an Orchard-only receiver %s.", + strategy.AllowRevealedAmounts() + ? strprintf("despite a lax privacy policy, because %s", + selector.SelectsSprout() + ? "you are sending from the Sprout pool and there is " + "no transaction version that supports both Sprout " + "and Orchard" + : "NU5 has not been activated yet") + : "without spending non-Orchard funds, which would reveal " + "transaction amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit " + "with the `privacyPolicy` parameter set to " + "`AllowRevealedAmounts` or weaker if you wish to allow this " + "transaction to proceed anyway")); + case AddressResolutionError::TransparentReceiverNotAllowed: + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "This transaction would send to a transparent receiver of a unified " + "address, which is not enabled by default because it will publicly reveal " + "transaction recipients and amounts. THIS MAY AFFECT YOUR PRIVACY. " + "Resubmit with the `privacyPolicy` parameter set to " + "`AllowRevealedRecipients` or weaker if you wish to allow this transaction " + "to proceed anyway."); + case AddressResolutionError::RevealingReceiverAmountsNotAllowed: + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "Could not send to a shielded receiver of a unified address without " + "spending funds from a different pool, which would reveal transaction " + "amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` " + "parameter set to `AllowRevealedAmounts` or weaker if you wish to allow " + "this transaction to proceed anyway."); + default: + assert(false); + } + }, + [&](const InvalidFundsError& err) { + bool isFromUa = std::holds_alternative(selector.GetPattern()); + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf( + "Insufficient funds: have %s, %s", + FormatMoney(err.available), + examine(err.reason, match { + [](const PhantomChangeError& qce) { + return strprintf( + "need %s more to surpass the dust threshold and avoid being " + "forced to over-pay the fee. Alternatively, you could specify " + "a fee of %s to allow overpayment of the conventional fee and " + "have this transaction proceed.", + FormatMoney(qce.dustThreshold), + FormatMoney(qce.finalFee)); + }, + [](const InsufficientFundsError& ife) { + return strprintf("need %s", FormatMoney(ife.required)); + }, + // TODO: Add the fee here, so we can suggest specifying an explicit fee (see + // `PhantomChangeError`). + [](const DustThresholdError& dte) { + return strprintf( + "need %s more to avoid creating invalid change output %s (dust threshold is %s)", + FormatMoney(dte.dustThreshold - dte.changeAmount), + FormatMoney(dte.changeAmount), + FormatMoney(dte.dustThreshold)); + } + })) + + (selector.TransparentCoinbasePolicy() != TransparentCoinbasePolicy::Disallow + ? "" : + "; note that coinbase outputs will not be selected if you specify " + "ANY_TADDR, any transparent recipients are included, or if the " + "`privacyPolicy` parameter is not set to `AllowRevealedSenders` or weaker") + + (!isFromUa || strategy.AllowLinkingAccountAddresses() ? "." : + ". (This transaction may require selecting transparent coins that were sent " + "to multiple Unified Addresses, which is not enabled by default because " + "it would create a public link between the transparent receivers of these " + "addresses. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` " + "parameter set to `AllowLinkingAccountAddresses` or weaker if you wish to " + "allow this transaction to proceed anyway.)")); + }, + [](const ChangeNotAllowedError& err) { + throw JSONRPCError( + RPC_WALLET_ERROR, + strprintf( + "When shielding coinbase funds, the wallet does not allow any change. " + "The proposed transaction would result in %s in change.", + FormatMoney(err.available - err.required))); + }, + [](const InvalidFeeError& err) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf( + "The provided fee, %s, is invalid. Fees must be non-negative, and no greater " + "than the total amount of ZEC that will ever be available.", + DisplayMoney(err.fixedFee), + DisplayMoney(MAX_MONEY))); + }, + [](const AbsurdFeeError& err) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf( + "Fee %s is greater than %d times the conventional fee for this tx (which is " + "%s). There is no prioritisation benefit to a fee this large (see " + "https://zips.z.cash/zip-0317#recommended-algorithm-for-block-template-construction), " + "and it likely indicates a mistake in setting the fee.", + FormatMoney(err.fixedFee), + WEIGHT_RATIO_CAP, + FormatMoney(err.conventionalFee))); + }, + [](const MaxFeeError& err) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf( + "Fee (%s) is greater than the maximum fee allowed by this instance (%s). Run " + "zcashd with `-maxtxfee` to adjust this limit.", + DisplayMoney(err.fixedFee), + DisplayMoney(maxTxFee))); + }, + [](const ExcessOrchardActionsError& err) { + std::string side; + switch (err.side) { + case ActionSide::Input: + side = "inputs"; + case ActionSide::Output: + side = "outputs"; + case ActionSide::Both: + side = "actions"; + }; + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf( + "Including %u Orchard %s would exceed the current limit " + "of %u notes, which exists to prevent memory exhaustion. Restart with " + "`-orchardactionlimit=N` where N >= %u to allow the wallet to attempt " + "to construct this transaction.", + err.orchardNotes, + side, + err.maxNotes, + err.orchardNotes)); + } + }); +} diff --git a/depend/zcash/src/wallet/asyncrpcoperation_common.h b/depend/zcash/src/wallet/asyncrpcoperation_common.h index 4105d5dc7..c3ac05179 100644 --- a/depend/zcash/src/wallet/asyncrpcoperation_common.h +++ b/depend/zcash/src/wallet/asyncrpcoperation_common.h @@ -11,6 +11,7 @@ #include "rpc/protocol.h" #include "univalue.h" #include "wallet.h" +#include "wallet/wallet_tx_builder.h" #include @@ -63,4 +64,9 @@ UniValue SendTransaction( */ std::pair SignSendRawTransaction(UniValue obj, std::optional> reservekey, bool testmode); +void ThrowInputSelectionError( + const InputSelectionError& err, + const ZTXOSelector& selector, + const TransactionStrategy& strategy); + #endif // ZCASH_WALLET_ASYNCRPCOPERATION_COMMON_H diff --git a/depend/zcash/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/depend/zcash/src/wallet/asyncrpcoperation_mergetoaddress.cpp index 666ee0f6d..0a1c19d80 100644 --- a/depend/zcash/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/depend/zcash/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -20,14 +20,13 @@ #include "rpc/server.h" #include "script/interpreter.h" #include "timedata.h" -#include "transaction_builder.h" +#include "uint256.h" #include "util/system.h" #include "util/match.h" #include "util/moneystr.h" #include "util/time.h" #include "wallet.h" #include "walletdb.h" -#include "wallet/paymentdisclosuredb.h" #include "zcash/IncrementalMerkleTree.hpp" #include @@ -40,85 +39,16 @@ using namespace libzcash; -int mta_find_output(UniValue obj, int n) -{ - UniValue outputMapValue = find_value(obj, "outputmap"); - if (!outputMapValue.isArray()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); - } - - UniValue outputMap = outputMapValue.get_array(); - assert(outputMap.size() == ZC_NUM_JS_OUTPUTS); - for (size_t i = 0; i < outputMap.size(); i++) { - if (outputMap[i].get_int() == n) { - return i; - } - } - - throw std::logic_error("n is not present in outputmap"); -} - AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress( - std::optional builder, - CMutableTransaction contextualTx, - std::vector utxoInputs, - std::vector sproutNoteInputs, - std::vector saplingNoteInputs, - MergeToAddressRecipient recipient, - CAmount fee, - UniValue contextInfo) : - tx_(contextualTx), utxoInputs_(utxoInputs), sproutNoteInputs_(sproutNoteInputs), - saplingNoteInputs_(saplingNoteInputs), memo_(recipient.second), fee_(fee), contextinfo_(contextInfo) + CWallet& wallet, + TransactionStrategy strategy, + TransactionEffects effects, + UniValue contextInfo) + : strategy_(strategy), effects_(effects), contextinfo_(contextInfo) { - if (fee < 0 || fee > MAX_MONEY) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range"); - } - - if (utxoInputs.empty() && sproutNoteInputs.empty() && saplingNoteInputs.empty()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs"); - } - - if (sproutNoteInputs.size() > 0 && saplingNoteInputs.size() > 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress"); - } - - if (sproutNoteInputs.size() > 0 && builder) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Sprout notes are not supported by the TransactionBuilder"); - } - - isUsingBuilder_ = false; - if (builder) { - isUsingBuilder_ = true; - builder_ = std::move(builder.value()); - } + effects.LockSpendable(*pwalletMain); KeyIO keyIO(Params()); - isToTaddr_ = false; - isToZaddr_ = false; - - std::visit(match { - [&](const CKeyID& keyId) { - toTaddr_ = keyId; - isToTaddr_ = true; - }, - [&](const CScriptID& scriptId) { - toTaddr_ = scriptId; - isToTaddr_ = true; - }, - [&](const libzcash::SproutPaymentAddress& addr) { - toPaymentAddress_ = addr; - isToZaddr_ = true; - }, - [&](const libzcash::SaplingPaymentAddress& addr) { - toPaymentAddress_ = addr; - isToZaddr_ = true; - }, - [&](const libzcash::UnifiedAddress& addr) { - throw JSONRPCError( - RPC_INVALID_ADDRESS_OR_KEY, - "z_mergetoaddress does not yet support sending to unified addresses"); - }, - }, recipient.first); // Log the context info i.e. the call parameters to z_mergetoaddress if (LogAcceptCategory("zrpcunsafe")) { @@ -126,38 +56,41 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress( } else { LogPrint("zrpc", "%s: z_mergetoaddress initialized\n", getId()); } - - // Lock UTXOs - lock_utxos(); - lock_notes(); - - // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalPaymentDisclosure; } AsyncRPCOperation_mergetoaddress::~AsyncRPCOperation_mergetoaddress() { } +std::pair +main_impl( + const CChainParams& chainparams, + CWallet& wallet, + const TransactionStrategy& strategy, + const TransactionEffects& effects, + const std::string& id, + bool testmode); + void AsyncRPCOperation_mergetoaddress::main() { if (isCancelled()) { - unlock_utxos(); // clean up - unlock_notes(); + effects_.UnlockSpendable(*pwalletMain); return; } set_state(OperationStatus::EXECUTING); start_execution_clock(); - bool success = false; - #ifdef ENABLE_MINING GenerateBitcoins(false, 0, Params()); #endif + std::optional txid; try { - success = main_impl(); + UniValue sendResult; + std::tie(txid, sendResult) = + main_impl(Params(), *pwalletMain, strategy_, effects_, getId(), testmode); + set_result(sendResult); } catch (const UniValue& objError) { int code = find_value(objError, "code").get_int(); std::string message = find_value(objError, "message").get_str(); @@ -183,781 +116,87 @@ void AsyncRPCOperation_mergetoaddress::main() stop_execution_clock(); - if (success) { + if (txid.has_value()) { set_state(OperationStatus::SUCCESS); } else { set_state(OperationStatus::FAILED); } std::string s = strprintf("%s: z_mergetoaddress finished (status=%s", getId(), getStateAsString()); - if (success) { - s += strprintf(", txid=%s)\n", tx_.GetHash().ToString()); + if (txid.has_value()) { + s += strprintf(", txid=%s)\n", txid.value().GetHex()); } else { s += strprintf(", error=%s)\n", getErrorMessage()); } LogPrintf("%s", s); - - unlock_utxos(); // clean up - unlock_notes(); // clean up - - // !!! Payment disclosure START - if (success && paymentDisclosureMode && paymentDisclosureData_.size() > 0) { - uint256 txidhash = tx_.GetHash(); - std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); - for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { - p.first.hash = txidhash; - if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); - } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); - } - } - } - // !!! Payment disclosure END } -// Notes: -// 1. #1359 Currently there is no limit set on the number of joinsplits, so size of tx could be invalid. -// 2. #1277 Spendable notes are not locked, so an operation running in parallel could also try to use them. -bool AsyncRPCOperation_mergetoaddress::main_impl() +std::pair +main_impl( + const CChainParams& chainparams, + CWallet& wallet, + const TransactionStrategy& strategy, + const TransactionEffects& effects, + const std::string& id, + bool testmode) { - assert(isToTaddr_ != isToZaddr_); - - bool isPureTaddrOnlyTx = (sproutNoteInputs_.empty() && saplingNoteInputs_.empty() && isToTaddr_); - CAmount minersFee = fee_; - - size_t numInputs = utxoInputs_.size(); - - CAmount t_inputs_total = 0; - for (MergeToAddressInputUTXO& t : utxoInputs_) { - t_inputs_total += std::get<1>(t); - } - - CAmount z_inputs_total = 0; - for (const MergeToAddressInputSproutNote& t : sproutNoteInputs_) { - z_inputs_total += std::get<2>(t); - } - - for (const MergeToAddressInputSaplingNote& t : saplingNoteInputs_) { - z_inputs_total += std::get<2>(t); - } - - CAmount targetAmount = z_inputs_total + t_inputs_total; - - if (targetAmount <= minersFee) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, - strprintf("Insufficient funds, have %s and miners fee is %s", - FormatMoney(targetAmount), FormatMoney(minersFee))); - } - - CAmount sendAmount = targetAmount - minersFee; - - // update the transaction with the UTXO inputs and output (if any) - if (!isUsingBuilder_) { - CMutableTransaction rawTx(tx_); - for (const MergeToAddressInputUTXO& t : utxoInputs_) { - CTxIn in(std::get<0>(t)); - rawTx.vin.push_back(in); - } - if (isToTaddr_) { - CScript scriptPubKey = GetScriptForDestination(toTaddr_); - CTxOut out(sendAmount, scriptPubKey); - rawTx.vout.push_back(out); - } - tx_ = CTransaction(rawTx); - } - - if (isPureTaddrOnlyTx) { - LogPrint("zrpc", "%s: spending %s to send %s with fee %s\n", - getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); - } else { - LogPrint("zrpcunsafe", "%s: spending %s to send %s with fee %s\n", - getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); - } - LogPrint("zrpc", "%s: transparent input: %s\n", getId(), FormatMoney(t_inputs_total)); - LogPrint("zrpcunsafe", "%s: private input: %s\n", getId(), FormatMoney(z_inputs_total)); - if (isToTaddr_) { - LogPrint("zrpc", "%s: transparent output: %s\n", getId(), FormatMoney(sendAmount)); - } else { - LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(sendAmount)); - } - LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); - - // Grab the current consensus branch ID - { - LOCK(cs_main); - consensusBranchId_ = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - } - - /** - * SCENARIO #0 - * - * Sprout not involved, so we just use the TransactionBuilder and we're done. - * - * This is based on code from AsyncRPCOperation_sendmany::main_impl() and should be refactored. - */ - if (isUsingBuilder_) { - builder_.SetFee(minersFee); - - - for (const MergeToAddressInputUTXO& t : utxoInputs_) { - COutPoint outPoint = std::get<0>(t); - CAmount amount = std::get<1>(t); - CScript scriptPubKey = std::get<2>(t); - builder_.AddTransparentInput(outPoint, scriptPubKey, amount); - } - - std::optional ovk; - // Select Sapling notes - std::vector saplingOPs; - std::vector saplingNotes; - std::vector expsks; - for (const MergeToAddressInputSaplingNote& saplingNoteInput: saplingNoteInputs_) { - saplingOPs.push_back(std::get<0>(saplingNoteInput)); - saplingNotes.push_back(std::get<1>(saplingNoteInput)); - auto expsk = std::get<3>(saplingNoteInput); - expsks.push_back(expsk); - if (!ovk) { - ovk = expsk.full_viewing_key().ovk; - } - } - - // Fetch Sapling anchor and witnesses - uint256 anchor; - std::vector> witnesses; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - if (!pwalletMain->GetSaplingNoteWitnesses(saplingOPs, nAnchorConfirmations, witnesses, anchor)) { - // This error should not appear once we're nAnchorConfirmations blocks past - // Sapling activation. - throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sapling witnesses."); - }; - } - - // Add Sapling spends - for (size_t i = 0; i < saplingNotes.size(); i++) { - if (!witnesses[i]) { - throw JSONRPCError(RPC_WALLET_ERROR, "Missing witness for Sapling note"); - } - builder_.AddSaplingSpend(expsks[i], saplingNotes[i], anchor, witnesses[i].value()); - } - - if (isToTaddr_) { - builder_.AddTransparentOutput(toTaddr_, sendAmount); - } else { - std::array hexMemo = get_memo_from_hex_string(memo_); - auto saplingPaymentAddress = std::get_if(&toPaymentAddress_); - if (saplingPaymentAddress == nullptr) { - // This should never happen as we have already determined that the payment is to sapling - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Could not get Sapling payment address."); - } - if (saplingNoteInputs_.size() == 0 && utxoInputs_.size() > 0) { - // Sending from t-addresses, which we don't have ovks for. Instead, - // generate a common one from the HD seed. This ensures the data is - // recoverable, while keeping it logically separate from the ZIP 32 - // Sapling key hierarchy, which the user might not be using. - HDSeed seed = pwalletMain->GetHDSeedForRPC(); - ovk = ovkForShieldingFromTaddr(seed); - } - if (!ovk) { - throw JSONRPCError(RPC_WALLET_ERROR, "Sending to a Sapling address requires an ovk."); - } - builder_.AddSaplingOutput(ovk.value(), *saplingPaymentAddress, sendAmount, hexMemo); - } - - // Build the transaction - tx_ = builder_.Build().GetTxOrThrow(); - - // TODO: update this when unified address support is added - std::vector recipientMappings; - UniValue sendResult = SendTransaction(tx_, recipientMappings, std::nullopt, testmode); - set_result(sendResult); - - return true; - } - /** - * END SCENARIO #0 - */ - - - /** - * SCENARIO #1 - * - * taddrs -> taddr - * - * There are no zaddrs or joinsplits involved. - */ - if (isPureTaddrOnlyTx) { - UniValue obj(UniValue::VOBJ); - obj.pushKV("rawtxn", EncodeHexTx(tx_)); - auto txAndResult = SignSendRawTransaction(obj, std::nullopt, testmode); - tx_ = txAndResult.first; - set_result(txAndResult.second); - return true; - } - /** - * END SCENARIO #1 - */ - - // Prepare raw transaction to handle JoinSplits - CMutableTransaction mtx(tx_); - ed25519_generate_keypair(&joinSplitPrivKey_, &joinSplitPubKey_); - mtx.joinSplitPubKey = joinSplitPubKey_; - tx_ = CTransaction(mtx); - - /** - * SCENARIO #2 - * - * taddrs -> zaddr - * - * We only need a single JoinSplit. - */ - if (sproutNoteInputs_.empty() && isToZaddr_) { - // Create JoinSplit to target z-addr. - MergeToAddressJSInfo info; - info.vpub_old = sendAmount; - info.vpub_new = 0; - - JSOutput jso = JSOutput(std::get(toPaymentAddress_), sendAmount); - if (memo_.size() > 0) { - jso.memo = get_memo_from_hex_string(memo_); - } - info.vjsout.push_back(jso); - - UniValue obj = perform_joinsplit(info); - auto txAndResult = SignSendRawTransaction(obj, std::nullopt, testmode); - tx_ = txAndResult.first; - set_result(txAndResult.second); - return true; - } - /** - * END SCENARIO #2 - */ - - - // Copy zinputs to more flexible containers - std::deque zInputsDeque; - for (const auto& o : sproutNoteInputs_) { - zInputsDeque.push_back(o); - } - - // When spending notes, take a snapshot of note witnesses and anchors as the treestate will - // change upon arrival of new blocks which contain joinsplit transactions. This is likely - // to happen as creating a chained joinsplit transaction can take longer than the block interval. - { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto t : sproutNoteInputs_) { - JSOutPoint jso = std::get<0>(t); - std::vector vOutPoints = {jso}; - uint256 inputAnchor; - std::vector> vInputWitnesses; - if (!pwalletMain->GetSproutNoteWitnesses(vOutPoints, nAnchorConfirmations, vInputWitnesses, inputAnchor)) { - // This error should not appear once we're nAnchorConfirmations blocks past - // Sprout activation. - throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sprout witnesses."); - }; - jsopWitnessAnchorMap[jso.ToString()] = MergeToAddressWitnessAnchorData{vInputWitnesses[0], inputAnchor}; - } - } - - /** - * SCENARIO #3 - * - * zaddrs -> zaddr - * taddrs -> - * - * zaddrs -> - * taddrs -> taddr - * - * Send to zaddr by chaining JoinSplits together and immediately consuming any change - * Send to taddr by creating dummy z outputs and accumulating value in a change note - * which is used to set vpub_new in the last chained joinsplit. - */ - UniValue obj(UniValue::VOBJ); - CAmount jsChange = 0; // this is updated after each joinsplit - int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 - bool vpubOldProcessed = false; // updated when vpub_old for taddr inputs is set in first joinsplit - bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit - - // At this point, we are guaranteed to have at least one input note. - // Use address of first input note as the temporary change address. - SproutSpendingKey changeKey = std::get<3>(zInputsDeque.front()); - SproutPaymentAddress changeAddress = changeKey.address(); - - CAmount vpubOldTarget = 0; - CAmount vpubNewTarget = 0; - if (isToTaddr_) { - vpubNewTarget = z_inputs_total; - } else { - if (utxoInputs_.empty()) { - vpubNewTarget = minersFee; + try { + const auto& spendable = effects.GetSpendable(); + const auto& payments = effects.GetPayments(); + spendable.LogInputs(id); + + bool sendsToShielded = false; + for (const auto& resolvedPayment : payments.GetResolvedPayments()) { + sendsToShielded = sendsToShielded || examine(resolvedPayment.address, match { + [](const CKeyID&) { return false; }, + [](const CScriptID&) { return false; }, + [](const libzcash::SaplingPaymentAddress&) { return true; }, + [](const libzcash::OrchardRawAddress&) { return true; }, + }); + } + if (spendable.sproutNoteEntries.empty() + && spendable.saplingNoteEntries.empty() + && spendable.orchardNoteMetadata.empty() + && !sendsToShielded) { + LogPrint("zrpc", "%s: spending %s to send %s with fee %s\n", + id, + FormatMoney(payments.Total()), + FormatMoney(spendable.Total()), + FormatMoney(effects.GetFee())); } else { - vpubOldTarget = t_inputs_total - minersFee; - } - } - - // Keep track of treestate within this transaction - // The SaltedTxidHasher is fine to use here; it salts the map keys automatically - // with randomness generated on construction. - boost::unordered_map intermediates; - std::vector previousCommitments; - - while (!vpubNewProcessed) { - MergeToAddressJSInfo info; - info.vpub_old = 0; - info.vpub_new = 0; - - // Set vpub_old in the first joinsplit - if (!vpubOldProcessed) { - if (t_inputs_total < vpubOldTarget) { - throw JSONRPCError(RPC_WALLET_ERROR, - strprintf("Insufficient transparent funds for vpub_old %s (miners fee %s, taddr inputs %s)", - FormatMoney(vpubOldTarget), FormatMoney(minersFee), FormatMoney(t_inputs_total))); - } - info.vpub_old += vpubOldTarget; // funds flowing from public pool - vpubOldProcessed = true; - } - - CAmount jsInputValue = 0; - uint256 jsAnchor; - std::vector> witnesses; - - JSDescription prevJoinSplit; - - // Keep track of previous JoinSplit and its commitments - if (tx_.vJoinSplit.size() > 0) { - prevJoinSplit = tx_.vJoinSplit.back(); - } - - // If there is no change, the chain has terminated so we can reset the tracked treestate. - if (jsChange == 0 && tx_.vJoinSplit.size() > 0) { - intermediates.clear(); - previousCommitments.clear(); - } - - // - // Consume change as the first input of the JoinSplit. - // - if (jsChange > 0) { - LOCK2(cs_main, pwalletMain->cs_wallet); - - // Update tree state with previous joinsplit - SproutMerkleTree tree; - auto it = intermediates.find(prevJoinSplit.anchor); - if (it != intermediates.end()) { - tree = it->second; - } else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); - } - - assert(changeOutputIndex != -1); - std::optional changeWitness; - int n = 0; - for (const uint256& commitment : prevJoinSplit.commitments) { - tree.append(commitment); - previousCommitments.push_back(commitment); - if (!changeWitness && changeOutputIndex == n++) { - changeWitness = tree.witness(); - } else if (changeWitness) { - changeWitness.value().append(commitment); - } - } - if (changeWitness) { - witnesses.push_back(changeWitness); - } - jsAnchor = tree.root(); - intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) - - // Decrypt the change note's ciphertext to retrieve some data we need - ZCNoteDecryption decryptor(changeKey.receiving_key()); - auto hSig = ZCJoinSplit::h_sig( - prevJoinSplit.randomSeed, - prevJoinSplit.nullifiers, - tx_.joinSplitPubKey); - try { - SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( - decryptor, - prevJoinSplit.ciphertexts[changeOutputIndex], - prevJoinSplit.ephemeralKey, - hSig, - (unsigned char)changeOutputIndex); - - SproutNote note = plaintext.note(changeAddress); - info.notes.push_back(note); - info.zkeys.push_back(changeKey); - - jsInputValue += plaintext.value(); - - LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", - getId(), - FormatMoney(plaintext.value())); - - } catch (const std::exception& e) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); - } - } - - - // - // Consume spendable non-change notes - // - std::vector vInputNotes; - std::vector vInputZKeys; - std::vector vOutPoints; - std::vector> vInputWitnesses; - uint256 inputAnchor; - int numInputsNeeded = (jsChange > 0) ? 1 : 0; - while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) { - MergeToAddressInputSproutNote t = zInputsDeque.front(); - JSOutPoint jso = std::get<0>(t); - SproutNote note = std::get<1>(t); - CAmount noteFunds = std::get<2>(t); - SproutSpendingKey zkey = std::get<3>(t); - zInputsDeque.pop_front(); - - MergeToAddressWitnessAnchorData wad = jsopWitnessAnchorMap[jso.ToString()]; - vInputWitnesses.push_back(wad.witness); - if (inputAnchor.IsNull()) { - inputAnchor = wad.anchor; - } else if (inputAnchor != wad.anchor) { - throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); - } - - vOutPoints.push_back(jso); - vInputNotes.push_back(note); - vInputZKeys.push_back(zkey); - - jsInputValue += noteFunds; - - int wtxHeight = -1; - int wtxDepth = -1; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash]; - // Zero confirmation notes belong to transactions which have not yet been mined - if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) { - throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString())); - } - wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; - wtxDepth = wtx.GetDepthInMainChain(std::nullopt); - } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, height=%d, confirmations=%d)\n", - getId(), - jso.hash.ToString().substr(0, 10), - jso.js, - int(jso.n), // uint8_t - FormatMoney(noteFunds), - wtxHeight, - wtxDepth); - } - - // Add history of previous commitments to witness - if (vInputNotes.size() > 0) { - if (vInputWitnesses.size() == 0) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); - } - - for (auto& optionalWitness : vInputWitnesses) { - if (!optionalWitness) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); - } - SproutWitness w = *optionalWitness; // could use .value(); - if (jsChange > 0) { - for (const uint256& commitment : previousCommitments) { - w.append(commitment); - } - if (jsAnchor != w.root()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Witness for spendable note does not have same anchor as change input"); - } - } - witnesses.push_back(w); - } - - // The jsAnchor is null if this JoinSplit is at the start of a new chain - if (jsAnchor.IsNull()) { - jsAnchor = inputAnchor; - } - - // Add spendable notes as inputs - std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); - std::copy(vInputZKeys.begin(), vInputZKeys.end(), std::back_inserter(info.zkeys)); - } - - // Accumulate change - jsChange = jsInputValue + info.vpub_old; - - // Set vpub_new in the last joinsplit (when there are no more notes to spend) - if (zInputsDeque.empty()) { - assert(!vpubNewProcessed); - if (jsInputValue < vpubNewTarget) { - throw JSONRPCError(RPC_WALLET_ERROR, - strprintf("Insufficient funds for vpub_new %s (miners fee %s, taddr inputs %s)", - FormatMoney(vpubNewTarget), FormatMoney(minersFee), FormatMoney(t_inputs_total))); - } - info.vpub_new += vpubNewTarget; // funds flowing back to public pool - vpubNewProcessed = true; - jsChange -= vpubNewTarget; - // If we are merging to a t-addr, there should be no change - if (isToTaddr_) assert(jsChange == 0); - } - - // create dummy output - info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new - - // create output for any change - if (jsChange > 0) { - std::string outputType = "change"; - auto jso = JSOutput(changeAddress, jsChange); - // If this is the final output, set the target and memo - if (isToZaddr_ && vpubNewProcessed) { - outputType = "target"; - jso.addr = std::get(toPaymentAddress_); - if (!memo_.empty()) { - jso.memo = get_memo_from_hex_string(memo_); - } - } - info.vjsout.push_back(jso); - - LogPrint("zrpcunsafe", "%s: generating note for %s (amount=%s)\n", - getId(), - outputType, - FormatMoney(jsChange)); - } - - obj = perform_joinsplit(info, witnesses, jsAnchor); - - if (jsChange > 0) { - changeOutputIndex = mta_find_output(obj, 1); - } - } - - // Sanity check in case changes to code block above exits loop by invoking 'break' - assert(zInputsDeque.size() == 0); - assert(vpubNewProcessed); - - auto txAndResult = SignSendRawTransaction(obj, std::nullopt, testmode); - tx_ = txAndResult.first; - set_result(txAndResult.second); - return true; -} - - -UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInfo& info) -{ - std::vector> witnesses; - uint256 anchor; - { - LOCK(cs_main); - anchor = pcoinsTip->GetBestAnchor(SPROUT); // As there are no inputs, ask the wallet for the best anchor - } - return perform_joinsplit(info, witnesses, anchor); -} - - -UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInfo& info, std::vector& outPoints) -{ - std::vector> witnesses; - uint256 anchor; - { - LOCK(cs_main); - if (!pwalletMain->GetSproutNoteWitnesses(outPoints, nAnchorConfirmations, witnesses, anchor)) { - // This error should not appear once we're nAnchorConfirmations blocks past - // Sprout activation. - throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sprout witnesses."); - } - } - return perform_joinsplit(info, witnesses, anchor); -} - -UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( - MergeToAddressJSInfo& info, - std::vector> witnesses, - uint256 anchor) -{ - if (anchor.IsNull()) { - throw std::runtime_error("anchor is null"); - } - - if (witnesses.size() != info.notes.size()) { - throw runtime_error("number of notes and witnesses do not match"); - } - - if (info.notes.size() != info.zkeys.size()) { - throw runtime_error("number of notes and spending keys do not match"); - } - - for (size_t i = 0; i < witnesses.size(); i++) { - if (!witnesses[i]) { - throw runtime_error("joinsplit input could not be found in tree"); - } - info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], info.zkeys[i])); - } - - // Make sure there are two inputs and two outputs - while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { - info.vjsin.push_back(JSInput()); - } - - while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { - info.vjsout.push_back(JSOutput()); - } - - if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { - throw runtime_error("unsupported joinsplit input/output counts"); - } - - CMutableTransaction mtx(tx_); - - LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", - getId(), - tx_.vJoinSplit.size(), - FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), - FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()), - FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value)); - - // Generate the proof, this can take over a minute. - std::array inputs{info.vjsin[0], info.vjsin[1]}; - std::array outputs{info.vjsout[0], info.vjsout[1]}; - std::array inputMap; - std::array outputMap; - - uint256 esk; // payment disclosure - secret - - assert(mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)); - JSDescription jsdesc = JSDescriptionInfo( - joinSplitPubKey_, - anchor, - inputs, - outputs, - info.vpub_old, - info.vpub_new - ).BuildRandomized( - inputMap, - outputMap, - !this->testmode, - &esk); // parameter expects pointer to esk, so pass in address - { - auto verifier = ProofVerifier::Strict(); - if (!(verifier.VerifySprout(jsdesc, joinSplitPubKey_))) { - throw std::runtime_error("error verifying joinsplit"); - } - } - - mtx.vJoinSplit.push_back(jsdesc); - - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - std::vector allPrevOutputs; - for (const MergeToAddressInputUTXO& t : utxoInputs_) { - allPrevOutputs.emplace_back(std::get<1>(t), std::get<2>(t)); - } - PrecomputedTransactionData txdata(signTx, allPrevOutputs); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_, txdata); - - // Add the signature - if (!ed25519_sign( - &joinSplitPrivKey_, - dataToBeSigned.begin(), 32, - &mtx.joinSplitSig)) - { - throw std::runtime_error("ed25519_sign failed"); - } - - // Sanity check - if (!ed25519_verify( - &mtx.joinSplitPubKey, - &mtx.joinSplitSig, - dataToBeSigned.begin(), 32)) - { - throw std::runtime_error("ed25519_verify failed"); - } - - CTransaction rawTx(mtx); - tx_ = rawTx; - - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << rawTx; - - std::string encryptedNote1; - std::string encryptedNote2; - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char)0x00); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[0]; - ss2 << ZCJoinSplit::h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey_); - - encryptedNote1 = HexStr(ss2.begin(), ss2.end()); - } - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char)0x01); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[1]; - ss2 << ZCJoinSplit::h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey_); - - encryptedNote2 = HexStr(ss2.begin(), ss2.end()); - } - - UniValue arrInputMap(UniValue::VARR); - UniValue arrOutputMap(UniValue::VARR); - for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { - arrInputMap.push_back(static_cast(inputMap[i])); - } - for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - arrOutputMap.push_back(static_cast(outputMap[i])); - } - - KeyIO keyIO(Params()); - - // !!! Payment disclosure START - size_t js_index = tx_.vJoinSplit.size() - 1; - uint256 placeholder; - for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - uint8_t mapped_index = outputMap[i]; - // placeholder for txid will be filled in later when tx has been finalized and signed. - PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index}; - JSOutput output = outputs[mapped_index]; - libzcash::SproutPaymentAddress zaddr = output.addr; // randomized output - PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey_, zaddr}; - paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - - LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), keyIO.EncodePaymentAddress(zaddr)); - } - // !!! Payment disclosure END - - UniValue obj(UniValue::VOBJ); - obj.pushKV("encryptednote1", encryptedNote1); - obj.pushKV("encryptednote2", encryptedNote2); - obj.pushKV("rawtxn", HexStr(ss.begin(), ss.end())); - obj.pushKV("inputmap", arrInputMap); - obj.pushKV("outputmap", arrOutputMap); - return obj; -} - -std::array AsyncRPCOperation_mergetoaddress::get_memo_from_hex_string(std::string s) -{ - std::array memo = {{0x00}}; - - std::vector rawMemo = ParseHex(s.c_str()); - - // If ParseHex comes across a non-hex char, it will stop but still return results so far. - size_t slen = s.length(); - if (slen % 2 != 0 || (slen > 0 && rawMemo.size() != slen / 2)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo must be in hexadecimal format"); - } - - if (rawMemo.size() > ZC_MEMO_SIZE) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Memo size of %d is too big, maximum allowed is %d", rawMemo.size(), ZC_MEMO_SIZE)); - } - - // copy vector into boost array - int lenMemo = rawMemo.size(); - for (int i = 0; i < ZC_MEMO_SIZE && i < lenMemo; i++) { - memo[i] = rawMemo[i]; + LogPrint("zrpcunsafe", "%s: spending %s to send %s with fee %s\n", + id, + FormatMoney(payments.Total()), + FormatMoney(spendable.Total()), + FormatMoney(effects.GetFee())); + } + LogPrint("zrpc", "%s: total transparent input: %s\n", id, + FormatMoney(spendable.GetTransparentTotal())); + LogPrint("zrpcunsafe", "%s: total shielded input: %s\n", id, + FormatMoney(spendable.GetSaplingTotal() + spendable.GetOrchardTotal())); + LogPrint("zrpc", "%s: total transparent output: %s\n", id, + FormatMoney(payments.GetTransparentTotal())); + LogPrint("zrpcunsafe", "%s: total shielded Sapling output: %s\n", id, + FormatMoney(payments.GetSaplingTotal())); + LogPrint("zrpcunsafe", "%s: total shielded Orchard output: %s\n", id, + FormatMoney(payments.GetOrchardTotal())); + LogPrint("zrpc", "%s: fee: %s\n", id, FormatMoney(effects.GetFee())); + + auto buildResult = effects.ApproveAndBuild( + chainparams.GetConsensus(), + wallet, + chainActive, + strategy); + auto tx = buildResult.GetTxOrThrow(); + + UniValue sendResult = SendTransaction(tx, payments.GetResolvedPayments(), std::nullopt, testmode); + + effects.UnlockSpendable(wallet); + return {tx.GetHash(), sendResult}; + } catch (...) { + effects.UnlockSpendable(wallet); + throw; } - return memo; } /** @@ -975,50 +214,3 @@ UniValue AsyncRPCOperation_mergetoaddress::getStatus() const obj.pushKV("params", contextinfo_); return obj; } - -/** - * Lock input utxos - */ - void AsyncRPCOperation_mergetoaddress::lock_utxos() { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto utxo : utxoInputs_) { - pwalletMain->LockCoin(std::get<0>(utxo)); - } -} - -/** - * Unlock input utxos - */ -void AsyncRPCOperation_mergetoaddress::unlock_utxos() { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto utxo : utxoInputs_) { - pwalletMain->UnlockCoin(std::get<0>(utxo)); - } -} - - -/** - * Lock input notes - */ - void AsyncRPCOperation_mergetoaddress::lock_notes() { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto note : sproutNoteInputs_) { - pwalletMain->LockNote(std::get<0>(note)); - } - for (auto note : saplingNoteInputs_) { - pwalletMain->LockNote(std::get<0>(note)); - } -} - -/** - * Unlock input notes - */ -void AsyncRPCOperation_mergetoaddress::unlock_notes() { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto note : sproutNoteInputs_) { - pwalletMain->UnlockNote(std::get<0>(note)); - } - for (auto note : saplingNoteInputs_) { - pwalletMain->UnlockNote(std::get<0>(note)); - } -} diff --git a/depend/zcash/src/wallet/asyncrpcoperation_mergetoaddress.h b/depend/zcash/src/wallet/asyncrpcoperation_mergetoaddress.h index 042197dd7..7398c905d 100644 --- a/depend/zcash/src/wallet/asyncrpcoperation_mergetoaddress.h +++ b/depend/zcash/src/wallet/asyncrpcoperation_mergetoaddress.h @@ -9,8 +9,10 @@ #include "asyncrpcoperation.h" #include "primitives/transaction.h" #include "transaction_builder.h" +#include "uint256.h" #include "wallet.h" #include "wallet/paymentdisclosure.h" +#include "wallet/wallet_tx_builder.h" #include "zcash/Address.hpp" #include "zcash/JoinSplit.hpp" @@ -21,21 +23,10 @@ #include -#include +#include using namespace libzcash; -// Input UTXO is a tuple of txid, vout, amount, script -typedef std::tuple MergeToAddressInputUTXO; - -// Input JSOP is a tuple of JSOutpoint, note, amount, spending key -typedef std::tuple MergeToAddressInputSproutNote; - -typedef std::tuple MergeToAddressInputSaplingNote; - -// A recipient is a tuple of address, memo (optional if zaddr) -typedef std::pair MergeToAddressRecipient; - // Package of info which is passed to perform_joinsplit methods. struct MergeToAddressJSInfo { std::vector vjsin; @@ -56,14 +47,10 @@ class AsyncRPCOperation_mergetoaddress : public AsyncRPCOperation { public: AsyncRPCOperation_mergetoaddress( - std::optional builder, - CMutableTransaction contextualTx, - std::vector utxoInputs, - std::vector sproutNoteInputs, - std::vector saplingNoteInputs, - MergeToAddressRecipient recipient, - CAmount fee = DEFAULT_FEE, - UniValue contextInfo = NullUniValue); + CWallet& wallet, + TransactionStrategy strategy, + TransactionEffects effects, + UniValue contextInfo = NullUniValue); virtual ~AsyncRPCOperation_mergetoaddress(); // We don't want to be copied or moved around @@ -76,119 +63,16 @@ class AsyncRPCOperation_mergetoaddress : public AsyncRPCOperation virtual UniValue getStatus() const; - bool testmode = false; // Set to true to disable sending txs and generating proofs - - bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database. + /// Set to true to disable sending txs and generating proofs + bool testmode = false; private: - friend class TEST_FRIEND_AsyncRPCOperation_mergetoaddress; // class for unit testing - - UniValue contextinfo_; // optional data to include in return value from getStatus() - - bool isUsingBuilder_; // Indicates that no Sprout addresses are involved - uint32_t consensusBranchId_; - CAmount fee_; - int mindepth_; - bool isToTaddr_; - bool isToZaddr_; - CTxDestination toTaddr_; - PaymentAddress toPaymentAddress_; - std::string memo_; - - Ed25519VerificationKey joinSplitPubKey_; - Ed25519SigningKey joinSplitPrivKey_; - - // The key is the result string from calling JSOutPoint::ToString() - std::unordered_map jsopWitnessAnchorMap; - - std::vector utxoInputs_; - std::vector sproutNoteInputs_; - std::vector saplingNoteInputs_; - - TransactionBuilder builder_; - CTransaction tx_; - - std::array get_memo_from_hex_string(std::string s); - bool main_impl(); + const TransactionStrategy strategy_; - // JoinSplit without any input notes to spend - UniValue perform_joinsplit(MergeToAddressJSInfo&); + const TransactionEffects effects_; - // JoinSplit with input notes to spend (JSOutPoints)) - UniValue perform_joinsplit(MergeToAddressJSInfo&, std::vector&); - - // JoinSplit where you have the witnesses and anchor - UniValue perform_joinsplit( - MergeToAddressJSInfo& info, - std::vector> witnesses, - uint256 anchor); - - void lock_utxos(); - - void unlock_utxos(); - - void lock_notes(); - - void unlock_notes(); - - // payment disclosure! - std::vector paymentDisclosureData_; + /// optional data to include in return value from getStatus() + UniValue contextinfo_; }; - -// To test private methods, a friend class can act as a proxy -class TEST_FRIEND_AsyncRPCOperation_mergetoaddress -{ -public: - std::shared_ptr delegate; - - TEST_FRIEND_AsyncRPCOperation_mergetoaddress(std::shared_ptr ptr) : delegate(ptr) {} - - CTransaction getTx() - { - return delegate->tx_; - } - - void setTx(CTransaction tx) - { - delegate->tx_ = tx; - } - - // Delegated methods - - std::array get_memo_from_hex_string(std::string s) - { - return delegate->get_memo_from_hex_string(s); - } - - bool main_impl() - { - return delegate->main_impl(); - } - - UniValue perform_joinsplit(MergeToAddressJSInfo& info) - { - return delegate->perform_joinsplit(info); - } - - UniValue perform_joinsplit(MergeToAddressJSInfo& info, std::vector& v) - { - return delegate->perform_joinsplit(info, v); - } - - UniValue perform_joinsplit( - MergeToAddressJSInfo& info, - std::vector> witnesses, - uint256 anchor) - { - return delegate->perform_joinsplit(info, witnesses, anchor); - } - - void set_state(OperationStatus state) - { - delegate->state_.store(state); - } -}; - - #endif // ZCASH_WALLET_ASYNCRPCOPERATION_MERGETOADDRESS_H diff --git a/depend/zcash/src/wallet/asyncrpcoperation_sendmany.cpp b/depend/zcash/src/wallet/asyncrpcoperation_sendmany.cpp index 735cb2f6d..ac808659a 100644 --- a/depend/zcash/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/depend/zcash/src/wallet/asyncrpcoperation_sendmany.cpp @@ -18,7 +18,6 @@ #include "proof_verifier.h" #include "rpc/protocol.h" #include "rpc/server.h" -#include "transaction_builder.h" #include "timedata.h" #include "util/system.h" #include "util/match.h" @@ -29,7 +28,6 @@ #include "util/time.h" #include "zcash/IncrementalMerkleTree.hpp" #include "miner.h" -#include "wallet/paymentdisclosuredb.h" #include "wallet/wallet_tx_builder.h" #include @@ -40,54 +38,26 @@ #include #include -#include - using namespace libzcash; AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( - TransactionBuilder builder, + WalletTxBuilder builder, ZTXOSelector ztxoSelector, - std::vector recipients, + std::vector recipients, int minDepth, unsigned int anchorDepth, TransactionStrategy strategy, - CAmount fee, + std::optional fee, UniValue contextInfo) : builder_(std::move(builder)), ztxoSelector_(ztxoSelector), recipients_(recipients), mindepth_(minDepth), anchordepth_(anchorDepth), strategy_(strategy), fee_(fee), contextinfo_(contextInfo) { - assert(fee_ >= 0); + assert(!fee_.has_value() || fee_.value() >= 0); assert(mindepth_ >= 0); assert(!recipients_.empty()); assert(ztxoSelector.RequireSpendingKeys()); - sendFromAccount_ = pwalletMain->FindAccountForSelector(ztxoSelector_).value_or(ZCASH_LEGACY_ACCOUNT); - - // Determine the target totals and recipient pools - for (const ResolvedPayment& recipient : recipients_) { - std::visit(match { - [&](const CKeyID& addr) { - txOutputAmounts_.t_outputs_total += recipient.amount; - recipientPools_.insert(OutputPool::Transparent); - }, - [&](const CScriptID& addr) { - txOutputAmounts_.t_outputs_total += recipient.amount; - recipientPools_.insert(OutputPool::Transparent); - }, - [&](const libzcash::SaplingPaymentAddress& addr) { - txOutputAmounts_.sapling_outputs_total += recipient.amount; - recipientPools_.insert(OutputPool::Sapling); - }, - [&](const libzcash::OrchardRawAddress& addr) { - txOutputAmounts_.orchard_outputs_total += recipient.amount; - recipientPools_.insert(OutputPool::Orchard); - // No transaction allows sends from Sprout to Orchard. - assert(!ztxoSelector_.SelectsSprout()); - } - }, recipient.address); - } - // Log the context info i.e. the call parameters to z_sendmany if (LogAcceptCategory("zrpcunsafe")) { LogPrint("zrpcunsafe", "%s: z_sendmany initialized (params=%s)\n", getId(), contextInfo.write()); @@ -106,15 +76,17 @@ void AsyncRPCOperation_sendmany::main() { set_state(OperationStatus::EXECUTING); start_execution_clock(); - bool success = false; - #ifdef ENABLE_MINING GenerateBitcoins(false, 0, Params()); #endif std::optional txid; try { - txid = main_impl(); + txid = main_impl(*pwalletMain) + .map_error([&](const InputSelectionError& err) { + ThrowInputSelectionError(err, ztxoSelector_, strategy_); + }) + .value(); } catch (const UniValue& objError) { int code = find_value(objError, "code").get_int(); std::string message = find_value(objError, "message").get_str(); @@ -164,606 +136,64 @@ void AsyncRPCOperation_sendmany::main() { // 2. #1360 Note selection is not optimal. // 3. #1277 Spendable notes are not locked, so an operation running in parallel // could also try to use them. -// 4. #1614 Anchors are chosen at the most recent block; this is unreliable and leaks -// information in case of rollback. -// 5. #3615 There is no padding of inputs or outputs, which may leak information. +// 4. #3615 There is no padding of inputs or outputs, which may leak information. // -// At least 4. and 5. differ from the Rust transaction builder. -uint256 AsyncRPCOperation_sendmany::main_impl() { - CAmount sendAmount = ( - txOutputAmounts_.orchard_outputs_total + - txOutputAmounts_.sapling_outputs_total + - txOutputAmounts_.t_outputs_total); - CAmount targetAmount = sendAmount + fee_; - - builder_.SetFee(fee_); - - // Allow transparent coinbase inputs if there are no transparent - // recipients. - bool allowTransparentCoinbase = !recipientPools_.count(OutputPool::Transparent); - - // Set the dust threshold so that we can select enough inputs to avoid - // creating dust change amounts. - CAmount dustThreshold{DefaultDustThreshold()}; - - // Find spendable inputs, and select a minimal set of them that - // can supply the required target amount. - SpendableInputs spendable; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - spendable = pwalletMain->FindSpendableInputs(ztxoSelector_, allowTransparentCoinbase, mindepth_, std::nullopt); - } - if (!spendable.LimitToAmount(targetAmount, dustThreshold, recipientPools_)) { - CAmount changeAmount{spendable.Total() - targetAmount}; - std::string insufficientFundsMessage = - strprintf("Insufficient funds: have %s", FormatMoney(spendable.Total())); - if (changeAmount > 0 && changeAmount < dustThreshold) { - // TODO: we should provide the option for the caller to explicitly - // forego change (definitionally an amount below the dust amount) - // and send the extra to the recipient or the miner fee to avoid - // creating dust change, rather than prohibit them from sending - // entirely in this circumstance. - // (Daira disagrees, as this could leak information to the recipient - // or to an external viewing key holder.) - insufficientFundsMessage += - strprintf( - ", need %s more to avoid creating invalid change output %s (dust threshold is %s)", - FormatMoney(dustThreshold - changeAmount), - FormatMoney(changeAmount), - FormatMoney(dustThreshold)); - } else { - insufficientFundsMessage += strprintf(", need %s", FormatMoney(targetAmount)); - } - bool isFromUa = std::holds_alternative(ztxoSelector_.GetPattern()); - throw JSONRPCError( - RPC_WALLET_INSUFFICIENT_FUNDS, - insufficientFundsMessage - + (allowTransparentCoinbase && ztxoSelector_.SelectsTransparentCoinbase() ? "." : - "; note that coinbase outputs will not be selected if you specify " - "ANY_TADDR or if any transparent recipients are included.") - + ((!isFromUa || strategy_.AllowLinkingAccountAddresses()) ? "" : - " (This transaction may require selecting transparent coins that were sent " - "to multiple Unified Addresses, which is not enabled by default because " - "it would create a public link between the transparent receivers of these " - "addresses. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` " - "parameter set to `AllowLinkingAccountAddresses` or weaker if you wish to " - "allow this transaction to proceed anyway.)")); - } - - if (!(spendable.utxos.empty() || strategy_.AllowRevealedSenders())) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "This transaction requires selecting transparent coins, which is " - "not enabled by default because it will publicly reveal transaction " - "senders and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit " - "with the `privacyPolicy` parameter set to `AllowRevealedSenders` " - "or weaker if you wish to allow this transaction to proceed anyway."); - } - - if (recipientPools_.count(OutputPool::Transparent) && !strategy_.AllowRevealedRecipients()) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "This transaction would have transparent recipients, which is not " - "enabled by default because it will publicly reveal transaction " - "recipients and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit " - "with the `privacyPolicy` parameter set to `AllowRevealedRecipients` " - "or weaker if you wish to allow this transaction to proceed anyway."); - } - - if (!spendable.sproutNoteEntries.empty()) { - if (recipientPools_.count(OutputPool::Sapling) && !strategy_.AllowRevealedAmounts()) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Sending from the Sprout shielded pool to the Sapling " - "shielded pool is not enabled by default because it will " - "publicly reveal the transaction amount. THIS MAY AFFECT YOUR PRIVACY. " - "Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` " - "or weaker if you wish to allow this transaction to proceed anyway."); - } - } - - if (!spendable.saplingNoteEntries.empty()) { - if (recipientPools_.count(OutputPool::Orchard) && !strategy_.AllowRevealedAmounts()) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Sending from the Sapling shielded pool to the Orchard " - "shielded pool is not enabled by default because it will " - "publicly reveal the transaction amount. THIS MAY AFFECT YOUR PRIVACY. " - "Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` " - "or weaker if you wish to allow this transaction to proceed anyway."); - } - // Sending from Sapling to transparent will be caught above in the - // AllowRevealedRecipients check; sending to Sprout is disallowed - // entirely. - } - - if (!spendable.orchardNoteMetadata.empty()) { - if (recipientPools_.count(OutputPool::Sapling) && !strategy_.AllowRevealedAmounts()) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Sending from the Orchard shielded pool to the Sapling " - "shielded pool is not enabled by default because it will " - "publicly reveal the transaction amount. THIS MAY AFFECT YOUR PRIVACY. " - "Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` " - "or weaker if you wish to allow this transaction to proceed anyway."); - } - - // Sending from Orchard to transparent will be caught above in the - // AllowRevealedRecipients check; sending to Sprout is disallowed - // entirely. - - if (spendable.orchardNoteMetadata.size() > nOrchardActionLimit) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - strprintf( - "Attempting to spend %u Orchard notes would exceed the current limit " - "of %u notes, which exists to prevent memory exhaustion. Restart with " - "`-orchardactionlimit=N` where N >= %u to allow the wallet to attempt " - "to construct this transaction.", - spendable.orchardNoteMetadata.size(), - nOrchardActionLimit, - spendable.orchardNoteMetadata.size())); - } - } - - spendable.LogInputs(getId()); - - CAmount t_inputs_total{0}; - CAmount z_inputs_total{0}; - for (const auto& t : spendable.utxos) { - t_inputs_total += t.Value(); - } - for (const auto& t : spendable.sproutNoteEntries) { - z_inputs_total += t.note.value(); - } - for (const auto& t : spendable.saplingNoteEntries) { - z_inputs_total += t.note.value(); - } - for (const auto& t : spendable.orchardNoteMetadata) { - z_inputs_total += t.GetNoteValue(); - } - - if (z_inputs_total > 0 && mindepth_ == 0) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Minconf cannot be zero when sending from a shielded address"); - } - - // When spending transparent coinbase outputs, all inputs must be fully - // consumed, and they may only be sent to shielded recipients. - if (spendable.HasTransparentCoinbase()) { - if (t_inputs_total + z_inputs_total != targetAmount) { - throw JSONRPCError( - RPC_WALLET_ERROR, - strprintf( - "When shielding coinbase funds, the wallet does not allow any change. " - "The proposed transaction would result in %s in change.", - FormatMoney(t_inputs_total - targetAmount) - )); - } - if (txOutputAmounts_.t_outputs_total != 0) { - throw JSONRPCError( - RPC_WALLET_ERROR, - "Coinbase funds may only be sent to shielded recipients."); - } - } - - LogPrint("zrpcunsafe", "%s: spending %s to send %s with fee %s\n", - getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(fee_)); - LogPrint("zrpc", "%s: total transparent input: %s (to choose from)\n", getId(), FormatMoney(t_inputs_total)); - LogPrint("zrpcunsafe", "%s: total shielded input: %s (to choose from)\n", getId(), FormatMoney(z_inputs_total)); - LogPrint("zrpc", "%s: total transparent output: %s\n", getId(), FormatMoney(txOutputAmounts_.t_outputs_total)); - LogPrint("zrpcunsafe", "%s: total shielded Sapling output: %s\n", getId(), FormatMoney(txOutputAmounts_.sapling_outputs_total)); - LogPrint("zrpcunsafe", "%s: total shielded Orchard output: %s\n", getId(), FormatMoney(txOutputAmounts_.orchard_outputs_total)); - LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(fee_)); - - // Allow change to go to any pool for which we have recipients. - std::set allowedChangeTypes = recipientPools_; - - // We always allow shielded change when not sending from the legacy account. - if (sendFromAccount_ != ZCASH_LEGACY_ACCOUNT) { - allowedChangeTypes.insert(OutputPool::Sapling); - } - - auto ovks = this->SelectOVKs(spendable); - auto allowChangeTypes = [&](const std::set& receiverTypes) { - for (ReceiverType rtype : receiverTypes) { - switch (rtype) { - case ReceiverType::P2PKH: - case ReceiverType::P2SH: - if (!spendable.utxos.empty() || strategy_.AllowRevealedRecipients()) { - allowedChangeTypes.insert(OutputPool::Transparent); - } - break; - case ReceiverType::Sapling: - if (!spendable.saplingNoteEntries.empty() || strategy_.AllowRevealedAmounts()) { - allowedChangeTypes.insert(OutputPool::Sapling); - } - break; - case ReceiverType::Orchard: - if (builder_.SupportsOrchard() && - (!spendable.orchardNoteMetadata.empty() || strategy_.AllowRevealedAmounts())) { - allowedChangeTypes.insert(OutputPool::Orchard); - } - break; - } - } - }; - - std::visit(match { - [&](const CKeyID& keyId) { - allowedChangeTypes.insert(OutputPool::Transparent); - auto changeAddr = pwalletMain->GenerateChangeAddressForAccount( - sendFromAccount_, allowedChangeTypes); - assert(changeAddr.has_value()); - builder_.SendChangeTo(changeAddr.value(), ovks.first); - }, - [&](const CScriptID& scriptId) { - allowedChangeTypes.insert(OutputPool::Transparent); - auto changeAddr = pwalletMain->GenerateChangeAddressForAccount( - sendFromAccount_, allowedChangeTypes); - assert(changeAddr.has_value()); - builder_.SendChangeTo(changeAddr.value(), ovks.first); - }, - [&](const libzcash::SproutPaymentAddress& addr) { - // for Sprout, we return change to the originating address. - builder_.SendChangeToSprout(addr); - }, - [&](const libzcash::SproutViewingKey& vk) { - // for Sprout, we return change to the originating address. - builder_.SendChangeToSprout(vk.address()); - }, - [&](const libzcash::SaplingPaymentAddress& addr) { - // for Sapling, if using a legacy address, return change to the - // originating address; otherwise return it to the Sapling internal - // address corresponding to the UFVK. - if (sendFromAccount_ == ZCASH_LEGACY_ACCOUNT) { - builder_.SendChangeTo(addr, ovks.first); - } else { - auto changeAddr = pwalletMain->GenerateChangeAddressForAccount( - sendFromAccount_, allowedChangeTypes); - assert(changeAddr.has_value()); - builder_.SendChangeTo(changeAddr.value(), ovks.first); - } - }, - [&](const libzcash::SaplingExtendedFullViewingKey& fvk) { - // for Sapling, if using a legacy address, return change to the - // originating address; otherwise return it to the Sapling internal - // address corresponding to the UFVK. - if (sendFromAccount_ == ZCASH_LEGACY_ACCOUNT) { - builder_.SendChangeTo(fvk.DefaultAddress(), ovks.first); - } else { - auto changeAddr = pwalletMain->GenerateChangeAddressForAccount( - sendFromAccount_, allowedChangeTypes); - assert(changeAddr.has_value()); - builder_.SendChangeTo(changeAddr.value(), ovks.first); - } - }, - [&](const libzcash::UnifiedAddress& ua) { - allowChangeTypes(ua.GetKnownReceiverTypes()); - - auto zufvk = pwalletMain->GetUFVKForAddress(ua); - if (!zufvk.has_value()) { - throw JSONRPCError( - RPC_WALLET_ERROR, - "Could not determine full viewing key for unified address."); - } - - auto changeAddr = zufvk.value().GetChangeAddress(allowedChangeTypes); - if (!changeAddr.has_value()) { - throw JSONRPCError( - RPC_WALLET_ERROR, - "Could not generate a change address from the inferred full viewing key."); +// At least #4 differs from the Rust transaction builder. +tl::expected +AsyncRPCOperation_sendmany::main_impl(CWallet& wallet) { + auto spendable = builder_.FindAllSpendableInputs(wallet, ztxoSelector_, mindepth_); + + auto preparedTx = builder_.PrepareTransaction( + wallet, + ztxoSelector_, + spendable, + recipients_, + chainActive, + strategy_, + fee_, + anchordepth_); + + return preparedTx + .map([&](const TransactionEffects& effects) { + effects.LockSpendable(wallet); + try { + const auto& spendable = effects.GetSpendable(); + const auto& payments = effects.GetPayments(); + spendable.LogInputs(getId()); + + LogPrint("zrpcunsafe", "%s: spending %s to send %s with fee %s\n", getId(), + FormatMoney(payments.Total()), + FormatMoney(spendable.Total()), + FormatMoney(effects.GetFee())); + LogPrint("zrpc", "%s: total transparent input: %s (to choose from)\n", getId(), + FormatMoney(spendable.GetTransparentTotal())); + LogPrint("zrpcunsafe", "%s: total shielded input: %s (to choose from)\n", getId(), + FormatMoney(spendable.GetSaplingTotal() + spendable.GetOrchardTotal())); + LogPrint("zrpc", "%s: total transparent output: %s\n", getId(), + FormatMoney(payments.GetTransparentTotal())); + LogPrint("zrpcunsafe", "%s: total shielded Sapling output: %s\n", getId(), + FormatMoney(payments.GetSaplingTotal())); + LogPrint("zrpcunsafe", "%s: total shielded Orchard output: %s\n", getId(), + FormatMoney(payments.GetOrchardTotal())); + LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(effects.GetFee())); + + auto buildResult = effects.ApproveAndBuild( + Params().GetConsensus(), + wallet, + chainActive, + strategy_); + auto tx = buildResult.GetTxOrThrow(); + + UniValue sendResult = SendTransaction(tx, payments.GetResolvedPayments(), std::nullopt, testmode); + set_result(sendResult); + + effects.UnlockSpendable(wallet); + return tx.GetHash(); + } catch (...) { + effects.UnlockSpendable(wallet); + throw; } - builder_.SendChangeTo(changeAddr.value(), ovks.first); - }, - [&](const libzcash::UnifiedFullViewingKey& fvk) { - allowChangeTypes(fvk.GetKnownReceiverTypes()); - auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), fvk); - auto changeAddr = zufvk.GetChangeAddress(allowedChangeTypes); - if (!changeAddr.has_value()) { - throw JSONRPCError( - RPC_WALLET_ERROR, - "Could not generate a change address from the specified full viewing key."); - } - builder_.SendChangeTo(changeAddr.value(), ovks.first); - }, - [&](const AccountZTXOPattern& acct) { - allowChangeTypes(acct.GetReceiverTypes()); - auto changeAddr = pwalletMain->GenerateChangeAddressForAccount( - acct.GetAccountId(), - allowedChangeTypes); - - assert(changeAddr.has_value()); - builder_.SendChangeTo(changeAddr.value(), ovks.first); - } - }, ztxoSelector_.GetPattern()); - - // Track the total of notes that we've added to the builder. This - // shouldn't strictly be necessary, given `spendable.LimitToAmount` - CAmount sum = 0; - - // Create Sapling outpoints - std::vector saplingOutPoints; - std::vector saplingNotes; - std::vector saplingKeys; - - for (const auto& t : spendable.saplingNoteEntries) { - saplingOutPoints.push_back(t.op); - saplingNotes.push_back(t.note); - - libzcash::SaplingExtendedSpendingKey saplingKey; - assert(pwalletMain->GetSaplingExtendedSpendingKey(t.address, saplingKey)); - saplingKeys.push_back(saplingKey); - - sum += t.note.value(); - if (sum >= targetAmount) { - break; - } - } - - // Fetch Sapling anchor and witnesses, and Orchard Merkle paths. - uint256 anchor; - std::vector> witnesses; - std::vector> orchardSpendInfo; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - if (!pwalletMain->GetSaplingNoteWitnesses(saplingOutPoints, anchordepth_, witnesses, anchor)) { - // This error should not appear once we're nAnchorConfirmations blocks past - // Sapling activation. - throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sapling witnesses."); - } - if (builder_.GetOrchardAnchor().has_value()) { - orchardSpendInfo = pwalletMain->GetOrchardSpendInfo(spendable.orchardNoteMetadata, builder_.GetOrchardAnchor().value()); - } - } - - // Add Orchard spends - for (size_t i = 0; i < orchardSpendInfo.size(); i++) { - auto spendInfo = std::move(orchardSpendInfo[i]); - if (!builder_.AddOrchardSpend( - std::move(spendInfo.first), - std::move(spendInfo.second))) - { - throw JSONRPCError( - RPC_WALLET_ERROR, - strprintf("Failed to add Orchard note to transaction (check %s for details)", GetDebugLogPath()) - ); - } - } - - // Add Sapling spends - for (size_t i = 0; i < saplingNotes.size(); i++) { - if (!witnesses[i]) { - throw JSONRPCError( - RPC_WALLET_ERROR, - strprintf( - "Missing witness for Sapling note at outpoint %s", - spendable.saplingNoteEntries[i].op.ToString()) - ); - } - - builder_.AddSaplingSpend(saplingKeys[i].expsk, saplingNotes[i], anchor, witnesses[i].value()); - } - - // Add outputs - for (const auto& r : recipients_) { - std::visit(match { - [&](const CKeyID& keyId) { - builder_.AddTransparentOutput(keyId, r.amount); - }, - [&](const CScriptID& scriptId) { - builder_.AddTransparentOutput(scriptId, r.amount); - }, - [&](const libzcash::SaplingPaymentAddress& addr) { - builder_.AddSaplingOutput( - ovks.second, addr, r.amount, - r.memo.has_value() ? r.memo.value().ToBytes() : Memo::NoMemo().ToBytes()); - }, - [&](const libzcash::OrchardRawAddress& addr) { - builder_.AddOrchardOutput( - ovks.second, addr, r.amount, - r.memo.has_value() ? std::optional(r.memo.value().ToBytes()) : std::nullopt); - } - }, r.address); - } - - // Add transparent utxos - for (const auto& out : spendable.utxos) { - const CTxOut& txOut = out.tx->vout[out.i]; - builder_.AddTransparentInput(COutPoint(out.tx->GetHash(), out.i), txOut.scriptPubKey, txOut.nValue); - - sum += txOut.nValue; - if (sum >= targetAmount) { - break; - } - } - - // Find Sprout witnesses - // When spending notes, take a snapshot of note witnesses and anchors as the treestate will - // change upon arrival of new blocks which contain joinsplit transactions. This is likely - // to happen as creating a chained joinsplit transaction can take longer than the block interval. - // So, we need to take locks on cs_main and pwalletMain->cs_wallet so that the witnesses aren't - // updated. - // - // TODO: these locks would ideally be shared for selection of Sapling anchors and witnesses - // as well. - std::vector> vSproutWitnesses; - { - LOCK2(cs_main, pwalletMain->cs_wallet); - std::vector vOutPoints; - for (const auto& t : spendable.sproutNoteEntries) { - vOutPoints.push_back(t.jsop); - } - - // inputAnchor is not needed by builder_.AddSproutInput as it is for Sapling. - uint256 inputAnchor; - if (!pwalletMain->GetSproutNoteWitnesses(vOutPoints, anchordepth_, vSproutWitnesses, inputAnchor)) { - // This error should not appear once we're nAnchorConfirmations blocks past - // Sprout activation. - throw JSONRPCError(RPC_WALLET_ERROR, "Insufficient Sprout witnesses."); - } - } - - // Add Sprout spends - for (int i = 0; i < spendable.sproutNoteEntries.size(); i++) { - const auto& t = spendable.sproutNoteEntries[i]; - libzcash::SproutSpendingKey sk; - assert(pwalletMain->GetSproutSpendingKey(t.address, sk)); - - builder_.AddSproutInput(sk, t.note, vSproutWitnesses[i].value()); - - sum += t.note.value(); - if (sum >= targetAmount) { - break; - } - } - - // Build the transaction - auto buildResult = builder_.Build(); - auto tx = buildResult.GetTxOrThrow(); - - UniValue sendResult = SendTransaction(tx, recipients_, std::nullopt, testmode); - set_result(sendResult); - - return tx.GetHash(); -} - -std::pair AsyncRPCOperation_sendmany::SelectOVKs(const SpendableInputs& spendable) const { - uint256 internalOVK; - uint256 externalOVK; - if (!spendable.orchardNoteMetadata.empty()) { - std::optional fvk; - std::visit(match { - [&](const UnifiedAddress& addr) { - auto ufvk = pwalletMain->GetUFVKForAddress(addr); - // This is safe because spending key checks will have ensured that we - // have a UFVK corresponding to this address, and Orchard notes will - // not have been selected if the UFVK does not contain an Orchard key. - fvk = ufvk.value().GetOrchardKey().value(); - }, - [&](const UnifiedFullViewingKey& ufvk) { - // Orchard notes will not have been selected if the UFVK does not contain - // an Orchard key. - fvk = ufvk.GetOrchardKey().value(); - }, - [&](const AccountZTXOPattern& acct) { - // By definition, we have a UFVK for every known account. - auto ufvk = pwalletMain->GetUnifiedFullViewingKeyByAccount(acct.GetAccountId()); - // Orchard notes will not have been selected if the UFVK does not contain - // an Orchard key. - fvk = ufvk.value().GetOrchardKey().value(); - }, - [&](const auto& other) { - throw std::runtime_error("SelectOVKs: Selector cannot select Orchard notes."); - } - }, this->ztxoSelector_.GetPattern()); - assert(fvk.has_value()); - - internalOVK = fvk.value().ToInternalOutgoingViewingKey(); - externalOVK = fvk.value().ToExternalOutgoingViewingKey(); - } else if (!spendable.saplingNoteEntries.empty()) { - std::optional dfvk; - std::visit(match { - [&](const libzcash::SaplingPaymentAddress& addr) { - libzcash::SaplingExtendedSpendingKey extsk; - assert(pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk)); - dfvk = extsk.ToXFVK(); - }, - [&](const UnifiedAddress& addr) { - auto ufvk = pwalletMain->GetUFVKForAddress(addr); - // This is safe because spending key checks will have ensured that we - // have a UFVK corresponding to this address, and Sapling notes will - // not have been selected if the UFVK does not contain a Sapling key. - dfvk = ufvk.value().GetSaplingKey().value(); - }, - [&](const UnifiedFullViewingKey& ufvk) { - // Sapling notes will not have been selected if the UFVK does not contain - // a Sapling key. - dfvk = ufvk.GetSaplingKey().value(); - }, - [&](const AccountZTXOPattern& acct) { - // By definition, we have a UFVK for every known account. - auto ufvk = pwalletMain->GetUnifiedFullViewingKeyByAccount(acct.GetAccountId()); - // Sapling notes will not have been selected if the UFVK does not contain - // a Sapling key. - dfvk = ufvk.value().GetSaplingKey().value(); - }, - [&](const auto& other) { - throw std::runtime_error("SelectOVKs: Selector cannot select Sapling notes."); - } - }, this->ztxoSelector_.GetPattern()); - assert(dfvk.has_value()); - - auto ovks = dfvk.value().GetOVKs(); - internalOVK = ovks.first; - externalOVK = ovks.second; - } else if (!spendable.utxos.empty()) { - std::optional tfvk; - std::visit(match { - [&](const CKeyID& keyId) { - tfvk = pwalletMain->GetLegacyAccountKey().ToAccountPubKey(); - }, - [&](const CScriptID& keyId) { - tfvk = pwalletMain->GetLegacyAccountKey().ToAccountPubKey(); - }, - [&](const UnifiedAddress& addr) { - // This is safe because spending key checks will have ensured that we - // have a UFVK corresponding to this address, and transparent UTXOs will - // not have been selected if the UFVK does not contain a transparent key. - auto ufvk = pwalletMain->GetUFVKForAddress(addr); - tfvk = ufvk.value().GetTransparentKey().value(); - }, - [&](const UnifiedFullViewingKey& ufvk) { - // Transparent UTXOs will not have been selected if the UFVK does not contain - // a transparent key. - tfvk = ufvk.GetTransparentKey().value(); - }, - [&](const AccountZTXOPattern& acct) { - if (acct.GetAccountId() == ZCASH_LEGACY_ACCOUNT) { - tfvk = pwalletMain->GetLegacyAccountKey().ToAccountPubKey(); - } else { - // By definition, we have a UFVK for every known account. - auto ufvk = pwalletMain->GetUnifiedFullViewingKeyByAccount(acct.GetAccountId()).value(); - // Transparent UTXOs will not have been selected if the UFVK does not contain - // a transparent key. - tfvk = ufvk.GetTransparentKey().value(); - } - }, - [&](const auto& other) { - throw std::runtime_error("SelectOVKs: Selector cannot select transparent UTXOs."); - } - }, this->ztxoSelector_.GetPattern()); - assert(tfvk.has_value()); - - auto ovks = tfvk.value().GetOVKsForShielding(); - internalOVK = ovks.first; - externalOVK = ovks.second; - } else if (!spendable.sproutNoteEntries.empty()) { - // use the legacy transparent account OVKs when sending from Sprout - auto tfvk = pwalletMain->GetLegacyAccountKey().ToAccountPubKey(); - auto ovks = tfvk.GetOVKsForShielding(); - internalOVK = ovks.first; - externalOVK = ovks.second; - } else { - // This should be unreachable; it is left in place as a guard to ensure - // that when new input types are added to SpendableInputs in the future - // that we do not accidentally return the all-zeros OVK. - throw std::runtime_error("No spendable inputs."); - } - - return std::make_pair(internalOVK, externalOVK); -} - -/** - * Compute a dust threshold based upon a standard p2pkh txout. - */ -CAmount AsyncRPCOperation_sendmany::DefaultDustThreshold() { - CKey secret{CKey::TestOnlyRandomKey(true)}; - CScript scriptPubKey = GetScriptForDestination(secret.GetPubKey().GetID()); - CTxOut txout(CAmount(1), scriptPubKey); - // TODO: use a local for minRelayTxFee rather than a global - return txout.GetDustThreshold(minRelayTxFee); + }); } /** diff --git a/depend/zcash/src/wallet/asyncrpcoperation_sendmany.h b/depend/zcash/src/wallet/asyncrpcoperation_sendmany.h index e17cebdaa..409839064 100644 --- a/depend/zcash/src/wallet/asyncrpcoperation_sendmany.h +++ b/depend/zcash/src/wallet/asyncrpcoperation_sendmany.h @@ -22,28 +22,20 @@ #include -#include - using namespace libzcash; -class TxOutputAmounts { -public: - CAmount t_outputs_total{0}; - CAmount sapling_outputs_total{0}; - CAmount orchard_outputs_total{0}; -}; - class AsyncRPCOperation_sendmany : public AsyncRPCOperation { public: AsyncRPCOperation_sendmany( - TransactionBuilder builder, + WalletTxBuilder builder, ZTXOSelector ztxoSelector, - std::vector recipients, + std::vector recipients, int minDepth, unsigned int anchorDepth, TransactionStrategy strategy, - CAmount fee = DEFAULT_FEE, + std::optional fee, UniValue contextInfo = NullUniValue); + virtual ~AsyncRPCOperation_sendmany(); // We don't want to be copied or moved around @@ -61,30 +53,16 @@ class AsyncRPCOperation_sendmany : public AsyncRPCOperation { private: friend class TEST_FRIEND_AsyncRPCOperation_sendmany; // class for unit testing - TransactionBuilder builder_; + WalletTxBuilder builder_; ZTXOSelector ztxoSelector_; - std::vector recipients_; + std::vector recipients_; + TransactionStrategy strategy_; int mindepth_{1}; unsigned int anchordepth_{nAnchorConfirmations}; - CAmount fee_; + std::optional fee_; UniValue contextinfo_; // optional data to include in return value from getStatus() - bool isfromsprout_{false}; - bool isfromsapling_{false}; - TransactionStrategy strategy_; - AccountId sendFromAccount_; - std::set recipientPools_; - TxOutputAmounts txOutputAmounts_; - - /** - * Compute the internal and external OVKs to use in transaction construction, given - * the spendable inputs. - */ - std::pair SelectOVKs(const SpendableInputs& spendable) const; - - static CAmount DefaultDustThreshold(); - - uint256 main_impl(); + tl::expected main_impl(CWallet& wallet); }; // To test private methods, a friend class can act as a proxy @@ -94,8 +72,8 @@ class TEST_FRIEND_AsyncRPCOperation_sendmany { TEST_FRIEND_AsyncRPCOperation_sendmany(std::shared_ptr ptr) : delegate(ptr) {} - uint256 main_impl() { - return delegate->main_impl(); + tl::expected main_impl(CWallet& wallet) { + return delegate->main_impl(wallet); } void set_state(OperationStatus state) { diff --git a/depend/zcash/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/depend/zcash/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index db255ccec..90bce6ede 100644 --- a/depend/zcash/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/depend/zcash/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -18,7 +18,6 @@ #include "proof_verifier.h" #include "rpc/protocol.h" #include "rpc/server.h" -#include "transaction_builder.h" #include "timedata.h" #include "util/system.h" #include "util/moneystr.h" @@ -29,7 +28,6 @@ #include "util/match.h" #include "zcash/IncrementalMerkleTree.hpp" #include "miner.h" -#include "wallet/paymentdisclosuredb.h" #include #include @@ -43,60 +41,36 @@ using namespace libzcash; -static int find_output(UniValue obj, int n) { - UniValue outputMapValue = find_value(obj, "outputmap"); - if (!outputMapValue.isArray()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); - } - - UniValue outputMap = outputMapValue.get_array(); - assert(outputMap.size() == ZC_NUM_JS_OUTPUTS); - for (size_t i = 0; i < outputMap.size(); i++) { - if (outputMap[i].get_int() == n) { - return i; - } - } - - throw std::logic_error("n is not present in outputmap"); -} - AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( - TransactionBuilder builder, - CMutableTransaction contextualTx, - std::vector inputs, + WalletTxBuilder builder, + ZTXOSelector ztxoSelector, PaymentAddress toAddress, - CAmount fee, + std::optional memo, + TransactionStrategy strategy, + int nUTXOLimit, + std::optional fee, UniValue contextInfo) : - builder_(std::move(builder)), tx_(contextualTx), inputs_(inputs), fee_(fee), contextinfo_(contextInfo) + builder_(std::move(builder)), + ztxoSelector_(ztxoSelector), + toAddress_(toAddress), + memo_(memo), + strategy_(strategy), + nUTXOLimit_(nUTXOLimit), + fee_(fee), + contextinfo_(contextInfo) { - assert(contextualTx.nVersion >= 2); // transaction format version must support vJoinSplit - - if (fee < 0 || fee > MAX_MONEY) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range"); - } + assert(!fee.has_value() || MoneyRange(fee.value())); + assert(ztxoSelector.RequireSpendingKeys()); - if (inputs.size() == 0) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Empty inputs"); - } - - // Check the destination address is valid for this network i.e. not testnet being used on mainnet - std::visit(match { - [&](CKeyID addr) { + examine(toAddress_, match { + [](const CKeyID&) { throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2pkh address."); }, - [&](CScriptID addr) { + [](const CScriptID&) { throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2sh address."); }, - [&](libzcash::SaplingPaymentAddress addr) { - tozaddr_ = addr; - }, - [&](libzcash::SproutPaymentAddress addr) { - tozaddr_ = addr; - }, - [&](libzcash::UnifiedAddress addr) { - tozaddr_ = addr; - } - }, toAddress); + [](const auto&) { }, + }); // Log the context info if (LogAcceptCategory("zrpcunsafe")) { @@ -104,12 +78,6 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( } else { LogPrint("zrpc", "%s: z_shieldcoinbase initialized\n", getId()); } - - // Lock UTXOs - lock_utxos(); - - // Enable payment disclosure if requested - paymentDisclosureMode = fExperimentalPaymentDisclosure; } AsyncRPCOperation_shieldcoinbase::~AsyncRPCOperation_shieldcoinbase() { @@ -117,21 +85,20 @@ AsyncRPCOperation_shieldcoinbase::~AsyncRPCOperation_shieldcoinbase() { void AsyncRPCOperation_shieldcoinbase::main() { if (isCancelled()) { - unlock_utxos(); // clean up + effects_.value().UnlockSpendable(*pwalletMain); return; } set_state(OperationStatus::EXECUTING); start_execution_clock(); - bool success = false; - #ifdef ENABLE_MINING GenerateBitcoins(false, 0, Params()); #endif + std::optional txid; try { - success = main_impl(); + txid = main_impl(*pwalletMain); } catch (const UniValue& objError) { int code = find_value(objError, "code").get_int(); std::string message = find_value(objError, "message").get_str(); @@ -157,306 +124,131 @@ void AsyncRPCOperation_shieldcoinbase::main() { stop_execution_clock(); - if (success) { + if (txid.has_value()) { set_state(OperationStatus::SUCCESS); } else { set_state(OperationStatus::FAILED); } std::string s = strprintf("%s: z_shieldcoinbase finished (status=%s", getId(), getStateAsString()); - if (success) { - s += strprintf(", txid=%s)\n", tx_.GetHash().ToString()); + if (txid.has_value()) { + s += strprintf(", txid=%s)\n", txid.value().GetHex()); } else { s += strprintf(", error=%s)\n", getErrorMessage()); } LogPrintf("%s",s); - - unlock_utxos(); // clean up - - // !!! Payment disclosure START - if (success && paymentDisclosureMode && paymentDisclosureData_.size()>0) { - uint256 txidhash = tx_.GetHash(); - std::shared_ptr db = PaymentDisclosureDB::sharedInstance(); - for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { - p.first.hash = txidhash; - if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); - } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); - } - } - } - // !!! Payment disclosure END } -bool AsyncRPCOperation_shieldcoinbase::main_impl() { - CAmount minersFee = fee_; +Remaining AsyncRPCOperation_shieldcoinbase::prepare(CWallet& wallet) { + auto spendable = builder_.FindAllSpendableInputs(wallet, ztxoSelector_, COINBASE_MATURITY); - size_t numInputs = inputs_.size(); + // Find unspent coinbase utxos and update estimated size + unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING; + CAmount shieldingValue = 0; + CAmount remainingValue = 0; + // We start with the estimated size being the most largest recipient, which `MIN_TX_COST` + // approximates. This then increases as we add inputs to the tx. + size_t estimatedTxSize = MIN_TX_COST; + size_t utxoCounter = 0; + size_t numUtxos = 0; + bool maxedOutFlag = false; - CAmount targetAmount = 0; - for (ShieldCoinbaseUTXO & utxo : inputs_) { - targetAmount += utxo.amount; - } - - if (targetAmount <= minersFee) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, - strprintf("Insufficient coinbase funds, have %s and miners fee is %s", - FormatMoney(targetAmount), FormatMoney(minersFee))); - } - - CAmount sendAmount = targetAmount - minersFee; - LogPrint("zrpc", "%s: spending %s to shield %s with fee %s\n", - getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); - - return std::visit(ShieldToAddress(this, sendAmount), tozaddr_); -} + for (const COutput& out : spendable.utxos) { + auto scriptPubKey = out.tx->vout[out.i].scriptPubKey; + CAmount nValue = out.tx->vout[out.i].nValue; -void ShieldToAddress::shieldToAddress(const libzcash::RecipientAddress& recipient, AsyncRPCOperation_shieldcoinbase *m_op) { - m_op->builder_.SetFee(m_op->fee_); - - // Sending from a t-address, which we don't have an ovk for. Instead, - // generate a common one from the HD seed. This ensures the data is - // recoverable, while keeping it logically separate from the ZIP 32 - // Sapling key hierarchy, which the user might not be using. - // FIXME: update to use the ZIP-316 OVK (#5511) - HDSeed seed = pwalletMain->GetHDSeedForRPC(); - uint256 ovk = ovkForShieldingFromTaddr(seed); - - // Add transparent inputs - for (auto t : m_op->inputs_) { - m_op->builder_.AddTransparentInput(COutPoint(t.txid, t.vout), t.scriptPubKey, t.amount); - } - - // Send all value to the target recipient - m_op->builder_.SendChangeTo(recipient, ovk); - - // Build the transaction - m_op->tx_ = m_op->builder_.Build().GetTxOrThrow(); -} - -bool ShieldToAddress::operator()(const CKeyID &addr) const { - return false; -} - -bool ShieldToAddress::operator()(const CScriptID &addr) const { - return false; -} - -bool ShieldToAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const { - // update the transaction with these inputs - CMutableTransaction rawTx(m_op->tx_); - for (ShieldCoinbaseUTXO & t : m_op->inputs_) { - CTxIn in(COutPoint(t.txid, t.vout)); - rawTx.vin.push_back(in); - } - m_op->tx_ = CTransaction(rawTx); - - // Prepare raw transaction to handle JoinSplits - CMutableTransaction mtx(m_op->tx_); - ed25519_generate_keypair(&m_op->joinSplitPrivKey_, &m_op->joinSplitPubKey_); - mtx.joinSplitPubKey = m_op->joinSplitPubKey_; - m_op->tx_ = CTransaction(mtx); - - // Create joinsplit - ShieldCoinbaseJSInfo info; - info.vpub_old = sendAmount; - info.vpub_new = 0; - JSOutput jso = JSOutput(zaddr, sendAmount); - info.vjsout.push_back(jso); - UniValue obj = m_op->perform_joinsplit(info); - - auto txAndResult = SignSendRawTransaction(obj, std::nullopt, m_op->testmode); - m_op->tx_ = txAndResult.first; - m_op->set_result(txAndResult.second); - return true; -} - -bool ShieldToAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const { - ShieldToAddress::shieldToAddress(zaddr, m_op); - - std::vector recipientMappings; - UniValue sendResult = SendTransaction(m_op->tx_, recipientMappings, std::nullopt, m_op->testmode); - m_op->set_result(sendResult); - - return true; -} - -bool ShieldToAddress::operator()(const libzcash::UnifiedAddress &uaddr) const { - // TODO check if an Orchard address is present, send to it if so. - const auto receiver{uaddr.GetSaplingReceiver()}; - if (receiver.has_value()) { - ShieldToAddress::shieldToAddress(receiver.value(), m_op); - - std::vector recipientMappings = {RecipientMapping(uaddr, receiver.value())}; - UniValue sendResult = SendTransaction(m_op->tx_, recipientMappings, std::nullopt, m_op->testmode); - m_op->set_result(sendResult); - - return true; - } - // This UA must contain a transparent address, which can't be the destination of coinbase shielding. - return false; -} - -UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInfo & info) { - uint32_t consensusBranchId; - uint256 anchor; - { - LOCK(cs_main); - consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - anchor = pcoinsTip->GetBestAnchor(SPROUT); - } - - - if (anchor.IsNull()) { - throw std::runtime_error("anchor is null"); - } - - // Make sure there are two inputs and two outputs - while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { - info.vjsin.push_back(JSInput()); - } - - while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { - info.vjsout.push_back(JSOutput()); - } - - if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { - throw runtime_error("unsupported joinsplit input/output counts"); - } - - CMutableTransaction mtx(tx_); - - LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", - getId(), - tx_.vJoinSplit.size(), - FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), - FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()), - FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value) - ); - - // Generate the proof, this can take over a minute. - std::array inputs - {info.vjsin[0], info.vjsin[1]}; - std::array outputs - {info.vjsout[0], info.vjsout[1]}; - std::array inputMap; - std::array outputMap; - - uint256 esk; // payment disclosure - secret - - assert(mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)); - JSDescription jsdesc = JSDescriptionInfo( - joinSplitPubKey_, - anchor, - inputs, - outputs, - info.vpub_old, - info.vpub_new - ).BuildRandomized( - inputMap, - outputMap, - !this->testmode, - &esk); // parameter expects pointer to esk, so pass in address - { - auto verifier = ProofVerifier::Strict(); - if (!(verifier.VerifySprout(jsdesc, joinSplitPubKey_))) { - throw std::runtime_error("error verifying joinsplit"); + CTxDestination address; + if (!ExtractDestination(scriptPubKey, address)) { + continue; } - } - mtx.vJoinSplit.push_back(jsdesc); + utxoCounter++; + if (!maxedOutFlag) { + size_t increase = + (std::get_if(&address) != nullptr) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_P2PKH_SIZE; - // Empty output script. - CScript scriptCode; - CTransaction signTx(mtx); - std::vector allPrevOutputs; - for (ShieldCoinbaseUTXO & t : inputs_) { - allPrevOutputs.emplace_back(t.amount, t.scriptPubKey); - } - PrecomputedTransactionData txdata(signTx, allPrevOutputs); - uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata); - - // Add the signature - if (!ed25519_sign( - &joinSplitPrivKey_, - dataToBeSigned.begin(), 32, - &mtx.joinSplitSig)) - { - throw std::runtime_error("ed25519_sign failed"); - } + if (estimatedTxSize + increase >= max_tx_size || (0 < nUTXOLimit_ && nUTXOLimit_ < utxoCounter)) { + maxedOutFlag = true; + } else { + estimatedTxSize += increase; + shieldingValue += nValue; + numUtxos++; + } + } - // Sanity check - if (!ed25519_verify( - &mtx.joinSplitPubKey, - &mtx.joinSplitSig, - dataToBeSigned.begin(), 32)) - { - throw std::runtime_error("ed25519_verify failed"); + if (maxedOutFlag) { + remainingValue += nValue; + } } - CTransaction rawTx(mtx); - tx_ = rawTx; - - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << rawTx; - - std::string encryptedNote1; - std::string encryptedNote2; - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char) 0x00); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[0]; - ss2 << ZCJoinSplit::h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey_); - - encryptedNote1 = HexStr(ss2.begin(), ss2.end()); - } - { - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << ((unsigned char) 0x01); - ss2 << jsdesc.ephemeralKey; - ss2 << jsdesc.ciphertexts[1]; - ss2 << ZCJoinSplit::h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey_); - - encryptedNote2 = HexStr(ss2.begin(), ss2.end()); - } + spendable.LimitTransparentUtxos(numUtxos); + + auto preparationResult = builder_.PrepareTransaction( + wallet, + ztxoSelector_, + spendable, + std::make_pair(toAddress_, memo_), + chainActive, + strategy_, + fee_, + nAnchorConfirmations); + + preparationResult + .map_error([&](const InputSelectionError& err) { + ThrowInputSelectionError(err, ztxoSelector_, strategy_); + }) + .map([&](const TransactionEffects& effects) { + effects.LockSpendable(wallet); + effects_ = effects; + }); + + return Remaining(utxoCounter, numUtxos, remainingValue, shieldingValue); +} - UniValue arrInputMap(UniValue::VARR); - UniValue arrOutputMap(UniValue::VARR); - for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { - arrInputMap.push_back(static_cast(inputMap[i])); - } - for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - arrOutputMap.push_back(static_cast(outputMap[i])); - } +uint256 AsyncRPCOperation_shieldcoinbase::main_impl(CWallet& wallet) { + uint256 txid; - KeyIO keyIO(Params()); - - // !!! Payment disclosure START - size_t js_index = tx_.vJoinSplit.size() - 1; - uint256 placeholder; - for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { - uint8_t mapped_index = outputMap[i]; - // placeholder for txid will be filled in later when tx has been finalized and signed. - PaymentDisclosureKey pdKey = {placeholder, js_index, mapped_index}; - JSOutput output = outputs[mapped_index]; - libzcash::SproutPaymentAddress zaddr = output.addr; // randomized output - PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey_, zaddr}; - paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - - LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), keyIO.EncodePaymentAddress(zaddr)); + try { + const auto& spendable = effects_->GetSpendable(); + const auto& payments = effects_->GetPayments(); + spendable.LogInputs(getId()); + + LogPrint("zrpcunsafe", "%s: spending %s to send %s with fee %s\n", getId(), + FormatMoney(payments.Total()), + FormatMoney(spendable.Total()), + FormatMoney(effects_->GetFee())); + LogPrint("zrpc", "%s: total transparent input: %s (to choose from)\n", getId(), + FormatMoney(spendable.GetTransparentTotal())); + LogPrint("zrpcunsafe", "%s: total shielded input: %s (to choose from)\n", getId(), + FormatMoney(spendable.GetSaplingTotal() + spendable.GetOrchardTotal())); + LogPrint("zrpc", "%s: total transparent output: %s\n", getId(), + FormatMoney(payments.GetTransparentTotal())); + LogPrint("zrpcunsafe", "%s: total shielded Sapling output: %s\n", getId(), + FormatMoney(payments.GetSaplingTotal())); + LogPrint("zrpcunsafe", "%s: total shielded Orchard output: %s\n", getId(), + FormatMoney(payments.GetOrchardTotal())); + LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(effects_->GetFee())); + + auto buildResult = effects_->ApproveAndBuild( + Params().GetConsensus(), + wallet, + chainActive, + strategy_); + + auto tx = buildResult.GetTxOrThrow(); + + UniValue sendResult = SendTransaction(tx, payments.GetResolvedPayments(), std::nullopt, testmode); + set_result(sendResult); + + txid = tx.GetHash(); + + effects_->UnlockSpendable(wallet); + return txid; + } catch (...) { + effects_->UnlockSpendable(wallet); + throw; } - // !!! Payment disclosure END - - UniValue obj(UniValue::VOBJ); - obj.pushKV("encryptednote1", encryptedNote1); - obj.pushKV("encryptednote2", encryptedNote2); - obj.pushKV("rawtxn", HexStr(ss.begin(), ss.end())); - obj.pushKV("inputmap", arrInputMap); - obj.pushKV("outputmap", arrOutputMap); - return obj; } /** @@ -473,25 +265,3 @@ UniValue AsyncRPCOperation_shieldcoinbase::getStatus() const { obj.pushKV("params", contextinfo_ ); return obj; } - -/** - * Lock input utxos - */ - void AsyncRPCOperation_shieldcoinbase::lock_utxos() { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto utxo : inputs_) { - COutPoint outpt(utxo.txid, utxo.vout); - pwalletMain->LockCoin(outpt); - } -} - -/** - * Unlock input utxos - */ -void AsyncRPCOperation_shieldcoinbase::unlock_utxos() { - LOCK2(cs_main, pwalletMain->cs_wallet); - for (auto utxo : inputs_) { - COutPoint outpt(utxo.txid, utxo.vout); - pwalletMain->UnlockCoin(outpt); - } -} diff --git a/depend/zcash/src/wallet/asyncrpcoperation_shieldcoinbase.h b/depend/zcash/src/wallet/asyncrpcoperation_shieldcoinbase.h index 23ad851ee..bd2d3130e 100644 --- a/depend/zcash/src/wallet/asyncrpcoperation_shieldcoinbase.h +++ b/depend/zcash/src/wallet/asyncrpcoperation_shieldcoinbase.h @@ -12,41 +12,58 @@ #include "zcash/JoinSplit.hpp" #include "zcash/Address.hpp" #include "wallet.h" -#include "wallet/paymentdisclosure.h" +#include "wallet/wallet_tx_builder.h" #include #include #include -#include +#include using namespace libzcash; -struct ShieldCoinbaseUTXO { - uint256 txid; - int vout; - CScript scriptPubKey; - CAmount amount; -}; +/** +When estimating the number of coinbase utxos we can shield in a single transaction: +1. An Orchard receiver is 9165 bytes. +2. Transaction overhead ~ 100 bytes +3. Spending a typical P2PKH is >=148 bytes, as defined in CTXIN_SPEND_P2PKH_SIZE. +4. Spending a multi-sig P2SH address can vary greatly: + https://github.com/bitcoin/bitcoin/blob/c3ad56f4e0b587d8d763af03d743fdfc2d180c9b/src/main.cpp#L517 + In real-world coinbase utxos, we consider a 3-of-3 multisig, where the size is roughly: + (3*(33+1))+3 = 105 byte redeem script + 105 + 1 + 3*(73+1) = 328 bytes of scriptSig, rounded up to 400 based on testnet experiments. +*/ +#define CTXIN_SPEND_P2SH_SIZE 400 + +#define SHIELD_COINBASE_DEFAULT_LIMIT 50 + +// transaction.h comment: spending taddr output requires CTxIn >= 148 bytes and +// typical taddr txout is 34 bytes +#define CTXIN_SPEND_P2PKH_SIZE 148 +#define CTXOUT_REGULAR_SIZE 34 + +class Remaining { +public: + Remaining(size_t utxoCounter, size_t numUtxos, CAmount remainingValue, CAmount shieldingValue): + utxoCounter(utxoCounter), numUtxos(numUtxos), remainingValue(remainingValue), shieldingValue(shieldingValue) { } -// Package of info which is passed to perform_joinsplit methods. -struct ShieldCoinbaseJSInfo -{ - std::vector vjsin; - std::vector vjsout; - CAmount vpub_old = 0; - CAmount vpub_new = 0; + size_t utxoCounter; + size_t numUtxos; + CAmount remainingValue; + CAmount shieldingValue; }; class AsyncRPCOperation_shieldcoinbase : public AsyncRPCOperation { public: AsyncRPCOperation_shieldcoinbase( - TransactionBuilder builder, - CMutableTransaction contextualTx, - std::vector inputs, + WalletTxBuilder builder, + ZTXOSelector ztxoSelector, PaymentAddress toAddress, - CAmount fee = DEFAULT_FEE, + std::optional memo, + TransactionStrategy strategy, + int nUTXOLimit, + std::optional fee, UniValue contextInfo = NullUniValue); virtual ~AsyncRPCOperation_shieldcoinbase(); @@ -56,63 +73,31 @@ class AsyncRPCOperation_shieldcoinbase : public AsyncRPCOperation { AsyncRPCOperation_shieldcoinbase& operator=(AsyncRPCOperation_shieldcoinbase const&) = delete; // Copy assign AsyncRPCOperation_shieldcoinbase& operator=(AsyncRPCOperation_shieldcoinbase &&) = delete; // Move assign + Remaining prepare(CWallet& wallet); + virtual void main(); virtual UniValue getStatus() const; - bool testmode = false; // Set to true to disable sending txs and generating proofs - - bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database. + bool testmode{false}; // Set to true to disable sending txs and generating proofs private: - friend class ShieldToAddress; friend class TEST_FRIEND_AsyncRPCOperation_shieldcoinbase; // class for unit testing - UniValue contextinfo_; // optional data to include in return value from getStatus() - - CAmount fee_; - PaymentAddress tozaddr_; - - Ed25519VerificationKey joinSplitPubKey_; - Ed25519SigningKey joinSplitPrivKey_; - - std::vector inputs_; - - TransactionBuilder builder_; - CTransaction tx_; - - bool main_impl(); - - // JoinSplit without any input notes to spend - UniValue perform_joinsplit(ShieldCoinbaseJSInfo &); - - void lock_utxos(); - - void unlock_utxos(); + WalletTxBuilder builder_; + ZTXOSelector ztxoSelector_; + PaymentAddress toAddress_; + std::optional memo_; + TransactionStrategy strategy_; + int nUTXOLimit_; + std::optional fee_; + std::optional effects_; - // payment disclosure! - std::vector paymentDisclosureData_; -}; - -class ShieldToAddress -{ -private: - AsyncRPCOperation_shieldcoinbase *m_op; - CAmount sendAmount; + UniValue contextinfo_; // optional data to include in return value from getStatus() - static void shieldToAddress(const libzcash::RecipientAddress& recipient, AsyncRPCOperation_shieldcoinbase *m_op); -public: - ShieldToAddress(AsyncRPCOperation_shieldcoinbase *op, CAmount sendAmount) : - m_op(op), sendAmount(sendAmount) {} - - bool operator()(const CKeyID &zaddr) const; - bool operator()(const CScriptID &zaddr) const; - bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; - bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; - bool operator()(const libzcash::UnifiedAddress &uaddr) const; + uint256 main_impl(CWallet& wallet); }; - // To test private methods, a friend class can act as a proxy class TEST_FRIEND_AsyncRPCOperation_shieldcoinbase { public: @@ -120,22 +105,10 @@ class TEST_FRIEND_AsyncRPCOperation_shieldcoinbase { TEST_FRIEND_AsyncRPCOperation_shieldcoinbase(std::shared_ptr ptr) : delegate(ptr) {} - CTransaction getTx() { - return delegate->tx_; - } - - void setTx(CTransaction tx) { - delegate->tx_ = tx; - } - // Delegated methods - bool main_impl() { - return delegate->main_impl(); - } - - UniValue perform_joinsplit(ShieldCoinbaseJSInfo &info) { - return delegate->perform_joinsplit(info); + uint256 main_impl(CWallet& wallet) { + return delegate->main_impl(wallet); } void set_state(OperationStatus state) { @@ -145,4 +118,3 @@ class TEST_FRIEND_AsyncRPCOperation_shieldcoinbase { #endif // ZCASH_WALLET_ASYNCRPCOPERATION_SHIELDCOINBASE_H - diff --git a/depend/zcash/src/wallet/gtest/test_orchard_wallet.cpp b/depend/zcash/src/wallet/gtest/test_orchard_wallet.cpp index 25802ad3b..a463477a0 100644 --- a/depend/zcash/src/wallet/gtest/test_orchard_wallet.cpp +++ b/depend/zcash/src/wallet/gtest/test_orchard_wallet.cpp @@ -9,8 +9,6 @@ #include "wallet/orchard.h" #include "zcash/Address.hpp" -#include "gtest/test_transaction_builder.h" - #include using namespace libzcash; @@ -30,9 +28,7 @@ CTransaction FakeOrchardTx(const OrchardSpendingKey& sk, libzcash::diversifier_i auto fvk = sk.ToFullViewingKey(); auto ivk = fvk.ToIncomingViewingKey(); auto recipient = ivk.Address(j); - - TransactionBuilderCoinsViewDB fakeDB; - auto orchardAnchor = fakeDB.GetBestAnchor(ShieldedType::ORCHARD); + auto orchardAnchor = uint256(); // Create a shielding transaction from transparent to Orchard // 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 fee diff --git a/depend/zcash/src/wallet/gtest/test_paymentdisclosure.cpp b/depend/zcash/src/wallet/gtest/test_paymentdisclosure.cpp index dad3efbd8..241b3ac07 100644 --- a/depend/zcash/src/wallet/gtest/test_paymentdisclosure.cpp +++ b/depend/zcash/src/wallet/gtest/test_paymentdisclosure.cpp @@ -97,9 +97,9 @@ TEST(paymentdisclosure, mainnet) { for (int i=0; i::max(); @@ -118,7 +118,7 @@ TEST(paymentdisclosure, mainnet) { // Modify this local variable and confirm it no longer matches info2.esk = GetRandHash(); - GetRandBytes(info2.joinSplitPrivKey.bytes, ED25519_VERIFICATION_KEY_LEN); + GetRandBytes(info2.joinSplitPrivKey.bytes.data(), info2.joinSplitPrivKey.bytes.size()); info2.zaddr = libzcash::SproutSpendingKey::random().address(); ASSERT_NE(info, info2); @@ -136,20 +136,17 @@ TEST(paymentdisclosure, mainnet) { uint256 dataToBeSigned = SerializeHash(payload, SER_GETHASH, 0); // Compute the payload signature - Ed25519Signature payloadSig; - if (!ed25519_sign( - &joinSplitPrivKey, - dataToBeSigned.begin(), 32, - &payloadSig)) - { - throw std::runtime_error("ed25519_sign failed"); - } + ed25519::Signature payloadSig; + ed25519::sign( + joinSplitPrivKey, + {dataToBeSigned.begin(), 32}, + payloadSig); // Sanity check - if (!ed25519_verify( - &joinSplitPubKey, - &payloadSig, - dataToBeSigned.begin(), 32)) + if (!ed25519::verify( + joinSplitPubKey, + payloadSig, + {dataToBeSigned.begin(), 32})) { throw std::runtime_error("ed25519_verify failed"); } diff --git a/depend/zcash/src/wallet/gtest/test_rpc_wallet.cpp b/depend/zcash/src/wallet/gtest/test_rpc_wallet.cpp index 3e29112c5..fc38e99d0 100644 --- a/depend/zcash/src/wallet/gtest/test_rpc_wallet.cpp +++ b/depend/zcash/src/wallet/gtest/test_rpc_wallet.cpp @@ -8,7 +8,6 @@ #include "transaction_builder.h" #include "util/test.h" #include "gtest/utils.h" -#include "wallet/asyncrpcoperation_mergetoaddress.h" #include "wallet/asyncrpcoperation_shieldcoinbase.h" #include "wallet/asyncrpcoperation_sendmany.h" #include "wallet/memo.h" @@ -17,72 +16,20 @@ #include #include +namespace { + bool find_error(const UniValue& objError, const std::string& expected) { return find_value(objError, "message").get_str().find(expected) != string::npos; } -TEST(WalletRPCTests,ZShieldCoinbaseInternals) -{ - LoadProofParameters(); - - SelectParams(CBaseChainParams::TESTNET); - const Consensus::Params& consensusParams = Params().GetConsensus(); - - // we need to use pwalletMain because of AsyncRPCOperation_shieldcoinbase - LoadGlobalWallet(); - { - LOCK2(cs_main, pwalletMain->cs_wallet); - - // Mutable tx containing contextual information we need to build tx - // We removed the ability to create pre-Sapling Sprout proofs, so we can - // only create Sapling-onwards transactions. - int nHeight = consensusParams.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight; - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight + 1, false); - - // Add keys manually - auto pa = pwalletMain->GenerateNewSproutZKey(); - - // Insufficient funds - { - std::vector inputs = { ShieldCoinbaseUTXO{uint256(),0,0} }; - std::shared_ptr operation(new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, pa) ); - operation->main(); - EXPECT_TRUE(operation->isFailed()); - std::string msg = operation->getErrorMessage(); - EXPECT_TRUE(msg.find("Insufficient coinbase funds") != string::npos); - } - - // Test the perform_joinsplit methods. - { - // Dummy input so the operation object can be instantiated. - std::vector inputs = { ShieldCoinbaseUTXO{uint256(),0,100000} }; - std::shared_ptr operation(new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, pa) ); - std::shared_ptr ptr = std::dynamic_pointer_cast (operation); - TEST_FRIEND_AsyncRPCOperation_shieldcoinbase proxy(ptr); - static_cast(operation.get())->testmode = true; - - ShieldCoinbaseJSInfo info; - info.vjsin.push_back(JSInput()); - info.vjsin.push_back(JSInput()); - info.vjsin.push_back(JSInput()); - try { - proxy.perform_joinsplit(info); - } catch (const std::runtime_error & e) { - EXPECT_TRUE(string(e.what()).find("unsupported joinsplit input") != string::npos); - } - - info.vjsin.clear(); - try { - proxy.perform_joinsplit(info); - } catch (const std::runtime_error & e) { - EXPECT_TRUE(string(e.what()).find("error verifying joinsplit") != string::npos); - } - } - - } - UnloadGlobalWallet(); +CWalletTx FakeWalletTx() { + CMutableTransaction mtx; + mtx.vout.resize(1); + mtx.vout[0].nValue = 1; + return CWalletTx(nullptr, mtx); } +} // TODO: test private methods TEST(WalletRPCTests, RPCZMergeToAddressInternals) @@ -109,129 +56,62 @@ TEST(WalletRPCTests, RPCZMergeToAddressInternals) auto taddr = pwalletMain->GenerateNewKey(true).GetID(); std::string taddr_string = keyIO.EncodeDestination(taddr); - MergeToAddressRecipient taddr1(keyIO.DecodePaymentAddress(taddr_string).value(), ""); - auto pa = pwalletMain->GenerateNewSproutZKey(); - MergeToAddressRecipient zaddr1(pa, "DEADBEEF"); + NetAmountRecipient taddr1(keyIO.DecodePaymentAddress(taddr_string).value(), Memo()); + auto sproutKey = pwalletMain->GenerateNewSproutZKey(); + NetAmountRecipient zaddr1(sproutKey, Memo()); - // Insufficient funds - { - std::vector inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},0, CScript()} }; - std::shared_ptr operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, inputs, {}, {}, zaddr1) ); - operation->main(); - EXPECT_TRUE(operation->isFailed()); - std::string msg = operation->getErrorMessage(); - EXPECT_TRUE(msg.find("Insufficient funds, have 0.00 and miners fee is 0.00001") != string::npos); - } + auto saplingKey = pwalletMain->GenerateNewLegacySaplingZKey(); + NetAmountRecipient zaddr2(saplingKey, Memo()); - // get_memo_from_hex_string()) - { - std::vector inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},100000, CScript()} }; - std::shared_ptr operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, inputs, {}, {}, zaddr1) ); - std::shared_ptr ptr = std::dynamic_pointer_cast (operation); - TEST_FRIEND_AsyncRPCOperation_mergetoaddress proxy(ptr); - - std::string memo = "DEADBEEF"; - std::array array = proxy.get_memo_from_hex_string(memo); - EXPECT_EQ(array[0], 0xDE); - EXPECT_EQ(array[1], 0xAD); - EXPECT_EQ(array[2], 0xBE); - EXPECT_EQ(array[3], 0xEF); - for (int i=4; i v (2 * (ZC_MEMO_SIZE+1)); - std::fill(v.begin(), v.end(), 'A'); - std::string bigmemo(v.begin(), v.end()); - - try { - proxy.get_memo_from_hex_string(bigmemo); - FAIL() << "Should have caused an error"; - } catch (const UniValue& objError) { - EXPECT_TRUE(find_error(objError, "too big")); - } - - // invalid hexadecimal string - std::fill(v.begin(), v.end(), '@'); // not a hex character - std::string badmemo(v.begin(), v.end()); - - try { - proxy.get_memo_from_hex_string(badmemo); - FAIL() << "Should have caused an error"; - } catch (const UniValue& objError) { - EXPECT_TRUE(find_error(objError, "hexadecimal format")); - } - - // odd length hexadecimal string - std::fill(v.begin(), v.end(), 'A'); - v.resize(v.size() - 1); - assert(v.size() % 2 == 1); // odd length - std::string oddmemo(v.begin(), v.end()); - try { - proxy.get_memo_from_hex_string(oddmemo); - FAIL() << "Should have caused an error"; - } catch (const UniValue& objError) { - EXPECT_TRUE(find_error(objError, "hexadecimal format")); - } - } + WalletTxBuilder builder(Params(), minRelayTxFee); + auto selector = CWallet::LegacyTransparentZTXOSelector( + true, + TransparentCoinbasePolicy::Disallow); + TransactionStrategy strategy(PrivacyPolicy::AllowRevealedSenders); - // Test the perform_joinsplit methods. - { - // Dummy input so the operation object can be instantiated. - std::vector inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},100000, CScript()} }; - std::shared_ptr operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, inputs, {}, {}, zaddr1) ); - std::shared_ptr ptr = std::dynamic_pointer_cast (operation); - TEST_FRIEND_AsyncRPCOperation_mergetoaddress proxy(ptr); - - // Enable test mode so tx is not sent and proofs are not generated - static_cast(operation.get())->testmode = true; - - MergeToAddressJSInfo info; - std::vector> witnesses; - uint256 anchor; - try { - proxy.perform_joinsplit(info, witnesses, anchor); - FAIL() << "Should have caused an error"; - } catch (const std::runtime_error & e) { - EXPECT_TRUE(string(e.what()).find("anchor is null") != string::npos); - } - - try { - std::vector v; - proxy.perform_joinsplit(info, v); - FAIL() << "Should have caused an error"; - } catch (const std::runtime_error & e) { - EXPECT_TRUE(string(e.what()).find("anchor is null") != string::npos); - } - - info.notes.push_back(SproutNote()); - try { - proxy.perform_joinsplit(info); - FAIL() << "Should have caused an error"; - } catch (const std::runtime_error & e) { - EXPECT_TRUE(string(e.what()).find("number of notes") != string::npos); - } - - info.notes.clear(); - info.vjsin.push_back(JSInput()); - info.vjsin.push_back(JSInput()); - info.vjsin.push_back(JSInput()); - try { - proxy.perform_joinsplit(info); - FAIL() << "Should have caused an error"; - } catch (const std::runtime_error & e) { - EXPECT_TRUE(string(e.what()).find("unsupported joinsplit input") != string::npos); - } - - info.vjsin.clear(); - try { - proxy.perform_joinsplit(info); - FAIL() << "Should have caused an error"; - } catch (const std::runtime_error & e) { - EXPECT_TRUE(string(e.what()).find("error verifying joinsplit") != string::npos); - } - } + SpendableInputs inputs; + auto wtx = FakeWalletTx(); + inputs.utxos.emplace_back(COutput(&wtx, 0, 100, true)); + + // Can’t send to Sprout + builder.PrepareTransaction( + *pwalletMain, + selector, + inputs, + zaddr1, + chainActive, + strategy, + 0, + 1) + .map_error([](const auto& err) { + EXPECT_TRUE(examine(err, match { + [](const AddressResolutionError& are) { + return are == AddressResolutionError::SproutRecipientsNotSupported; + }, + [](const auto&) { return false; }, + })); + }) + .map([](const auto&) { EXPECT_TRUE(false); }); + + // Insufficient funds + builder.PrepareTransaction( + *pwalletMain, + selector, + inputs, + zaddr2, + chainActive, + strategy, + std::nullopt, + 1) + .map_error([](const auto& err) { + EXPECT_TRUE(examine(err, match { + [](const InvalidFundsError& ife) { + return std::holds_alternative(ife.reason); + }, + [](const auto&) { return false; }, + })); + }) + .map([](const auto&) { EXPECT_TRUE(false); }); } UnloadGlobalWallet(); } @@ -284,13 +164,19 @@ TEST(WalletRPCTests, RPCZsendmanyTaddrToSapling) pwalletMain->LoadWalletTx(wtx); // Context that z_sendmany requires - auto builder = TransactionBuilder(consensusParams, nextBlockHeight, std::nullopt, pwalletMain); + auto builder = WalletTxBuilder(Params(), minRelayTxFee); mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight, false); - auto selector = pwalletMain->ZTXOSelectorForAddress(taddr, true, false).value(); - std::vector recipients = { ResolvedPayment(std::nullopt, pa, 1*COIN, Memo::FromHexOrThrow("ABCD")) }; - TransactionStrategy strategy(PrivacyPolicy::AllowRevealedSenders); - std::shared_ptr operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 0, 0, strategy)); + // we need AllowFullyTransparent because the transaction will result + // in transparent change as a consequence of sending from a legacy taddr + TransactionStrategy strategy(PrivacyPolicy::AllowFullyTransparent); + auto selector = pwalletMain->ZTXOSelectorForAddress( + taddr, + true, + TransparentCoinbasePolicy::Disallow, + false).value(); + std::vector recipients = { Payment(pa, 1*COIN, Memo::FromHexOrThrow("ABCD")) }; + std::shared_ptr operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 0, 0, strategy, std::nullopt)); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); // Enable test mode so tx is not sent diff --git a/depend/zcash/src/wallet/gtest/test_wallet.cpp b/depend/zcash/src/wallet/gtest/test_wallet.cpp index 7405666d2..fe7367a20 100644 --- a/depend/zcash/src/wallet/gtest/test_wallet.cpp +++ b/depend/zcash/src/wallet/gtest/test_wallet.cpp @@ -574,13 +574,13 @@ TEST(WalletTests, FindMySaplingNotes) { // No Sapling notes can be found in tx which does not belong to the wallet CWalletTx wtx {&wallet, tx}; ASSERT_FALSE(wallet.HaveSaplingSpendingKey(extfvk)); - auto noteMap = wallet.FindMySaplingNotes(consensusParams, wtx, 1).first; + auto noteMap = wallet.FindMySaplingNotes(Params(), wtx, 1).first; EXPECT_EQ(0, noteMap.size()); // Add spending key to wallet, so Sapling notes can be found ASSERT_TRUE(wallet.AddSaplingZKey(sk)); ASSERT_TRUE(wallet.HaveSaplingSpendingKey(extfvk)); - noteMap = wallet.FindMySaplingNotes(consensusParams, wtx, 1).first; + noteMap = wallet.FindMySaplingNotes(Params(), wtx, 1).first; EXPECT_EQ(2, noteMap.size()); // Revert to default @@ -733,7 +733,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) { EXPECT_EQ(0, chainActive.Height()); // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe - auto saplingNoteData = wallet.FindMySaplingNotes(consensusParams, wtx, 1).first; + auto saplingNoteData = wallet.FindMySaplingNotes(Params(), wtx, 1).first; ASSERT_TRUE(saplingNoteData.size() > 0); wtx.SetSaplingNoteData(saplingNoteData); wtx.SetMerkleBranch(block); @@ -748,15 +748,9 @@ TEST(WalletTests, GetConflictedSaplingNotes) { wtx = wallet.mapWallet[hash]; // Decrypt output note B - auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt( - consensusParams, - wtx.nExpiryHeight, - wtx.vShieldedOutput[0].encCiphertext, - ivk, - wtx.vShieldedOutput[0].ephemeralKey, - wtx.vShieldedOutput[0].cmu); + auto maybe_pt = wtx.DecryptSaplingNote(Params(), SaplingOutPoint(hash, 0)); ASSERT_EQ(static_cast(maybe_pt), true); - auto maybe_note = maybe_pt.value().note(ivk); + auto maybe_note = maybe_pt.value().first.note(ivk); ASSERT_EQ(static_cast(maybe_note), true); auto note2 = maybe_note.value(); @@ -1123,7 +1117,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) { // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe wtx.SetMerkleBranch(block); - auto saplingNoteData = wallet.FindMySaplingNotes(consensusParams, wtx, chainActive.Height()).first; + auto saplingNoteData = wallet.FindMySaplingNotes(Params(), wtx, chainActive.Height()).first; ASSERT_TRUE(saplingNoteData.size() > 0); wtx.SetSaplingNoteData(saplingNoteData); wallet.LoadWalletTx(wtx); @@ -1250,7 +1244,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { EXPECT_TRUE(chainActive.Contains(&fakeIndex)); EXPECT_EQ(0, chainActive.Height()); - auto saplingNoteData = wallet.FindMySaplingNotes(consensusParams, wtx, 1).first; + auto saplingNoteData = wallet.FindMySaplingNotes(Params(), wtx, 1).first; ASSERT_TRUE(saplingNoteData.size() > 0); wtx.SetSaplingNoteData(saplingNoteData); wtx.SetMerkleBranch(block); @@ -1276,15 +1270,9 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { ASSERT_FALSE(wallet.mapSaplingNullifiersToNotes.count(nf.value())); // Decrypt note B - auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt( - consensusParams, - wtx.nExpiryHeight, - wtx.vShieldedOutput[0].encCiphertext, - ivk, - wtx.vShieldedOutput[0].ephemeralKey, - wtx.vShieldedOutput[0].cmu); + auto maybe_pt = wtx.DecryptSaplingNote(Params(), SaplingOutPoint(wtx.GetHash(), 0)); ASSERT_EQ(static_cast(maybe_pt), true); - auto maybe_note = maybe_pt.value().note(ivk); + auto maybe_note = maybe_pt.value().first.note(ivk); ASSERT_EQ(static_cast(maybe_note), true); auto note2 = maybe_note.value(); @@ -1327,7 +1315,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) { EXPECT_TRUE(chainActive.Contains(&fakeIndex2)); EXPECT_EQ(1, chainActive.Height()); - auto saplingNoteData2 = wallet.FindMySaplingNotes(consensusParams, wtx2, 2).first; + auto saplingNoteData2 = wallet.FindMySaplingNotes(Params(), wtx2, 2).first; ASSERT_TRUE(saplingNoteData2.size() > 0); wtx2.SetSaplingNoteData(saplingNoteData2); wtx2.SetMerkleBranch(block2); @@ -2110,7 +2098,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) { EXPECT_EQ(0, chainActive.Height()); // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe - auto saplingNoteData = wallet.FindMySaplingNotes(consensusParams, wtx, chainActive.Height()).first; + auto saplingNoteData = wallet.FindMySaplingNotes(Params(), wtx, chainActive.Height()).first; ASSERT_TRUE(saplingNoteData.size() == 1); // wallet only has key for change output wtx.SetSaplingNoteData(saplingNoteData); wtx.SetMerkleBranch(block); @@ -2128,7 +2116,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) { ASSERT_TRUE(wallet.AddSaplingZKey(sk2)); ASSERT_TRUE(wallet.HaveSaplingSpendingKey(extfvk2)); CWalletTx wtx2 = wtx; - auto saplingNoteData2 = wallet.FindMySaplingNotes(consensusParams, wtx2, chainActive.Height()).first; + auto saplingNoteData2 = wallet.FindMySaplingNotes(Params(), wtx2, chainActive.Height()).first; ASSERT_TRUE(saplingNoteData2.size() == 2); wtx2.SetSaplingNoteData(saplingNoteData2); @@ -2259,7 +2247,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { EXPECT_EQ(0, chainActive.Height()); // Simulate SyncTransaction which calls AddToWalletIfInvolvingMe - auto saplingNoteData = wallet.FindMySaplingNotes(consensusParams, wtx, chainActive.Height()).first; + auto saplingNoteData = wallet.FindMySaplingNotes(Params(), wtx, chainActive.Height()).first; ASSERT_TRUE(saplingNoteData.size() > 0); wtx.SetSaplingNoteData(saplingNoteData); wtx.SetMerkleBranch(block); @@ -2274,9 +2262,9 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { wtx = wallet.mapWallet[hash]; // Prepare to spend the note that was just created - auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(consensusParams, fakeIndex.nHeight, tx1.vShieldedOutput[0].encCiphertext, ivk, tx1.vShieldedOutput[0].ephemeralKey, tx1.vShieldedOutput[0].cmu); + auto maybe_pt = wtx.DecryptSaplingNote(Params(), SaplingOutPoint(hash, 0)); ASSERT_EQ(static_cast(maybe_pt), true); - auto maybe_note = maybe_pt.value().note(ivk); + auto maybe_note = maybe_pt.value().first.note(ivk); ASSERT_EQ(static_cast(maybe_note), true); auto note = maybe_note.value(); auto anchor = frontiers.sapling.root(); diff --git a/depend/zcash/src/wallet/memo.h b/depend/zcash/src/wallet/memo.h index e4ee6c083..c95effbf9 100644 --- a/depend/zcash/src/wallet/memo.h +++ b/depend/zcash/src/wallet/memo.h @@ -50,7 +50,7 @@ class Memo { } static Memo FromHexOrThrow(const std::string& memoHex) { - return std::visit(match { + return examine(Memo::FromHex(memoHex), match { [&](Memo memo) { return memo; }, @@ -69,7 +69,7 @@ class Memo { // unreachable, but the compiler can't tell return Memo::NoMemo(); } - }, Memo::FromHex(memoHex)); + }); } // This copies, because if it returns a reference to the underlying value, diff --git a/depend/zcash/src/wallet/orchard.h b/depend/zcash/src/wallet/orchard.h index d3836c287..85df41e9d 100644 --- a/depend/zcash/src/wallet/orchard.h +++ b/depend/zcash/src/wallet/orchard.h @@ -226,7 +226,8 @@ class OrchardWallet */ void InitNoteCommitmentTree(const OrchardMerkleFrontier& frontier) { assert(!GetLastCheckpointHeight().has_value()); - assert(orchard_wallet_init_from_frontier(inner.get(), frontier.inner.get())); + assert(frontier.inner->init_wallet( + reinterpret_cast(inner.get()))); } /** @@ -282,7 +283,7 @@ class OrchardWallet if (orchard_wallet_add_notes_from_bundle( inner.get(), tx.GetHash().begin(), - tx.GetOrchardBundle().inner.get(), + tx.GetOrchardBundle().inner->as_ptr(), &txMeta, PushOrchardActionIVK, PushSpendActionIdx @@ -309,7 +310,7 @@ class OrchardWallet return orchard_wallet_load_bundle( inner.get(), tx.GetHash().begin(), - tx.GetOrchardBundle().inner.get(), + tx.GetOrchardBundle().inner->as_ptr(), rawHints.data(), rawHints.size(), txMeta.vActionsSpendingMyNotes.data(), @@ -331,7 +332,7 @@ class OrchardWallet (uint32_t) nBlockHeight, txidx, tx.GetHash().begin(), - tx.GetOrchardBundle().inner.get() + tx.GetOrchardBundle().inner->as_ptr() )) { return false; } @@ -475,7 +476,7 @@ class OrchardWallet OrchardActions result; orchard_wallet_get_txdata( inner.get(), - tx.GetOrchardBundle().inner.get(), + tx.GetOrchardBundle().inner->as_ptr(), reinterpret_cast(ovks.data()), ovks.size(), &result, diff --git a/depend/zcash/src/wallet/paymentdisclosure.cpp b/depend/zcash/src/wallet/paymentdisclosure.cpp index f6c03ef39..ff07665dc 100644 --- a/depend/zcash/src/wallet/paymentdisclosure.cpp +++ b/depend/zcash/src/wallet/paymentdisclosure.cpp @@ -16,7 +16,7 @@ std::string PaymentDisclosureInfo::ToString() const { } std::string PaymentDisclosure::ToString() const { - std::string s = HexStr(payloadSig.bytes, payloadSig.bytes + ED25519_SIGNATURE_LEN); + std::string s = HexStr(payloadSig.bytes.begin(), payloadSig.bytes.end()); return strprintf("PaymentDisclosure(payload=%s, payloadSig=%s)", payload.ToString(), s); } @@ -27,7 +27,7 @@ std::string PaymentDisclosurePayload::ToString() const { } PaymentDisclosure::PaymentDisclosure( - const Ed25519VerificationKey& joinSplitPubKey, + const ed25519::VerificationKey& joinSplitPubKey, const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info, const std::string& message) @@ -47,23 +47,20 @@ PaymentDisclosure::PaymentDisclosure( LogPrint("paymentdisclosure", "Payment Disclosure: signing raw payload = %s\n", dataToBeSigned.ToString()); // Compute payload signature member variable - if (!ed25519_sign( - &info.joinSplitPrivKey, - dataToBeSigned.begin(), 32, - &payloadSig)) - { - throw std::runtime_error("ed25519_sign failed"); - } + ed25519::sign( + info.joinSplitPrivKey, + {dataToBeSigned.begin(), 32}, + payloadSig); // Sanity check - if (!ed25519_verify( - &joinSplitPubKey, - &payloadSig, - dataToBeSigned.begin(), 32)) + if (!ed25519::verify( + joinSplitPubKey, + payloadSig, + {dataToBeSigned.begin(), 32})) { throw std::runtime_error("ed25519_verify failed"); } - std::string sigString = HexStr(payloadSig.bytes, payloadSig.bytes + ED25519_SIGNATURE_LEN); + std::string sigString = HexStr(payloadSig.bytes.begin(), payloadSig.bytes.end()); LogPrint("paymentdisclosure", "Payment Disclosure: signature = %s\n", sigString); } diff --git a/depend/zcash/src/wallet/paymentdisclosure.h b/depend/zcash/src/wallet/paymentdisclosure.h index 4e682b858..98dc8a3a7 100644 --- a/depend/zcash/src/wallet/paymentdisclosure.h +++ b/depend/zcash/src/wallet/paymentdisclosure.h @@ -18,7 +18,7 @@ #include #include -#include +#include // Ensure that the two different protocol messages, payment disclosure blobs and transactions, @@ -38,7 +38,7 @@ typedef JSOutPoint PaymentDisclosureKey; struct PaymentDisclosureInfo { uint8_t version; // 0 = experimental, 1 = first production version, etc. uint256 esk; // zcash/NoteEncryption.cpp - Ed25519SigningKey joinSplitPrivKey; // primitives/transaction.h + ed25519::SigningKey joinSplitPrivKey; // primitives/transaction.h // ed25519 - not tied to implementation e.g. libsodium, see ed25519 rfc libzcash::SproutPaymentAddress zaddr; @@ -46,7 +46,7 @@ struct PaymentDisclosureInfo { PaymentDisclosureInfo() : version(PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL) { } - PaymentDisclosureInfo(uint8_t v, uint256 esk, Ed25519SigningKey key, libzcash::SproutPaymentAddress zaddr) : version(v), esk(esk), joinSplitPrivKey(key), zaddr(zaddr) { } + PaymentDisclosureInfo(uint8_t v, uint256 esk, ed25519::SigningKey key, libzcash::SproutPaymentAddress zaddr) : version(v), esk(esk), joinSplitPrivKey(key), zaddr(zaddr) { } ADD_SERIALIZE_METHODS; @@ -64,10 +64,7 @@ struct PaymentDisclosureInfo { return ( a.version == b.version && a.esk == b.esk && - std::equal( - a.joinSplitPrivKey.bytes, - a.joinSplitPrivKey.bytes + ED25519_SIGNING_KEY_LEN, - b.joinSplitPrivKey.bytes) && + a.joinSplitPrivKey.bytes == b.joinSplitPrivKey.bytes && a.zaddr == b.zaddr); } @@ -123,13 +120,13 @@ struct PaymentDisclosurePayload { struct PaymentDisclosure { PaymentDisclosurePayload payload; - Ed25519Signature payloadSig; + ed25519::Signature payloadSig; // We use boost array because serialize doesn't like char buffer, otherwise we could do: unsigned char payloadSig[64]; PaymentDisclosure() {}; - PaymentDisclosure(const PaymentDisclosurePayload payload, const Ed25519Signature sig) : payload(payload), payloadSig(sig) {}; + PaymentDisclosure(const PaymentDisclosurePayload payload, const ed25519::Signature sig) : payload(payload), payloadSig(sig) {}; PaymentDisclosure( - const Ed25519VerificationKey& joinSplitPubKey, + const ed25519::VerificationKey& joinSplitPubKey, const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info, const std::string& message); @@ -147,10 +144,7 @@ struct PaymentDisclosure { friend bool operator==(const PaymentDisclosure& a, const PaymentDisclosure& b) { return ( a.payload == b.payload && - std::equal( - a.payloadSig.bytes, - a.payloadSig.bytes + ED25519_SIGNATURE_LEN, - b.payloadSig.bytes) + a.payloadSig.bytes == b.payloadSig.bytes ); } diff --git a/depend/zcash/src/wallet/rpcdisclosure.cpp b/depend/zcash/src/wallet/rpcdisclosure.cpp index 4bc0570d3..a8d4bc626 100644 --- a/depend/zcash/src/wallet/rpcdisclosure.cpp +++ b/depend/zcash/src/wallet/rpcdisclosure.cpp @@ -51,14 +51,14 @@ UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp) if (fHelp || params.size() < 3 || params.size() > 4 ) throw runtime_error( - "z_getpaymentdisclosure \"txid\" \"js_index\" \"output_index\" (\"message\") \n" + "z_getpaymentdisclosure \"txid\" js_index output_index (\"message\") \n" "\nGenerate a payment disclosure for a given joinsplit output.\n" "\nEXPERIMENTAL FEATURE\n" + disabledMsg + "\nArguments:\n" "1. \"txid\" (string, required) \n" - "2. \"js_index\" (string, required) \n" - "3. \"output_index\" (string, required) \n" + "2. js_index (numeric, required) \n" + "3. output_index (numeric, required) \n" "4. \"message\" (string, optional) \n" "\nResult:\n" "\"paymentdisclosure\" (string) Hex data string, with \"zpd:\" prefix.\n" @@ -101,7 +101,7 @@ UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp) // Check if shielded tx if (wtx.vJoinSplit.empty()) { - throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction"); + throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction"); } // Check js_index @@ -195,7 +195,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) // too much data is ignored, but if not enough data, exception of type ios_base::failure is thrown, // CBaseDataStream::read(): end of data: iostream error } catch (const std::exception &e) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure data is malformed."); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, payment disclosure data is malformed."); } if (pd.payload.marker != PAYMENT_DISCLOSURE_PAYLOAD_MAGIC_BYTES) { @@ -221,7 +221,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) // Check if shielded tx if (tx.vJoinSplit.empty()) { - throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction"); + throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not a shielded transaction"); } UniValue errs(UniValue::VARR); @@ -246,22 +246,22 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) // it is byte-flipped in the RPC output. uint256 joinSplitPubKey; std::copy( - tx.joinSplitPubKey.bytes, - tx.joinSplitPubKey.bytes + ED25519_VERIFICATION_KEY_LEN, + tx.joinSplitPubKey.bytes.begin(), + tx.joinSplitPubKey.bytes.end(), joinSplitPubKey.begin()); o.pushKV("joinSplitPubKey", joinSplitPubKey.ToString()); // Verify the payment disclosure was signed using the same key as the transaction i.e. the joinSplitPrivKey. uint256 dataToBeSigned = SerializeHash(pd.payload, SER_GETHASH, 0); - bool sigVerified = ed25519_verify( - &tx.joinSplitPubKey, - &pd.payloadSig, - dataToBeSigned.begin(), 32); + bool sigVerified = ed25519::verify( + tx.joinSplitPubKey, + pd.payloadSig, + {dataToBeSigned.begin(), 32}); o.pushKV("signatureVerified", sigVerified); if (!sigVerified) { - errs.push_back("Payment disclosure signature does not match transaction signature"); + errs.push_back("Payment disclosure signature does not match transaction signature"); } - + KeyIO keyIO(Params()); // Check the payment address is valid @@ -289,7 +289,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) string memoHexString = HexStr(npt.memo().data(), npt.memo().data() + npt.memo().size()); o.pushKV("memo", memoHexString); o.pushKV("value", ValueFromAmount(npt.value())); - + // Check the blockchain commitment matches decrypted note commitment uint256 cm_blockchain = jsdesc.commitments[pd.payload.n]; SproutNote note = npt.note(zaddr); diff --git a/depend/zcash/src/wallet/rpcdump.cpp b/depend/zcash/src/wallet/rpcdump.cpp index 5355a7305..63dc34478 100644 --- a/depend/zcash/src/wallet/rpcdump.cpp +++ b/depend/zcash/src/wallet/rpcdump.cpp @@ -929,7 +929,7 @@ UniValue z_exportkey(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); } - std::string result = std::visit(match { + std::string result = examine(address.value(), match { [&](const CKeyID& addr) { CKey key; if (pwalletMain->GetKey(addr, key)) { @@ -969,7 +969,7 @@ UniValue z_exportkey(const UniValue& params, bool fHelp) "Use the emergency recovery phrase for this wallet for backup purposes instead."); return std::string(); //unreachable, here to make the compiler happy } - }, address.value()); + }); return result; } diff --git a/depend/zcash/src/wallet/rpcwallet.cpp b/depend/zcash/src/wallet/rpcwallet.cpp index 6d0e7c266..e05104c80 100644 --- a/depend/zcash/src/wallet/rpcwallet.cpp +++ b/depend/zcash/src/wallet/rpcwallet.cpp @@ -34,9 +34,11 @@ #include "zcash/Address.hpp" #include "zcash/address/zip32.h" +#include "util/test.h" #include "util/time.h" #include "asyncrpcoperation.h" #include "asyncrpcqueue.h" +#include "wallet/asyncrpcoperation_common.h" #include "wallet/asyncrpcoperation_mergetoaddress.h" #include "wallet/asyncrpcoperation_saplingmigration.h" #include "wallet/asyncrpcoperation_sendmany.h" @@ -297,6 +299,79 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr } } +// We accept any `PrivacyPolicy` constructor name, but there is also a special +// “LegacyCompat” policy that maps to a different `PrivacyPolicy` depending on +// other aspects of the transaction. Here we use `std::nullopt` for the +// “LegacyCompat” case and resolve that when we have more context. +// +// We need to know the privacy policy before we construct the ZTXOSelector, but +// we can't determine what “LegacyCompat” maps to without knowing whether any +// UAs are involved. We break this cycle by parsing the privacy policy argument +// first, and then resolving “LegacyCompat” after parsing the rest of the +// arguments. This works because all interpretations for “LegacyCompat” have the +// same effect on ZTXOSelector construction (in that they don't include +// `AllowLinkingAccountAddresses`). +std::optional +ReifyPrivacyPolicy(const std::optional& defaultPolicy, + const std::optional& specifiedPolicy) +{ + std::optional strategy = std::nullopt; + if (specifiedPolicy.has_value()) { + auto strategyName = specifiedPolicy.value(); + if (strategyName == "LegacyCompat") { + strategy = std::nullopt; + } else { + strategy = TransactionStrategy::FromString(strategyName); + if (!strategy.has_value()) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("Unknown privacy policy name '%s'", strategyName)); + } + } + } else if (defaultPolicy.has_value()) { + strategy = TransactionStrategy(defaultPolicy.value()); + } + + return strategy; +} + +bool +InvolvesUnifiedAddress( + const std::optional& sender, + const std::set& recipients) +{ + bool hasUASender = + sender.has_value() && std::holds_alternative(sender.value()); + bool hasUARecipient = + std::find_if(recipients.begin(), recipients.end(), + [](const PaymentAddress& addr) { + return std::holds_alternative(addr); + }) + != recipients.end(); + + return hasUASender || hasUARecipient; +} + +// Determines which `TransactionStrategy` should be used for the “LegacyCompat” +// policy given the set of addresses involved. +PrivacyPolicy +InterpretLegacyCompat(const std::optional& sender, + const std::set& recipients) +{ + return !fEnableLegacyPrivacyStrategy || InvolvesUnifiedAddress(sender, recipients) + ? PrivacyPolicy::FullPrivacy + : PrivacyPolicy::AllowFullyTransparent; +} + +// Provides the final `TransactionStrategy` to be used for a transaction. +TransactionStrategy +ResolveTransactionStrategy( + const std::optional& maybeStrategy, + PrivacyPolicy defaultPolicy) +{ + return maybeStrategy.value_or(TransactionStrategy(defaultPolicy)); +} + UniValue sendtoaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -447,19 +522,23 @@ UniValue listaddresses(const UniValue& params, bool fHelp) std::set t_mnemonic_change_dests; std::set t_imported_dests; std::set t_watchonly_dests; + + auto GetSourceForDestination = match { + [&](const CKeyID& addr) -> std::optional { + return GetSourceForPaymentAddress(pwalletMain)(addr); + }, + [&](const CScriptID& addr) -> std::optional { + return GetSourceForPaymentAddress(pwalletMain)(addr); + }, + [&](const CNoDestination& addr) -> std::optional { + return std::nullopt; + }, + }; + // Get the CTxDestination values for all the entries in the transparent address book. // This will include any address that has been generated by this wallet. for (const std::pair& item : pwalletMain->mapAddressBook) { - std::optional source; - std::visit(match { - [&](const CKeyID& addr) { - source = GetSourceForPaymentAddress(pwalletMain)(addr); - }, - [&](const CScriptID& addr) { - source = GetSourceForPaymentAddress(pwalletMain)(addr); - }, - [&](const CNoDestination& addr) {} - }, item.first); + std::optional source = std::visit(GetSourceForDestination, item.first); if (source.has_value()) { switch (source.value()) { case PaymentAddressSource::Random: @@ -491,16 +570,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp) t_imported_dests.count(item.first) == 0 && t_watchonly_dests.count(item.first) == 0) { - std::optional source; - std::visit(match { - [&](const CKeyID& addr) { - source = GetSourceForPaymentAddress(pwalletMain)(addr); - }, - [&](const CScriptID& addr) { - source = GetSourceForPaymentAddress(pwalletMain)(addr); - }, - [&](const CNoDestination& addr) {} - }, item.first); + std::optional source = std::visit(GetSourceForDestination, item.first); if (source.has_value()) { switch (source.value()) { case PaymentAddressSource::Random: @@ -2339,9 +2409,9 @@ UniValue settxfee(const UniValue& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 1) throw runtime_error( "settxfee amount\n" - "\nSet the transaction fee per kB. Overwrites the paytxfee parameter.\n" + "\nSet the preferred transaction fee rate per 1000 bytes. This is only used by legacy transaction creation APIs (sendtoaddress, sendmany, and fundrawtransaction). Overwrites the paytxfee parameter.\n" "\nArguments:\n" - "1. amount (numeric, required) The transaction fee in " + CURRENCY_UNIT + "/kB rounded to the nearest 0.00000001\n" + "1. amount (numeric, required) The transaction fee rate in " + CURRENCY_UNIT + " per 1000 bytes rounded to the nearest 0.00000001\n" "\nResult\n" "true|false (boolean) Returns true if successful\n" "\nExamples:\n" @@ -2385,7 +2455,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) " \"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" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" + " \"paytxfee\": x.xxxx, (numeric) the preferred transaction fee rate used for transactions created by legacy APIs, set in " + CURRENCY_UNIT + " per 1000 bytes\n" " \"mnemonic_seedfp\": \"uint256\", (string) the BLAKE2b-256 hash of the HD seed derived from the wallet's emergency recovery phrase\n" " \"legacy_seedfp\": \"uint256\", (string, optional) if this wallet was created prior to release 4.5.2, this will contain the BLAKE2b-256\n" " hash of the legacy HD seed that was used to derive Sapling addresses prior to the 4.5.2 upgrade to mnemonic\n" @@ -2867,7 +2937,7 @@ UniValue zc_sample_joinsplit(const UniValue& params, bool fHelp) LOCK(cs_main); - Ed25519VerificationKey joinSplitPubKey; + ed25519::VerificationKey joinSplitPubKey; uint256 anchor = SproutMerkleTree().root(); std::array inputs({JSInput(), JSInput()}); std::array outputs({JSOutput(), JSOutput()}); @@ -3217,7 +3287,7 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); result.pushKV("account", (uint64_t)account); - std::visit(match { + examine(res, match { [&](std::pair addr) { result.pushKV("address", KeyIO(Params()).EncodePaymentAddress(addr.first)); UniValue j; @@ -3270,7 +3340,7 @@ UniValue z_getaddressforaccount(const UniValue& params, bool fHelp) } throw JSONRPCError(RPC_WALLET_ERROR, strErr); }, - }, res); + }); UniValue receiver_types(UniValue::VARR); for (const auto& receiverType : receiverTypes) { @@ -3464,7 +3534,7 @@ UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); for (const auto& receiver : ua) { - std::visit(match { + examine(receiver, match { [&](const libzcash::OrchardRawAddress& addr) { // Create a single-receiver UA that just contains this Orchard receiver. UnifiedAddress singleReceiver; @@ -3481,7 +3551,7 @@ UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp) result.pushKV("p2pkh", keyIO.EncodePaymentAddress(addr)); }, [](auto rest) {}, - }, receiver); + }); } return result; } @@ -3618,7 +3688,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) // A non-unified address argument that is a receiver within a // unified address known to this wallet is not allowed. - if (std::visit(match { + if (examine(decoded.value(), match { [&](const CKeyID& addr) { return pwalletMain->FindUnifiedAddressByReceiver(addr).has_value(); }, @@ -3636,7 +3706,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) // We allow unified addresses themselves, which cannot recurse. return false; } - }, decoded.value())) { + })) { throw JSONRPCError(RPC_INVALID_PARAMETER, "The provided address is a bare receiver from a Unified Address in this wallet. Provide the full UA instead."); } @@ -3741,7 +3811,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) } }; - std::visit(match { + examine(decoded.value(), match { [&](const CKeyID& addr) { push_transparent_result(addr); }, [&](const CScriptID& addr) { push_transparent_result(addr); }, [&](const libzcash::SproutPaymentAddress& addr) { @@ -3778,7 +3848,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) }, [&](const libzcash::UnifiedAddress& addr) { for (const auto& receiver : addr) { - std::visit(match { + examine(receiver, match { [&](const libzcash::SaplingPaymentAddress& addr) { push_sapling_result(addr); }, @@ -3795,10 +3865,10 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) }, [&](const UnknownReceiver& unknown) {} - }, receiver); + }); } } - }, decoded.value()); + }); return result; } @@ -3855,7 +3925,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) } CAmount nBalance = 0; - std::visit(match { + examine(pa.value(), match { [&](const CKeyID& addr) { nBalance = getBalanceTaddr(addr, std::nullopt, nMinDepth, false); }, @@ -3869,13 +3939,13 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) nBalance = getBalanceZaddr(addr, std::nullopt, nMinDepth, INT_MAX, false); }, [&](const libzcash::UnifiedAddress& addr) { - auto selector = pwalletMain->ZTXOSelectorForAddress(addr, true, false); + auto selector = pwalletMain->ZTXOSelectorForAddress(addr, true, TransparentCoinbasePolicy::Allow, false); if (!selector.has_value()) { throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, "Unified address does not correspond to an account in the wallet"); } - auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, nMinDepth, std::nullopt); + auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), nMinDepth, std::nullopt); for (const auto& t : spendableInputs.utxos) { nBalance += t.Value(); @@ -3887,7 +3957,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) nBalance += t.GetNoteValue(); } }, - }, pa.value()); + }); // inZat if (params.size() > 2 && params[2].get_bool()) { @@ -3960,14 +4030,14 @@ UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp) // FVKs make it possible to correctly determine balance without having the // spending key, so we permit that here. bool requireSpendingKey = std::holds_alternative(fvk); - auto selector = pwalletMain->ZTXOSelectorForViewingKey(fvk, requireSpendingKey); + auto selector = pwalletMain->ZTXOSelectorForViewingKey(fvk, requireSpendingKey, TransparentCoinbasePolicy::Allow); if (!selector.has_value()) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Error: the wallet does not recognize the specified viewing key."); } - auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, minconf, asOfHeight); + auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), minconf, asOfHeight); CAmount transparentBalance = 0; CAmount sproutBalance = 0; @@ -4058,14 +4128,14 @@ UniValue z_getbalanceforaccount(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Get the receivers for this account. - auto selector = pwalletMain->ZTXOSelectorForAccount(account, false); + auto selector = pwalletMain->ZTXOSelectorForAccount(account, false, TransparentCoinbasePolicy::Allow); if (!selector.has_value()) { throw JSONRPCError( RPC_INVALID_PARAMETER, tfm::format("Error: account %d has not been generated by z_getnewaccount.", account)); } - auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, minconf, asOfHeight); + auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), minconf, asOfHeight); // Accounts never contain Sprout notes. assert(spendableInputs.sproutNoteEntries.empty()); @@ -4191,7 +4261,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " \"actionPrev\" : n, (numeric, orchard) the index of the action within the orchard bundle\n" " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"valueZat\" : xxxx (numeric) The amount in zatoshis\n" + " \"valueZat\" : xxxx (numeric) The amount in " + MINOR_CURRENCY_UNIT + "\n" " }\n" " ,...\n" " ],\n" @@ -4207,7 +4277,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " \"outgoing\" : true|false (boolean) True if the output is not for an address in the wallet\n" " \"walletInternal\" : true|false (boolean) True if this is a change output.\n" " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"valueZat\" : xxxx (numeric) The amount in zatoshis\n" + " \"valueZat\" : xxxx (numeric) The amount in " + MINOR_CURRENCY_UNIT + "\n" " \"memo\" : \"hexmemo\", (string) hexadecimal string representation of the memo field\n" " \"memoStr\" : \"memo\", (string) Only returned if memo contains valid UTF-8 text.\n" " }\n" @@ -4361,12 +4431,13 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) auto op = res->second; auto wtxPrev = pwalletMain->mapWallet.at(op.hash); - // We don't need to check the leadbyte here: if wtx exists in + // We don't need to constrain the note plaintext lead byte + // to satisfy the ZIP 212 grace window: if wtx exists in // the wallet, it must have been successfully decrypted. This - // means the plaintext leadbyte was valid at the block height - // where the note was received. - // https://zips.z.cash/zip-0212#changes-to-the-process-of-receiving-sapling-notes - auto decrypted = wtxPrev.DecryptSaplingNoteWithoutLeadByteCheck(op).value(); + // means the note plaintext lead byte was valid at the block + // height where the note was received. + // https://zips.z.cash/zip-0212#changes-to-the-process-of-receiving-sapling-or-orchard-notes + auto decrypted = wtxPrev.DecryptSaplingNote(Params(), op).value(); auto notePt = decrypted.first; auto pa = decrypted.second; @@ -4407,19 +4478,20 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) SaplingPaymentAddress pa; bool isOutgoing; - // We don't need to check the leadbyte here: if wtx exists in + // We don't need to constrain the note plaintext lead byte + // to satisfy the ZIP 212 grace window: if wtx exists in // the wallet, it must have been successfully decrypted. This - // means the plaintext leadbyte was valid at the block height - // where the note was received. - // https://zips.z.cash/zip-0212#changes-to-the-process-of-receiving-sapling-notes - auto decrypted = wtx.DecryptSaplingNoteWithoutLeadByteCheck(op); + // means the note plaintext lead byte was valid at the block + // height where the note was received. + // https://zips.z.cash/zip-0212#changes-to-the-process-of-receiving-sapling-or-orchard-notes + auto decrypted = wtx.DecryptSaplingNote(Params(), op); if (decrypted) { notePt = decrypted->first; pa = decrypted->second; isOutgoing = false; } else { // Try recovering the output - auto recovered = wtx.RecoverSaplingNoteWithoutLeadByteCheck(op, ovks); + auto recovered = wtx.RecoverSaplingNote(Params(), op, ovks); if (recovered) { notePt = recovered->first; pa = recovered->second; @@ -4629,13 +4701,9 @@ UniValue z_getoperationstatus_IMPL(const UniValue& params, bool fRemoveFinishedO return ret; } -// transaction.h comment: spending taddr output requires CTxIn >= 148 bytes and typical taddr txout is 34 bytes -#define CTXIN_SPEND_DUST_SIZE 148 -#define CTXOUT_REGULAR_SIZE 34 - size_t EstimateTxSize( const ZTXOSelector& ztxoSelector, - const std::vector& recipients, + const std::vector& recipients, int nextBlockHeight) { CMutableTransaction mtx; mtx.fOverwintered = true; @@ -4649,8 +4717,8 @@ size_t EstimateTxSize( size_t txsize = 0; size_t taddrRecipientCount = 0; size_t orchardRecipientCount = 0; - for (const ResolvedPayment& recipient : recipients) { - std::visit(match { + for (const Payment& recipient : recipients) { + examine(recipient.GetAddress(), match { [&](const CKeyID&) { taddrRecipientCount += 1; }, @@ -4658,22 +4726,24 @@ size_t EstimateTxSize( taddrRecipientCount += 1; }, [&](const libzcash::SaplingPaymentAddress& addr) { - mtx.vShieldedOutput.push_back(OutputDescription()); + mtx.vShieldedOutput.push_back(RandomInvalidOutputDescription()); }, [&](const libzcash::SproutPaymentAddress& addr) { JSDescription jsdesc; jsdesc.proof = GrothProof(); mtx.vJoinSplit.push_back(jsdesc); }, - [&](const libzcash::OrchardRawAddress& addr) { - if (fromSprout) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Sending funds from a Sprout address to a Unified Address is not supported by z_sendmany"); + [&](const libzcash::UnifiedAddress& addr) { + if (addr.GetOrchardReceiver().has_value()) { + orchardRecipientCount += 1; + } else if (addr.GetSaplingReceiver().has_value()) { + mtx.vShieldedOutput.push_back(RandomInvalidOutputDescription()); + } else if (addr.GetP2PKHReceiver().has_value() + || addr.GetP2SHReceiver().has_value()) { + taddrRecipientCount += 1; } - orchardRecipientCount += 1; } - }, recipient.address); + }); } bool nu5Active = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_NU5); @@ -4689,7 +4759,7 @@ size_t EstimateTxSize( CTransaction tx(mtx); txsize += GetSerializeSize(tx, SER_NETWORK, tx.nVersion); if (fromTaddr) { - txsize += CTXIN_SPEND_DUST_SIZE; + txsize += CTXIN_SPEND_P2PKH_SIZE; txsize += CTXOUT_REGULAR_SIZE; // There will probably be taddr change } txsize += CTXOUT_REGULAR_SIZE * taddrRecipientCount; @@ -4703,6 +4773,37 @@ size_t EstimateTxSize( return txsize; } +static std::optional ParseMemo(const UniValue& memoValue) +{ + if (memoValue.isNull()) { + return std::nullopt; + } else { + return examine(Memo::FromHex(memoValue.get_str()), match { + [](MemoError err) -> std::optional { + switch (err) { + case MemoError::HexDecodeError: + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "Invalid parameter, expected memo data in hexadecimal format."); + case MemoError::MemoTooLong: + throw JSONRPCError( + RPC_INVALID_PARAMETER, + strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); + default: + assert(false); + } + }, + [](Memo result) -> std::optional { + if (result.ToBytes() == Memo::NoMemo().ToBytes()) { + return std::nullopt; + } else { + return result; + } + } + }); + } +} + UniValue z_sendmany(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -4731,10 +4832,12 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) " [{\n" " \"address\":address (string, required) The address is a taddr, zaddr, or Unified Address\n" " \"amount\":amount (numeric, required) The numeric amount in " + CURRENCY_UNIT + " is the value\n" - " \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format\n" + " \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format. If\n" + " the output is being sent to a transparent address, it’s an error to include this field.\n" " }, ... ]\n" "3. minconf (numeric, optional, default=" + strprintf("%u", DEFAULT_NOTE_CONFIRMATIONS) + ") Only use funds confirmed at least this many times.\n" - "4. fee (numeric, optional, default=" + strprintf("%s", FormatMoney(DEFAULT_FEE)) + ") The fee amount to attach to this transaction.\n" + "4. fee (numeric, optional, default=null) The fee amount in " + CURRENCY_UNIT + " to attach to this transaction. The default behavior\n" + " is to use a fee calculated according to ZIP 317.\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 value pool).\n" @@ -4760,6 +4863,8 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) "\nExamples:\n" + HelpExampleCli("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" '[{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\", \"amount\": 5.0}]'") + HelpExampleCli("z_sendmany", "\"ANY_TADDR\" '[{\"address\": \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"amount\": 2.0}]'") + + HelpExampleCli("z_sendmany", "\"ANY_TADDR\" '[{\"address\": \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"amount\": 2.0}]' 1 null 'AllowFullyTransparent'") + + HelpExampleCli("z_sendmany", "\"ANY_TADDR\" '[{\"address\": \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"amount\": 2.0}]' 1 5000") + HelpExampleRpc("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", [{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\", \"amount\": 5.0}]") ); @@ -4776,85 +4881,14 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) KeyIO keyIO(chainparams); - // We need to know the privacy policy before we construct the ZTXOSelector, - // but we can't determine the default privacy policy without knowing whether - // any UAs are involved. We break this cycle by parsing the privacy policy - // argument first, and then resolving it to the default after parsing the - // rest of the arguments. This works because all possible defaults for the - // privacy policy have the same effect on ZTXOSelector construction (in that - // they don't include AllowLinkingAccountAddresses). - std::optional maybeStrategy; - if (params.size() > 4) { - auto strategyName = params[4].get_str(); - if (strategyName != "LegacyCompat") { - maybeStrategy = TransactionStrategy::FromString(strategyName); - if (!maybeStrategy.has_value()) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - strprintf("Unknown privacy policy name '%s'", strategyName)); - } - } - } - - bool involvesUnifiedAddress = false; - bool involvesOrchard = false; - - // Check that the from address is valid. - // Unified address (UA) allowed here (#5185) - auto fromaddress = params[0].get_str(); - ZTXOSelector ztxoSelector = [&]() { - if (fromaddress == "ANY_TADDR") { - return CWallet::LegacyTransparentZTXOSelector(true); - } else { - auto decoded = keyIO.DecodePaymentAddress(fromaddress); - if (!decoded.has_value()) { - throw JSONRPCError( - RPC_INVALID_ADDRESS_OR_KEY, - "Invalid from address: should be a taddr, zaddr, UA, or the string 'ANY_TADDR'."); - } - - auto ztxoSelectorOpt = pwalletMain->ZTXOSelectorForAddress( - decoded.value(), - true, - // LegacyCompat does not include AllowLinkingAccountAddresses. - maybeStrategy.has_value() ? maybeStrategy.value().AllowLinkingAccountAddresses() : false); - if (!ztxoSelectorOpt.has_value()) { - throw JSONRPCError( - RPC_INVALID_ADDRESS_OR_KEY, - "Invalid from address, no payment source found for address."); - } - - auto selectorAccount = pwalletMain->FindAccountForSelector(ztxoSelectorOpt.value()); - std::visit(match { - [&](const libzcash::UnifiedAddress& ua) { - if (!selectorAccount.has_value() || selectorAccount.value() == ZCASH_LEGACY_ACCOUNT) { - throw JSONRPCError( - RPC_INVALID_ADDRESS_OR_KEY, - "Invalid from address, UA does not correspond to a known account."); - } - involvesUnifiedAddress = true; - involvesOrchard = ua.GetOrchardReceiver().has_value(); - }, - [&](const auto& other) { - if (selectorAccount.has_value() && selectorAccount.value() != ZCASH_LEGACY_ACCOUNT) { - throw JSONRPCError( - RPC_INVALID_ADDRESS_OR_KEY, - "Invalid from address: is a bare receiver from a Unified Address in this wallet. Provide the UA as returned by z_getaddressforaccount instead."); - } - } - }, decoded.value()); - - return ztxoSelectorOpt.value(); - } - }(); - UniValue outputs = params[1].get_array(); if (outputs.size() == 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amounts array is empty."); } - std::set recipientAddrs; - std::vector recipients; + std::set recipientAddrs; + std::vector recipients; + bool hasTransparentRecipient = false; CAmount nTotalOut = 0; size_t nOrchardOutputs = 0; for (const UniValue& o : outputs.getValues()) { @@ -4868,63 +4902,18 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) } std::string addrStr = find_value(o, "address").get_str(); - auto decoded = keyIO.DecodePaymentAddress(addrStr); - if (!decoded.has_value()) { + auto addr = keyIO.DecodePaymentAddress(addrStr); + if (!addr.has_value()) { throw JSONRPCError( RPC_INVALID_PARAMETER, std::string("Invalid parameter, unknown address format: ") + addrStr); } - std::optional addr = std::visit( - SelectRecipientAddress(chainparams.GetConsensus(), nextBlockHeight), - decoded.value()); - if (!addr.has_value()) { - bool toSprout = std::holds_alternative(decoded.value()); - if (toSprout) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Sending funds into the Sprout value pool is not supported by z_sendmany"); - } else { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Unified address contained only receiver types that are unrecognized or for which the required consensus feature is not yet active."); - } - } - if (!recipientAddrs.insert(addr.value()).second) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated recipient address: ") + addrStr); } - UniValue memoValue = find_value(o, "memo"); - std::optional memo; - if (!memoValue.isNull()) { - auto memoHex = memoValue.get_str(); - if (!std::visit(libzcash::IsShieldedRecipient(), addr.value())) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Invalid parameter, memos cannot be sent to transparent addresses."); - } - - std::visit(match { - [&](MemoError err) { - switch (err) { - case MemoError::HexDecodeError: - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Invalid parameter, expected memo data in hexadecimal format."); - case MemoError::MemoTooLong: - throw JSONRPCError( - RPC_INVALID_PARAMETER, - strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); - default: - assert(false); - } - }, - [&](Memo result) { - memo = result; - } - }, Memo::FromHex(memoHex)); - } + auto memo = ParseMemo(find_value(o, "memo")); UniValue av = find_value(o, "amount"); CAmount nAmount = AmountFromValue( av ); @@ -4932,44 +4921,90 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive"); } - std::optional ua = std::nullopt; - if (std::holds_alternative(decoded.value())) { - ua = std::get(decoded.value()); - involvesUnifiedAddress = true; - involvesOrchard = involvesOrchard || ua.value().GetOrchardReceiver().has_value(); - } - - if (std::holds_alternative(addr.value())) { - nOrchardOutputs += 1; - if (nOrchardOutputs > nOrchardActionLimit) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - strprintf( - "Attempting to create %u Orchard outputs would exceed the current limit " - "of %u notes, which exists to prevent memory exhaustion. Restart with " - "`-orchardactionlimit=N` where N >= %u to allow the wallet to attempt " - "to construct this transaction.", - nOrchardOutputs, - nOrchardActionLimit, - nOrchardOutputs)); - } - } + hasTransparentRecipient = hasTransparentRecipient || examine(addr.value(), match { + [](const CKeyID &) { return true; }, + [](const CScriptID &) { return true; }, + [&](const UnifiedAddress &ua) { + auto preferredRecipient = + ua.GetPreferredRecipientAddress(chainparams.GetConsensus(), nextBlockHeight); + return preferredRecipient.has_value() + && examine(preferredRecipient.value(), match { + [](const CKeyID &) { return true; }, + [](const CScriptID &) { return true; }, + [](const auto &) { return false; } + }); + }, + [](const auto &) { return false; } + }); - recipients.push_back(ResolvedPayment(ua, addr.value(), nAmount, memo)); + recipients.push_back(Payment(addr.value(), nAmount, memo)); nTotalOut += nAmount; } if (recipients.empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "No recipients"); } - // Now that we've set involvesUnifiedAddress correctly, we can finish - // evaluating the strategy. - TransactionStrategy strategy = maybeStrategy.value_or( - // Default privacy policy is "LegacyCompat". - (involvesUnifiedAddress || !fEnableLegacyPrivacyStrategy) ? - TransactionStrategy(PrivacyPolicy::FullPrivacy) : - TransactionStrategy(PrivacyPolicy::AllowFullyTransparent) - ); + // Check that the from address is valid. + // Unified address (UA) allowed here (#5185) + auto fromaddress = params[0].get_str(); + std::optional sender; + if (fromaddress != "ANY_TADDR") { + auto decoded = keyIO.DecodePaymentAddress(fromaddress); + if (decoded.has_value()) { + sender = decoded.value(); + } else { + throw JSONRPCError( + RPC_INVALID_ADDRESS_OR_KEY, + "Invalid from address: should be a taddr, zaddr, UA, or the string 'ANY_TADDR'."); + } + } + + auto strategy = + ResolveTransactionStrategy( + ReifyPrivacyPolicy( + std::nullopt, + params.size() > 4 ? std::optional(params[4].get_str()) : std::nullopt), + InterpretLegacyCompat(sender, recipientAddrs)); + + auto ztxoSelector = [&]() { + if (!sender.has_value()) { + return CWallet::LegacyTransparentZTXOSelector(true, TransparentCoinbasePolicy::Disallow); + } else { + auto ztxoSelectorOpt = pwalletMain->ZTXOSelectorForAddress( + sender.value(), + true, + strategy.AllowRevealedSenders() && !hasTransparentRecipient + ? TransparentCoinbasePolicy::Allow + : TransparentCoinbasePolicy::Disallow, + strategy.AllowLinkingAccountAddresses()); + if (!ztxoSelectorOpt.has_value()) { + throw JSONRPCError( + RPC_INVALID_ADDRESS_OR_KEY, + "Invalid from address, no payment source found for address."); + } + + auto selectorAccount = pwalletMain->FindAccountForSelector(ztxoSelectorOpt.value()); + bool unknownOrLegacy = !selectorAccount.has_value() || selectorAccount.value() == ZCASH_LEGACY_ACCOUNT; + examine(sender.value(), match { + [&](const libzcash::UnifiedAddress& ua) { + if (unknownOrLegacy) { + throw JSONRPCError( + RPC_INVALID_ADDRESS_OR_KEY, + "Invalid from address, UA does not correspond to a known account."); + } + }, + [&](const auto& other) { + if (!unknownOrLegacy) { + throw JSONRPCError( + RPC_INVALID_ADDRESS_OR_KEY, + "Invalid from address: is a bare receiver from a Unified Address in this wallet. Provide the UA as returned by z_getaddressforaccount instead."); + } + } + }); + + return ztxoSelectorOpt.value(); + } + }(); // Sanity check for transaction size // TODO: move this to the builder? @@ -4983,32 +5018,9 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) // Minimum confirmations int nMinDepth = parseMinconf(DEFAULT_NOTE_CONFIRMATIONS, params, 2, std::nullopt); - // Fee in Zatoshis, not currency format) - CAmount nFee = DEFAULT_FEE; - if (params.size() > 3) { - if (params[3].get_real() == 0.0) { - nFee = 0; - } else { - nFee = AmountFromValue( params[3] ); - } - - // Check that the user specified fee is not absurd. - // This allows amount=0 (and all amount < DEFAULT_FEE) transactions to use the default network fee - // or anything less than DEFAULT_FEE instead of being forced to use a custom fee and leak metadata - if (nTotalOut < DEFAULT_FEE) { - if (nFee > DEFAULT_FEE) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - strprintf("Small transaction amount %s has fee %s that is greater than the default fee %s", FormatMoney(nTotalOut), FormatMoney(nFee), FormatMoney(DEFAULT_FEE))); - } - } else { - // Check that the user specified fee is not absurd. - if (nFee > nTotalOut) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - strprintf("Fee %s is greater than the sum of outputs %s and also greater than the default fee", FormatMoney(nFee), FormatMoney(nTotalOut))); - } - } + std::optional nFee; + if (params.size() > 3 && !params[3].isNull()) { + nFee = AmountFromValue( params[3] ); } // Use input parameters as the optional context info to be returned by z_getoperationstatus and z_getoperationresult. @@ -5016,25 +5028,15 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) o.pushKV("fromaddress", params[0]); o.pushKV("amounts", params[1]); o.pushKV("minconf", nMinDepth); - o.pushKV("fee", std::stod(FormatMoney(nFee))); + if (nFee.has_value()) { + o.pushKV("fee", ValueFromAmount(nFee.value())); + } UniValue contextInfo = o; - std::optional orchardAnchor; + // Create operation and add to global queue auto nAnchorDepth = std::min((unsigned int) nMinDepth, nAnchorConfirmations); - if ((ztxoSelector.SelectsOrchard() || nOrchardOutputs > 0) && nAnchorDepth == 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot select Orchard notes or send to Orchard recipients when minconf=0."); - } - if (!ztxoSelector.SelectsSprout() && (involvesOrchard || nPreferredTxVersion >= ZIP225_MIN_TX_VERSION) && nAnchorDepth > 0) { - auto orchardAnchorHeight = nextBlockHeight - nAnchorDepth; - if (chainparams.GetConsensus().NetworkUpgradeActive(orchardAnchorHeight, Consensus::UPGRADE_NU5)) { - auto anchorBlockIndex = chainActive[orchardAnchorHeight]; - assert(anchorBlockIndex != nullptr); - orchardAnchor = anchorBlockIndex->hashFinalOrchardRoot; - } - } - TransactionBuilder builder(chainparams.GetConsensus(), nextBlockHeight, orchardAnchor, pwalletMain); + WalletTxBuilder builder(chainparams, minRelayTxFee); - // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); std::shared_ptr operation( new AsyncRPCOperation_sendmany( @@ -5177,29 +5179,14 @@ UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) { return migrationStatus; } -/** -When estimating the number of coinbase utxos we can shield in a single transaction: -1. Joinsplit description is 1802 bytes. -2. Transaction overhead ~ 100 bytes -3. Spending a typical P2PKH is >=148 bytes, as defined in CTXIN_SPEND_DUST_SIZE. -4. Spending a multi-sig P2SH address can vary greatly: - https://github.com/bitcoin/bitcoin/blob/c3ad56f4e0b587d8d763af03d743fdfc2d180c9b/src/main.cpp#L517 - In real-world coinbase utxos, we consider a 3-of-3 multisig, where the size is roughly: - (3*(33+1))+3 = 105 byte redeem script - 105 + 1 + 3*(73+1) = 328 bytes of scriptSig, rounded up to 400 based on testnet experiments. -*/ -#define CTXIN_SPEND_P2SH_SIZE 400 - -#define SHIELD_COINBASE_DEFAULT_LIMIT 50 - UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() < 2 || params.size() > 4) + if (fHelp || params.size() < 2 || params.size() > 6) throw runtime_error( - "z_shieldcoinbase \"fromaddress\" \"tozaddress\" ( fee ) ( limit )\n" + "z_shieldcoinbase \"fromaddress\" \"tozaddress\" ( fee ) ( limit ) ( memo ) ( privacyPolicy )\n" "\nShield transparent coinbase funds by sending to a shielded zaddr. This is an asynchronous operation and utxos" "\nselected for shielding will be locked. If there is an error, they are unlocked. The RPC call `listlockunspent`" "\ncan be used to return a list of locked utxos. The number of coinbase utxos selected for shielding can be limited" @@ -5210,16 +5197,20 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) "\nArguments:\n" "1. \"fromaddress\" (string, required) The address is a taddr or \"*\" for all taddrs belonging to the wallet.\n" "2. \"toaddress\" (string, required) The address is a zaddr.\n" - "3. fee (numeric, optional, default=" - + strprintf("%s", FormatMoney(DEFAULT_FEE)) + ") The fee amount to attach to this transaction.\n" + "3. fee (numeric, optional, default=null) The fee amount in " + CURRENCY_UNIT + " to attach to this transaction. The default behavior\n" + " is to use a fee calculated according to ZIP 317.\n" "4. limit (numeric, optional, default=" + strprintf("%d", SHIELD_COINBASE_DEFAULT_LIMIT) + ") Limit on the maximum number of utxos to shield. Set to 0 to use as many as will fit in the transaction.\n" + "5. \"memo\" (string, optional) Encoded as hex. This will be stored in the memo field of the new note.\n" + "6. privacyPolicy (string, optional, default=\"AllowRevealedSenders\") Policy for what information leakage is acceptable.\n" + " This allows the same values as z_sendmany, but only \"AllowRevealedSenders\" and \"AllowLinkingAccountAddresses\"\n" + " are relevant.\n" "\nResult:\n" "{\n" - " \"remainingUTXOs\": xxx (numeric) Number of coinbase utxos still available for shielding.\n" - " \"remainingValue\": xxx (numeric) Value of coinbase utxos still available for shielding.\n" - " \"shieldingUTXOs\": xxx (numeric) Number of coinbase utxos being shielded.\n" - " \"shieldingValue\": xxx (numeric) Value of coinbase utxos being shielded.\n" + " \"remainingUTXOs\": xxx (numeric) Number of coinbase utxos still available for shielding.\n" + " \"remainingValue\": xxx (numeric) Value of coinbase utxos still available for shielding.\n" + " \"shieldingUTXOs\": xxx (numeric) Number of coinbase utxos being shielded.\n" + " \"shieldingValue\": xxx (numeric) Value of coinbase utxos being shielded.\n" " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" "}\n" "\nExamples:\n" @@ -5230,6 +5221,30 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); ThrowIfInitialBlockDownload(); + const auto chainparams = Params(); + const auto consensus = chainparams.GetConsensus(); + int nextBlockHeight = chainActive.Height() + 1; + + // This API cannot be used to create coinbase shielding transactions before Sapling + // activation. + if (!consensus.NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_SAPLING)) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, "Cannot create shielded transactions before Sapling has activated"); + } + + std::optional memo; + if (params.size() > 4) { + memo = ParseMemo(params[4]); + } + + auto strategy = + ResolveTransactionStrategy( + ReifyPrivacyPolicy( + PrivacyPolicy::AllowRevealedSenders, + params.size() > 5 ? std::optional(params[5].get_str()) : std::nullopt), + // This has identical behavior to `AllowRevealedSenders` for this operation, but + // technically, this is what “LegacyCompat” means, so just for consistency. + PrivacyPolicy::AllowFullyTransparent); // Validate the from address auto fromaddress = params[0].get_str(); @@ -5238,198 +5253,98 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); // Set of source addresses to filter utxos by - std::set sources = {}; - if (!isFromWildcard) { - CTxDestination taddr = keyIO.DecodeDestination(fromaddress); - if (IsValidDestination(taddr)) { - sources.insert(taddr); + ZTXOSelector ztxoSelector = [&]() { + if (isFromWildcard) { + return CWallet::LegacyTransparentZTXOSelector(true, TransparentCoinbasePolicy::Require); } else { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or \"*\"."); - } - } - - int nextBlockHeight = chainActive.Height() + 1; - const bool canopyActive = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_CANOPY); - - // Validate the destination address - auto destStr = params[1].get_str(); - auto destaddress = keyIO.DecodePaymentAddress(destStr); - if (destaddress.has_value()) { - std::visit(match { - [&](const CKeyID& addr) { - throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2pkh address."); - }, - [&](const CScriptID&) { - throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2sh address."); - }, - [&](const libzcash::SaplingPaymentAddress& addr) { - // OK - }, - [&](const libzcash::SproutPaymentAddress& addr) { - if (canopyActive) { - throw JSONRPCError(RPC_VERIFY_REJECTED, "Sprout shielding is not supported after Canopy activation"); - } - }, - [&](const libzcash::UnifiedAddress& ua) { - if (!ua.GetSaplingReceiver().has_value()) { - throw JSONRPCError( - RPC_VERIFY_REJECTED, - "Only Sapling shielding is currently supported by z_shieldcoinbase. " - "Use z_sendmany with a transaction amount that results in no change for Orchard shielding."); - } - involvesOrchard = ua.GetOrchardReceiver().has_value(); + auto decoded = keyIO.DecodePaymentAddress(fromaddress); + if (!decoded.has_value()) { + throw JSONRPCError( + RPC_INVALID_ADDRESS_OR_KEY, + "Invalid from address: should be a taddr, zaddr, UA, or the string 'ANY_TADDR'."); } - }, destaddress.value()); - } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destStr); - } - // Convert fee from currency format to zatoshis - CAmount nFee = DEFAULT_FEE; - if (params.size() > 2) { - if (params[2].get_real() == 0.0) { - nFee = 0; - } else { - nFee = AmountFromValue( params[2] ); - } - } - - int nLimit = SHIELD_COINBASE_DEFAULT_LIMIT; - if (params.size() > 3) { - nLimit = params[3].get_int(); - if (nLimit < 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of utxos cannot be negative"); - } - } - - const bool saplingActive = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_SAPLING); - - // We cannot create shielded transactions before Sapling activates. - if (!saplingActive) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, "Cannot create shielded transactions before Sapling has activated"); - } - - bool overwinterActive = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_OVERWINTER); - assert(overwinterActive); - unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING; - - // Prepare to get coinbase utxos - std::vector inputs; - CAmount shieldedValue = 0; - CAmount remainingValue = 0; - size_t estimatedTxSize = 2000; // 1802 joinsplit description + tx overhead + wiggle room - size_t utxoCounter = 0; - bool maxedOutFlag = false; - const size_t mempoolLimit = nLimit; - - // Get available utxos - vector vecOutputs; - pwalletMain->AvailableCoins(vecOutputs, std::nullopt, true, NULL, false, true); - - // Find unspent coinbase utxos and update estimated size - for (const COutput& out : vecOutputs) { - if (!out.fSpendable) { - continue; - } - - CTxDestination address; - if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { - continue; - } - - // If from address was not the wildcard "*", filter utxos - if (sources.size() > 0 && !sources.count(address)) { - continue; - } - - if (!out.tx->IsCoinBase()) { - continue; - } - - utxoCounter++; - auto scriptPubKey = out.tx->vout[out.i].scriptPubKey; - CAmount nValue = out.tx->vout[out.i].nValue; + auto ztxoSelectorOpt = pwalletMain->ZTXOSelectorForAddress( + decoded.value(), + true, + TransparentCoinbasePolicy::Require, + strategy.AllowLinkingAccountAddresses()); - if (!maxedOutFlag) { - size_t increase = (std::get_if(&address) != nullptr) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; - if (estimatedTxSize + increase >= max_tx_size || - (mempoolLimit > 0 && utxoCounter > mempoolLimit)) - { - maxedOutFlag = true; - } else { - estimatedTxSize += increase; - ShieldCoinbaseUTXO utxo = {out.tx->GetHash(), out.i, scriptPubKey, nValue}; - inputs.push_back(utxo); - shieldedValue += nValue; + if (!ztxoSelectorOpt.has_value()) { + throw JSONRPCError( + RPC_INVALID_ADDRESS_OR_KEY, + "Invalid from address, no payment source found for address."); } - } - if (maxedOutFlag) { - remainingValue += nValue; - } - } + auto selectorAccount = pwalletMain->FindAccountForSelector(ztxoSelectorOpt.value()); + examine(decoded.value(), match { + [&](const libzcash::UnifiedAddress&) { + if (!selectorAccount.has_value() || selectorAccount.value() == ZCASH_LEGACY_ACCOUNT) { + throw JSONRPCError( + RPC_INVALID_ADDRESS_OR_KEY, + "Invalid from address, UA does not correspond to a known account."); + } + }, + [&](const auto&) { + if (selectorAccount.has_value() && selectorAccount.value() != ZCASH_LEGACY_ACCOUNT) { + throw JSONRPCError( + RPC_INVALID_ADDRESS_OR_KEY, + "Invalid from address: is a bare receiver from a Unified Address in this wallet. Provide the UA as returned by z_getaddressforaccount instead."); + } + } + }); - size_t numUtxos = inputs.size(); + return ztxoSelectorOpt.value(); + } + }(); - if (numUtxos == 0) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any coinbase funds to shield."); + // Validate the destination address + auto destStr = params[1].get_str(); + auto destaddress = keyIO.DecodePaymentAddress(destStr); + if (!destaddress.has_value()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destStr); } - if (shieldedValue < nFee) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, - strprintf("Insufficient coinbase funds, have %s, which is less than miners fee %s", - FormatMoney(shieldedValue), FormatMoney(nFee))); + std::optional nFee; + if (params.size() > 2 && !params[2].isNull()) { + nFee = AmountFromValue( params[2] ); } - // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) - CAmount netAmount = shieldedValue - nFee; - if (nFee > netAmount) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); + int nUTXOLimit = params.size() > 3 && !params[3].isNull() + ? params[3].get_int() + : SHIELD_COINBASE_DEFAULT_LIMIT; + if (nUTXOLimit < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of utxos cannot be negative"); } // Keep record of parameters in context object UniValue contextInfo(UniValue::VOBJ); contextInfo.pushKV("fromaddress", params[0]); contextInfo.pushKV("toaddress", params[1]); - contextInfo.pushKV("fee", ValueFromAmount(nFee)); - - // Builder (used if Sapling addresses are involved) - std::optional orchardAnchor; - if (nAnchorConfirmations > 0 && (involvesOrchard || nPreferredTxVersion >= ZIP225_MIN_TX_VERSION)) { - // Allow Orchard recipients by setting an Orchard anchor. - auto orchardAnchorHeight = nextBlockHeight - nAnchorConfirmations; - if (Params().GetConsensus().NetworkUpgradeActive(orchardAnchorHeight, Consensus::UPGRADE_NU5)) { - auto anchorBlockIndex = chainActive[orchardAnchorHeight]; - assert(anchorBlockIndex != nullptr); - orchardAnchor = anchorBlockIndex->hashFinalOrchardRoot; - } + if (nFee.has_value()) { + contextInfo.pushKV("fee", ValueFromAmount(nFee.value())); } - TransactionBuilder builder = TransactionBuilder( - Params().GetConsensus(), nextBlockHeight, orchardAnchor, pwalletMain); - // Contextual transaction we will build on - // (used if no Sapling addresses are involved) - CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( - Params().GetConsensus(), nextBlockHeight, - nPreferredTxVersion < ZIP225_MIN_TX_VERSION); - if (contextualTx.nVersion == 1) { - contextualTx.nVersion = 2; // Tx format should support vJoinSplit - } + // Create the wallet builder + WalletTxBuilder builder(chainparams, minRelayTxFee); + + auto async_shieldcoinbase = + new AsyncRPCOperation_shieldcoinbase( + std::move(builder), ztxoSelector, destaddress.value(), memo, strategy, nUTXOLimit, nFee, contextInfo); + auto results = async_shieldcoinbase->prepare(*pwalletMain); // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); - std::shared_ptr operation( new AsyncRPCOperation_shieldcoinbase( - std::move(builder), contextualTx, inputs, destaddress.value(), nFee, contextInfo) ); + std::shared_ptr operation(async_shieldcoinbase); q->addOperation(operation); AsyncRPCOperationId operationId = operation->getId(); // Return continuation information UniValue o(UniValue::VOBJ); - o.pushKV("remainingUTXOs", static_cast(utxoCounter - numUtxos)); - o.pushKV("remainingValue", ValueFromAmount(remainingValue)); - o.pushKV("shieldingUTXOs", static_cast(numUtxos)); - o.pushKV("shieldingValue", ValueFromAmount(shieldedValue)); + o.pushKV("remainingUTXOs", static_cast(results.utxoCounter - results.numUtxos)); + o.pushKV("remainingValue", ValueFromAmount(results.remainingValue)); + o.pushKV("shieldingUTXOs", static_cast(results.numUtxos)); + o.pushKV("shieldingValue", ValueFromAmount(results.shieldingValue)); o.pushKV("opid", operationId); return o; } @@ -5444,9 +5359,9 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() < 2 || params.size() > 6) + if (fHelp || params.size() < 2 || params.size() > 7) throw runtime_error( - "z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n" + "z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo ) ( privacyPolicy )\n" "\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`" "\nto combine those into a single note." "\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they" @@ -5471,13 +5386,33 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) " ,...\n" " ]\n" "2. \"toaddress\" (string, required) The taddr or zaddr to send the funds to.\n" - "3. fee (numeric, optional, default=" - + strprintf("%s", FormatMoney(DEFAULT_FEE)) + ") The fee amount to attach to this transaction.\n" + "3. fee (numeric, optional, default=null) The fee amount in " + CURRENCY_UNIT + " to attach to this transaction. The default behavior\n" + " is to use a fee calculated according to ZIP 317.\n" "4. transparent_limit (numeric, optional, default=" + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT) + ") Limit on the maximum number of UTXOs to merge. Set to 0 to use as many as will fit in the transaction.\n" "5. shielded_limit (numeric, optional, default=" + strprintf("%d Sprout or %d Sapling Notes", MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT, MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT) + ") Limit on the maximum number of notes to merge. Set to 0 to merge as many as will fit in the transaction.\n" "6. \"memo\" (string, optional) Encoded as hex. When toaddress is a zaddr, this will be stored in the memo field of the new note.\n" + "7. 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 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 value pools, revealing the amount\n" + " that crosses pools.\n" + " - \"AllowRevealedRecipients\": Allow transparent recipients. This also implies revealing\n" + " information described under \"AllowRevealedAmounts\".\n" + " - \"AllowRevealedSenders\": Allow transparent funds to be spent, revealing the sending\n" + " addresses and amounts. This implies revealing information described under \"AllowRevealedAmounts\".\n" + " - \"AllowFullyTransparent\": Allow transaction to both spend transparent funds and have\n" + " transparent recipients. This implies revealing information described under \"AllowRevealedSenders\"\n" + " and \"AllowRevealedRecipients\".\n" + " - \"AllowLinkingAccountAddresses\": Allow selecting transparent coins from the full account,\n" + " rather than just the funds sent to the transparent receiver in the provided Unified Address.\n" + " This implies revealing information described under \"AllowRevealedSenders\".\n" + " - \"NoPrivacy\": Allow the transaction to reveal any information necessary to create it.\n" + " This implies revealing information described under \"AllowFullyTransparent\" and\n" + " \"AllowLinkingAccountAddresses\".\n" "\nResult:\n" "{\n" " \"remainingUTXOs\": xxx (numeric) Number of UTXOs still available for merging.\n" @@ -5511,10 +5446,10 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) // Keep track of addresses to spot duplicates std::set setAddress; + std::set receiverTypes; - bool isFromNonSprout = false; - - KeyIO keyIO(Params()); + const auto chainparams = Params(); + KeyIO keyIO(chainparams); // Sources for (const UniValue& o : addresses.getValues()) { if (!o.isStr()) @@ -5523,28 +5458,27 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) std::string address = o.get_str(); if (address == "ANY_TADDR") { + receiverTypes.insert({ReceiverType::P2PKH, ReceiverType::P2SH}); useAnyUTXO = true; - isFromNonSprout = true; } else if (address == "ANY_SPROUT") { + // TODO: How can we add sprout addresses? + // receiverTypes.insert(ReceiverType::Sprout); useAnySprout = true; } else if (address == "ANY_SAPLING") { + receiverTypes.insert(ReceiverType::Sapling); useAnySapling = true; - isFromNonSprout = true; } else { auto addr = keyIO.DecodePaymentAddress(address); if (addr.has_value()) { - std::visit(match { + examine(addr.value(), match { [&](const CKeyID& taddr) { taddrs.insert(taddr); - isFromNonSprout = true; }, [&](const CScriptID& taddr) { taddrs.insert(taddr); - isFromNonSprout = true; }, [&](const libzcash::SaplingPaymentAddress& zaddr) { zaddrs.push_back(zaddr); - isFromNonSprout = true; }, [&](const libzcash::SproutPaymentAddress& zaddr) { zaddrs.push_back(zaddr); @@ -5552,9 +5486,9 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) [&](libzcash::UnifiedAddress) { throw JSONRPCError( RPC_INVALID_PARAMETER, - "Unified addresses are not supported in z_mergetoaddress"); + "Funds belonging to unified addresses can not be merged in z_mergetoaddress"); } - }, addr.value()); + }); } else { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Unknown address format: ") + address); } @@ -5573,18 +5507,18 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) } const int nextBlockHeight = chainActive.Height() + 1; - const bool overwinterActive = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_OVERWINTER); - const bool saplingActive = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_SAPLING); - const bool canopyActive = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_CANOPY); + const bool overwinterActive = chainparams.GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_OVERWINTER); + const bool saplingActive = chainparams.GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_SAPLING); + const bool canopyActive = chainparams.GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_CANOPY); // Validate the destination address auto destStr = params[1].get_str(); auto destaddress = keyIO.DecodePaymentAddress(destStr); bool isToTaddr = false; - bool isToSproutZaddr = false; - bool isToSaplingZaddr = false; + size_t estimatedTxSize = 200; // tx overhead + wiggle room + if (destaddress.has_value()) { - std::visit(match { + examine(destaddress.value(), match { [&](CKeyID addr) { isToTaddr = true; }, @@ -5592,44 +5526,30 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) isToTaddr = true; }, [&](libzcash::SaplingPaymentAddress addr) { - isToSaplingZaddr = true; // If Sapling is not active, do not allow sending to a sapling addresses. if (!saplingActive) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); } + estimatedTxSize += OUTPUTDESCRIPTION_SIZE * 2; }, - [&](libzcash::SproutPaymentAddress addr) { - isToSproutZaddr = true; - }, + [](libzcash::SproutPaymentAddress) { }, [&](libzcash::UnifiedAddress) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "Invalid parameter, unified addresses are not yet supported."); + estimatedTxSize += ZC_ZIP225_ORCHARD_BASE_SIZE + ZC_ZIP225_ORCHARD_MARGINAL_SIZE * 2; } - }, destaddress.value()); + }); } else { throw JSONRPCError( RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destStr); } - if (canopyActive && isFromNonSprout && isToSproutZaddr) { - // Value can be moved within Sprout, but not into Sprout. - throw JSONRPCError(RPC_VERIFY_REJECTED, "Sprout shielding is not supported after Canopy"); - } - - // Convert fee from currency format to zatoshis - CAmount nFee = DEFAULT_FEE; - if (params.size() > 2) { - if (params[2].get_real() == 0.0) { - nFee = 0; - } else { - nFee = AmountFromValue( params[2] ); - } + std::optional nFee; + if (params.size() > 2 && !params[2].isNull()) { + nFee = AmountFromValue( params[2] ); } int nUTXOLimit = MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT; - if (params.size() > 3) { + if (params.size() > 3 && !params[3].isNull()) { nUTXOLimit = params[3].get_int(); if (nUTXOLimit < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of UTXOs cannot be negative"); @@ -5638,7 +5558,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) int sproutNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT; int saplingNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT; - if (params.size() > 4) { + if (params.size() > 4 && !params[4].isNull()) { int nNoteLimit = params[4].get_int(); if (nNoteLimit < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); @@ -5647,25 +5567,15 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) saplingNoteLimit = nNoteLimit; } - std::string memo; + std::optional memo; if (params.size() > 5) { - memo = params[5].get_str(); - if (!(isToSproutZaddr || isToSaplingZaddr)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); - } else if (!IsHex(memo)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); - } - if (memo.length() > ZC_MEMO_SIZE*2) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); - } + memo = ParseMemo(params[5]); } - MergeToAddressRecipient recipient(destaddress.value(), memo); + NetAmountRecipient recipient(destaddress.value(), memo); // Prepare to get UTXOs and notes - std::vector utxoInputs; - std::vector sproutNoteInputs; - std::vector saplingNoteInputs; + SpendableInputs allInputs; CAmount mergedUTXOValue = 0; CAmount mergedNoteValue = 0; CAmount remainingUTXOValue = 0; @@ -5677,12 +5587,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) const size_t mempoolLimit = nUTXOLimit; unsigned int max_tx_size = saplingActive ? MAX_TX_SIZE_AFTER_SAPLING : MAX_TX_SIZE_BEFORE_SAPLING; - size_t estimatedTxSize = 200; // tx overhead + wiggle room - if (isToSproutZaddr) { - estimatedTxSize += JOINSPLIT_SIZE(SAPLING_TX_VERSION); // We assume that sapling has activated - } else if (isToSaplingZaddr) { - estimatedTxSize += OUTPUTDESCRIPTION_SIZE; - } if (useAnyUTXO || taddrs.size() > 0) { // Get available utxos @@ -5710,15 +5614,14 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) CAmount nValue = out.tx->vout[out.i].nValue; if (!maxedOutUTXOsFlag) { - size_t increase = (std::get_if(&address) != nullptr) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; + size_t increase = (std::get_if(&address) != nullptr) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_P2PKH_SIZE; if (estimatedTxSize + increase >= max_tx_size || (mempoolLimit > 0 && utxoCounter > mempoolLimit)) { maxedOutUTXOsFlag = true; } else { estimatedTxSize += increase; - COutPoint utxo(out.tx->GetHash(), out.i); - utxoInputs.emplace_back(utxo, nValue, scriptPubKey); + allInputs.utxos.push_back(out); mergedUTXOValue += nValue; } } @@ -5731,48 +5634,49 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) if (useAnySprout || useAnySapling || zaddrs.size() > 0) { // Get available notes - std::vector sproutEntries; - std::vector saplingEntries; - std::vector orchardEntries; std::optional noteFilter = useAnySprout || useAnySapling ? std::nullopt : std::optional(NoteFilter::ForPaymentAddresses(zaddrs)); - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, std::nullopt, nAnchorConfirmations); + + std::vector sproutCandidateNotes; + std::vector saplingCandidateNotes; + std::vector orchardCandidateNotes; + pwalletMain->GetFilteredNotes( + sproutCandidateNotes, + saplingCandidateNotes, + orchardCandidateNotes, + noteFilter, + std::nullopt, + nAnchorConfirmations); // If Sapling is not active, do not allow sending from a sapling addresses. - if (!saplingActive && saplingEntries.size() > 0) { + if (!saplingActive && saplingCandidateNotes.size() > 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); } // Do not include Sprout/Sapling notes if using "ANY_SAPLING"/"ANY_SPROUT" respectively if (useAnySprout) { - saplingEntries.clear(); + saplingCandidateNotes.clear(); } if (useAnySapling) { - sproutEntries.clear(); + sproutCandidateNotes.clear(); } // Sending from both Sprout and Sapling is currently unsupported using z_mergetoaddress - if ((sproutEntries.size() > 0 && saplingEntries.size() > 0) || (useAnySprout && useAnySapling)) { + if ((sproutCandidateNotes.size() > 0 && saplingCandidateNotes.size() > 0) || (useAnySprout && useAnySapling)) { throw JSONRPCError( RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress"); } - // 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, - "Cannot send between Sprout and Sapling addresses using z_mergetoaddress"); - } // Find unspent notes and update estimated size - for (const SproutNoteEntry& entry : sproutEntries) { + for (const SproutNoteEntry& entry : sproutCandidateNotes) { noteCounter++; CAmount nValue = entry.note.value(); if (!maxedOutNotesFlag) { // If we haven't added any notes yet and the merge is to a // z-address, we have already accounted for the first JoinSplit. - size_t increase = (sproutNoteInputs.empty() && !isToSproutZaddr) || (sproutNoteInputs.size() % 2 == 0) ? + size_t increase = allInputs.sproutNoteEntries.empty() || allInputs.sproutNoteEntries.size() % 2 == 0 ? JOINSPLIT_SIZE(SAPLING_TX_VERSION) : 0; if (estimatedTxSize + increase >= max_tx_size || (sproutNoteLimit > 0 && noteCounter > sproutNoteLimit)) @@ -5783,7 +5687,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) auto zaddr = entry.address; SproutSpendingKey zkey; pwalletMain->GetSproutSpendingKey(zaddr, zkey); - sproutNoteInputs.emplace_back(entry.jsop, entry.note, nValue, zkey); + allInputs.sproutNoteEntries.push_back(entry); mergedNoteValue += nValue; } } @@ -5793,7 +5697,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) } } - for (const SaplingNoteEntry& entry : saplingEntries) { + for (const SaplingNoteEntry& entry : saplingCandidateNotes) { noteCounter++; CAmount nValue = entry.note.value(); if (!maxedOutNotesFlag) { @@ -5808,7 +5712,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) if (!pwalletMain->GetSaplingExtendedSpendingKey(entry.address, extsk)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find spending key for payment address."); } - saplingNoteInputs.emplace_back(entry.op, entry.note, nValue, extsk.expsk); + allInputs.saplingNoteEntries.push_back(entry); mergedNoteValue += nValue; } } @@ -5819,8 +5723,8 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) } } - size_t numUtxos = utxoInputs.size(); - size_t numNotes = sproutNoteInputs.size() + saplingNoteInputs.size(); + size_t numUtxos = allInputs.utxos.size(); + size_t numNotes = allInputs.sproutNoteEntries.size() + allInputs.saplingNoteEntries.size(); if (numUtxos == 0 && numNotes == 0) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any funds to merge."); @@ -5830,68 +5734,73 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) // - We only have one from address // - It's equal to toaddress // - The address only contains a single UTXO or note + // TODO: Move this to WalletTxBuilder if (setAddress.size() == 1 && setAddress.count(destStr) && (numUtxos + numNotes) == 1) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Destination address is also the only source address, and all its funds are already merged."); } - CAmount mergedValue = mergedUTXOValue + mergedNoteValue; - if (mergedValue < nFee) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, - strprintf("Insufficient funds, have %s, which is less than miners fee %s", - FormatMoney(mergedValue), FormatMoney(nFee))); - } - - // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) - CAmount netAmount = mergedValue - nFee; - if (nFee > netAmount) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); - } - // Keep record of parameters in context object UniValue contextInfo(UniValue::VOBJ); contextInfo.pushKV("fromaddresses", params[0]); contextInfo.pushKV("toaddress", params[1]); - contextInfo.pushKV("fee", ValueFromAmount(nFee)); - - if (!sproutNoteInputs.empty() || !saplingNoteInputs.empty() || !isToTaddr) { - // We have shielded inputs or the recipient is a shielded address, and - // therefore we cannot create transactions before Sapling activates. - if (!saplingActive) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, "Cannot create shielded transactions before Sapling has activated"); - } + if (nFee.has_value()) { + contextInfo.pushKV("fee", ValueFromAmount(nFee.value())); + } + + // The privacy policy is determined early so as to be able to use it + // for selector construction. + auto strategy = + ResolveTransactionStrategy( + ReifyPrivacyPolicy( + std::nullopt, + params.size() > 6 ? std::optional(params[6].get_str()) : std::nullopt), + InterpretLegacyCompat(std::nullopt, {recipient.first})); + + WalletTxBuilder builder(Params(), minRelayTxFee); + + // The ZTXOSelector here is only used to determine whether or not Sprout can be selected. It + // should not be used for other purposes (e.g., note selection or finding a change address). + // TODO: Add a ZTXOSelector that can support the full range of `z_mergetoaddress` behavior and + // use it instead of `GetFilteredNotes`. + std::optional ztxoSelector; + if (allInputs.sproutNoteEntries.size() > 0) { + ztxoSelector = pwalletMain->ZTXOSelectorForAddress( + allInputs.sproutNoteEntries[0].address, + true, + TransparentCoinbasePolicy::Disallow, + strategy.AllowLinkingAccountAddresses()); + } else if (allInputs.saplingNoteEntries.size() > 0) { + ztxoSelector = pwalletMain->ZTXOSelectorForAddress( + allInputs.saplingNoteEntries[0].address, + true, + TransparentCoinbasePolicy::Disallow, + strategy.AllowLinkingAccountAddresses()); + } else { + ztxoSelector = CWallet::LegacyTransparentZTXOSelector(true, TransparentCoinbasePolicy::Disallow); } - bool isSproutShielded = sproutNoteInputs.size() > 0 || isToSproutZaddr; - // Contextual transaction we will build on - CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( - Params().GetConsensus(), - nextBlockHeight, - isSproutShielded || nPreferredTxVersion < ZIP225_MIN_TX_VERSION); - if (contextualTx.nVersion == 1 && isSproutShielded) { - contextualTx.nVersion = 2; // Tx format should support vJoinSplit - } - - // Builder (used if Sapling addresses are involved) - std::optional builder; - if (isToSaplingZaddr || saplingNoteInputs.size() > 0) { - std::optional orchardAnchor; - if (!isSproutShielded && nPreferredTxVersion >= ZIP225_MIN_TX_VERSION && nAnchorConfirmations > 0) { - // Allow Orchard recipients by setting an Orchard anchor. - auto orchardAnchorHeight = nextBlockHeight - nAnchorConfirmations; - if (Params().GetConsensus().NetworkUpgradeActive(orchardAnchorHeight, Consensus::UPGRADE_NU5)) { - auto anchorBlockIndex = chainActive[orchardAnchorHeight]; - assert(anchorBlockIndex != nullptr); - orchardAnchor = anchorBlockIndex->hashFinalOrchardRoot; - } - } - builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, orchardAnchor, pwalletMain); + if (!ztxoSelector.has_value()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing spending key for an address to be merged."); } + // Create operation and add to global queue + auto effects = builder.PrepareTransaction( + *pwalletMain, + ztxoSelector.value(), + allInputs, + recipient, + chainActive, + strategy, + nFee, + nAnchorConfirmations) + .map_error([&](const auto& err) { + ThrowInputSelectionError(err, ztxoSelector.value(), strategy); + }) + .value(); + std::shared_ptr q = getAsyncRPCQueue(); std::shared_ptr operation( - new AsyncRPCOperation_mergetoaddress( - std::move(builder), contextualTx, utxoInputs, sproutNoteInputs, saplingNoteInputs, recipient, nFee, contextInfo) ); + new AsyncRPCOperation_mergetoaddress(*pwalletMain, strategy, effects, contextInfo)); q->addOperation(operation); AsyncRPCOperationId operationId = operation->getId(); diff --git a/depend/zcash/src/wallet/test/rpc_wallet_tests.cpp b/depend/zcash/src/wallet/test/rpc_wallet_tests.cpp index d467f6033..89828d285 100644 --- a/depend/zcash/src/wallet/test/rpc_wallet_tests.cpp +++ b/depend/zcash/src/wallet/test/rpc_wallet_tests.cpp @@ -17,7 +17,6 @@ #include "asyncrpcqueue.h" #include "asyncrpcoperation.h" #include "wallet/asyncrpcoperation_common.h" -#include "wallet/asyncrpcoperation_mergetoaddress.h" #include "wallet/asyncrpcoperation_sendmany.h" #include "wallet/asyncrpcoperation_shieldcoinbase.h" #include "wallet/memo.h" @@ -69,6 +68,13 @@ class PushCurrentDirectory { fs::path old_cwd; }; +CWalletTx FakeWalletTx() { + CMutableTransaction mtx; + mtx.vout.resize(1); + mtx.vout[0].nValue = 1; + return CWalletTx(nullptr, mtx); +} + } static UniValue ValueFromString(const std::string &str) @@ -150,13 +156,13 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) CTxDestination setaccountDemoAddress(CTxDestination(setaccountDemoPubkey.GetID())); /********************************* - * getbalance + * getbalance *********************************/ BOOST_CHECK_NO_THROW(CallRPC("getbalance")); BOOST_CHECK_THROW(CallRPC("getbalance " + keyIO.EncodeDestination(demoAddress)), runtime_error); /********************************* - * listunspent + * listunspent *********************************/ BOOST_CHECK_NO_THROW(CallRPC("listunspent")); BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); @@ -167,7 +173,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) BOOST_CHECK(r.get_array().empty()); /********************************* - * listreceivedbyaddress + * listreceivedbyaddress *********************************/ BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress")); BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0")); @@ -201,22 +207,22 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) BOOST_CHECK_NO_THROW(CallRPC("listaddressgroupings")); /********************************* - * walletconfirmbackup + * walletconfirmbackup *********************************/ BOOST_CHECK_THROW(CallRPC(string("walletconfirmbackup \"badmnemonic\"")), runtime_error); /********************************* - * getrawchangeaddress + * getrawchangeaddress *********************************/ BOOST_CHECK_NO_THROW(CallRPC("getrawchangeaddress")); /********************************* - * getnewaddress + * getnewaddress *********************************/ BOOST_CHECK_NO_THROW(CallRPC("getnewaddress")); /********************************* - * signmessage + verifymessage + * signmessage + verifymessage *********************************/ BOOST_CHECK_NO_THROW(retValue = CallRPC("signmessage " + keyIO.EncodeDestination(demoAddress) + " mymessage")); BOOST_CHECK_THROW(CallRPC("signmessage"), runtime_error); @@ -236,7 +242,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) BOOST_CHECK(CallRPC("verifymessage " + keyIO.EncodeDestination(demoAddress) + " " + retValue.get_str() + " mymessage").get_bool() == true); /********************************* - * listaddresses + * listaddresses *********************************/ BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); UniValue arr = retValue.get_array(); @@ -258,7 +264,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) } /********************************* - * fundrawtransaction + * fundrawtransaction *********************************/ BOOST_CHECK_THROW(CallRPC("fundrawtransaction 28z"), runtime_error); BOOST_CHECK_THROW(CallRPC("fundrawtransaction 01000000000180969800000000001976a91450ce0a4b0ee0ddeb633da85199728b940ac3fe9488ac00000000"), runtime_error); @@ -802,7 +808,11 @@ void CheckHaveAddr(const std::optional& addr) { auto addr_of_type = std::get_if(&(addr.value())); BOOST_ASSERT(addr_of_type != nullptr); - BOOST_CHECK(pwalletMain->ZTXOSelectorForAddress(*addr_of_type, true, false).has_value()); + BOOST_CHECK(pwalletMain->ZTXOSelectorForAddress( + *addr_of_type, + true, + TransparentCoinbasePolicy::Allow, + false).has_value()); } BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) { @@ -1215,6 +1225,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) SelectParams(CBaseChainParams::TESTNET); const Consensus::Params& consensusParams = Params().GetConsensus(); KeyIO keyIO(Params()); + WalletTxBuilder builder(Params(), minRelayTxFee); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -1236,11 +1247,17 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) // there are no utxos to spend { - auto selector = pwalletMain->ZTXOSelectorForAddress(taddr1, true, false).value(); - TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain); - std::vector recipients = { ResolvedPayment(std::nullopt, zaddr1, 100*COIN, Memo::FromHexOrThrow("DEADBEEF")) }; - TransactionStrategy strategy; - std::shared_ptr operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, 1, strategy)); + auto selector = pwalletMain->ZTXOSelectorForAddress( + taddr1, + true, + // In the real transaction builder we use either Require or Disallow, but here we + // are checking that there are no UTXOs at all, so we allow either to be selected to + // confirm this. + TransparentCoinbasePolicy::Allow, + false).value(); + std::vector recipients = { Payment(zaddr1, 100*COIN, Memo::FromHexOrThrow("DEADBEEF")) }; + TransactionStrategy strategy(PrivacyPolicy::AllowRevealedSenders); + std::shared_ptr operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, 1, strategy, std::nullopt)); operation->main(); BOOST_CHECK(operation->isFailed()); std::string msg = operation->getErrorMessage(); @@ -1249,11 +1266,14 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) // there are no unspent notes to spend { - auto selector = pwalletMain->ZTXOSelectorForAddress(zaddr1, true, false).value(); - TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain); - std::vector recipients = { ResolvedPayment(std::nullopt, taddr1, 100*COIN, Memo::FromHexOrThrow("DEADBEEF")) }; + auto selector = pwalletMain->ZTXOSelectorForAddress( + zaddr1, + true, + TransparentCoinbasePolicy::Disallow, + false).value(); + std::vector recipients = { Payment(taddr1, 100*COIN, Memo::FromHexOrThrow("DEADBEEF")) }; TransactionStrategy strategy(PrivacyPolicy::AllowRevealedRecipients); - std::shared_ptr operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, 1, strategy)); + std::shared_ptr operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1, 1, strategy, std::nullopt)); operation->main(); BOOST_CHECK(operation->isFailed()); std::string msg = operation->getErrorMessage(); @@ -1506,26 +1526,24 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_parameters) "100 -1" ), runtime_error); - // Mutable tx containing contextual information we need to build tx - UniValue retValue = CallRPC("getblockcount"); - int nHeight = retValue.get_int(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1, false); - if (mtx.nVersion == 1) { - mtx.nVersion = 2; - } - // Test constructor of AsyncRPCOperation_shieldcoinbase KeyIO keyIO(Params()); + WalletTxBuilder builder(Params(), minRelayTxFee); + UniValue retValue; + BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress")); + auto taddr2 = std::get(keyIO.DecodePaymentAddress(retValue.get_str()).value()); auto testnetzaddr = std::get(keyIO.DecodePaymentAddress("ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP").value()); + auto selector = pwalletMain->ZTXOSelectorForAddress( + taddr2, + true, + // In the real transaction builder we use either Require or Disallow, but here we + // are checking that there are no UTXOs at all, so we allow either to be selected to + // confirm this. + TransparentCoinbasePolicy::Allow, + false).value(); try { - std::shared_ptr operation(new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, {}, testnetzaddr, -1 )); - } catch (const UniValue& objError) { - BOOST_CHECK( find_error(objError, "Fee is out of range")); - } - - try { - std::shared_ptr operation(new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, {}, testnetzaddr, 1)); + std::shared_ptr operation(new AsyncRPCOperation_shieldcoinbase(std::move(builder), selector, testnetzaddr, std::nullopt, PrivacyPolicy::AllowRevealedSenders, SHIELD_COINBASE_DEFAULT_LIMIT, 1)); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "Empty inputs")); } @@ -1555,7 +1573,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters) // bad from address CheckRPCThrows("z_mergetoaddress ** " + taddr2, - "Error parsing JSON:**"); + "Error parsing JSON: **"); // bad from address CheckRPCThrows("z_mergetoaddress [\"**\"] " + taddr2, @@ -1563,11 +1581,11 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters) // bad from address CheckRPCThrows("z_mergetoaddress " + taddr1 + " " + taddr2, - "Error parsing JSON:" + taddr1); + "Error parsing JSON: " + taddr1); // bad from address CheckRPCThrows("z_mergetoaddress [" + taddr1 + "] " + taddr2, - "Error parsing JSON:[" + taddr1 + "]"); + "Error parsing JSON: [" + taddr1 + "]"); // bad to address CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] INVALID" + taddr2, @@ -1607,54 +1625,44 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters) std::fill(v.begin(),v.end(), 'A'); std::string badmemo(v.begin(), v.end()); CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + aSproutAddr + " 0.00001 100 100 " + badmemo, - "Invalid parameter, size of memo is larger than maximum allowed 512"); + strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE)); // Mutable tx containing contextual information we need to build tx UniValue retValue = CallRPC("getblockcount"); int nHeight = retValue.get_int(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1, false); - // Test constructor of AsyncRPCOperation_mergetoaddress KeyIO keyIO(Params()); - MergeToAddressRecipient testnetzaddr( - keyIO.DecodePaymentAddress("ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP").value(), - "testnet memo"); - - try { - std::shared_ptr operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, {}, {}, {}, testnetzaddr, -1 )); - BOOST_FAIL("Should have caused an error"); - } catch (const UniValue& objError) { - BOOST_CHECK( find_error(objError, "Fee is out of range")); - } - - try { - std::shared_ptr operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, {}, {}, {}, testnetzaddr, 1)); - BOOST_FAIL("Should have caused an error"); - } catch (const UniValue& objError) { - BOOST_CHECK( find_error(objError, "No inputs")); - } - - std::vector inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0, CScript()} }; - - std::vector sproutNoteInputs = - {MergeToAddressInputSproutNote{JSOutPoint(), SproutNote(), 0, SproutSpendingKey()}}; - std::vector saplingNoteInputs = - {MergeToAddressInputSaplingNote{SaplingOutPoint(), SaplingNote({}, uint256(), 0, uint256(), Zip212Enabled::BeforeZip212), 0, SaplingExpandedSpendingKey()}}; - - // Sprout and Sapling inputs -> throw - try { - auto operation = new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, inputs, sproutNoteInputs, saplingNoteInputs, testnetzaddr, 1); - BOOST_FAIL("Should have caused an error"); - } catch (const UniValue& objError) { - BOOST_CHECK(find_error(objError, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress")); - } - // Sprout inputs and TransactionBuilder -> throw - try { - auto operation = new AsyncRPCOperation_mergetoaddress(TransactionBuilder(), mtx, inputs, sproutNoteInputs, {}, testnetzaddr, 1); - BOOST_FAIL("Should have caused an error"); - } catch (const UniValue& objError) { - BOOST_CHECK(find_error(objError, "Sprout notes are not supported by the TransactionBuilder")); - } + WalletTxBuilder builder(Params(), minRelayTxFee); + auto saplingKey = pwalletMain->GenerateNewLegacySaplingZKey(); + NetAmountRecipient testnetzaddr(saplingKey, Memo()); + auto selector = CWallet::LegacyTransparentZTXOSelector( + true, + TransparentCoinbasePolicy::Disallow); + TransactionStrategy strategy(PrivacyPolicy::AllowRevealedRecipients); + + builder.PrepareTransaction(*pwalletMain, selector, {}, testnetzaddr, chainActive, strategy, -1, 1) + .map_error([&](const auto& err) { + // TODO: Provide `operator==` on `InputSelectionError` and use that here. + BOOST_CHECK(std::holds_alternative(err)); + }) + .map([](const auto&) { + BOOST_FAIL("Fee value of -1 expected to be out of the valid range of values."); + }); + + builder.PrepareTransaction(*pwalletMain, selector, {}, testnetzaddr, chainActive, strategy, 1, 1) + .map_error([&](const auto& err) { + // TODO: Provide `operator==` on `InputSelectionError` and use that here. + BOOST_CHECK(examine(err, match { + [](const InvalidFundsError& ife) { + return std::holds_alternative(ife.reason); + }, + [](const auto&) { return false; }, + })); + }) + .map([](const auto&) { + BOOST_FAIL("Expected an error."); + }); } void TestWTxStatus(const Consensus::Params consensusParams, const int delta) { diff --git a/depend/zcash/src/wallet/test/wallet_tests.cpp b/depend/zcash/src/wallet/test/wallet_tests.cpp index 23008e505..9b98126c7 100644 --- a/depend/zcash/src/wallet/test/wallet_tests.cpp +++ b/depend/zcash/src/wallet/test/wallet_tests.cpp @@ -175,11 +175,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin( 3*COIN); add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents BOOST_CHECK( CWallet::SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin + BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 ZEC in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); BOOST_CHECK( CWallet::SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin + BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 ZEC in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // empty the wallet and start again, now with fractions of a cent, to test small change avoidance diff --git a/depend/zcash/src/wallet/wallet.cpp b/depend/zcash/src/wallet/wallet.cpp index 1c251f0d9..bb792517a 100644 --- a/depend/zcash/src/wallet/wallet.cpp +++ b/depend/zcash/src/wallet/wallet.cpp @@ -30,6 +30,7 @@ #include "zcash/Address.hpp" #include "zcash/JoinSplit.hpp" #include "zcash/Note.hpp" +#include "zip317.h" #include "crypter.h" #include "wallet/asyncrpcoperation_saplingmigration.h" @@ -47,21 +48,13 @@ using namespace libzcash; CWallet* pwalletMain = NULL; /** Transaction fee set by the user */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); -unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; -bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS; bool fPayAtLeastCustomFee = true; unsigned int nAnchorConfirmations = DEFAULT_ANCHOR_CONFIRMATIONS; unsigned int nOrchardActionLimit = DEFAULT_ORCHARD_ACTION_LIMIT; const char * DEFAULT_WALLET_DAT = "wallet.dat"; -/** - * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) - * Override with -mintxfee - */ -CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); - std::set CWallet::DefaultReceiverTypes(int nHeight) { // For now, just ignore the height information because the default // is always the same. @@ -680,14 +673,14 @@ std::optional CWallet::GetUnifiedFullViewingKeyByAc } WalletUAGenerationResult ToWalletUAGenerationResult(UnifiedAddressGenerationResult result) { - return std::visit(match { + return examine(result, match { [](const UnifiedAddressGenerationError& err) { return WalletUAGenerationResult(err); }, [](const std::pair& addrPair) { return WalletUAGenerationResult(addrPair); } - }, result); + }); } WalletUAGenerationResult CWallet::GenerateUnifiedAddress( @@ -882,7 +875,7 @@ std::pair CWallet::GetPaymentAddressForRecipient( } auto ufvk = self->GetUFVKForReceiver(RecipientAddressToReceiver(recipient)); - std::pair defaultAddress = std::visit(match { + std::pair defaultAddress = examine(recipient, match { [&](const CKeyID& addr) { auto ua = self->FindUnifiedAddressByReceiver(addr); if (ua.has_value()) { @@ -979,7 +972,7 @@ std::pair CWallet::GetPaymentAddressForRecipient( return std::make_pair(PaymentAddress{UnifiedAddress::ForSingleReceiver(addr)}, RecipientType::CounterpartyAddress); } - }, recipient); + }); auto recipientsPtr = sendRecipients.find(txid); if (recipientsPtr == sendRecipients.end()) { @@ -1005,7 +998,7 @@ std::pair CWallet::GetPaymentAddressForRecipient( bool CWallet::IsInternalRecipient(const libzcash::RecipientAddress& recipient) const { auto self = this; - return std::visit(match { + return examine(recipient, match { [&](const CKeyID& addr) { // we never send transparent change when sending to or from a // unified address @@ -1035,7 +1028,7 @@ bool CWallet::IsInternalRecipient(const libzcash::RecipientAddress& recipient) c } return false; } - }, recipient); + }); } void CWallet::LoadRecipientMapping(const uint256& txid, const RecipientMapping& mapping) { @@ -1092,7 +1085,7 @@ bool CWallet::LoadCaches() // restore unified addresses that have been previously generated to the // keystore for (const auto &[j, receiverTypes] : metadata->second.GetKnownReceiverSetsByDiversifierIndex()) { - bool restored = std::visit(match { + bool restored = examine(ufvk.value().Address(j, receiverTypes), match { [&](const UnifiedAddressGenerationError& err) { LogPrintf("%s: Error: Unable to generate a unified address.\n", __func__); @@ -1115,7 +1108,7 @@ bool CWallet::LoadCaches() return CCryptoKeyStore::AddTransparentReceiverForUnifiedAddress( ufvkId, addr.second, addr.first); } - }, ufvk.value().Address(j, receiverTypes)); + }); // failure to restore the generated address is an error if (!restored) return false; @@ -1871,6 +1864,7 @@ void CWallet::SyncMetaData(pair::iterator, typename TxSpe std::optional CWallet::ZTXOSelectorForAccount( libzcash::AccountId account, bool requireSpendingKey, + TransparentCoinbasePolicy transparentCoinbasePolicy, std::set receiverTypes) const { if (mnemonicHDChain.has_value() && @@ -1878,7 +1872,10 @@ std::optional CWallet::ZTXOSelectorForAccount( std::make_pair(mnemonicHDChain.value().GetSeedFingerprint(), account) ) > 0) { - return ZTXOSelector(AccountZTXOPattern(account, receiverTypes), requireSpendingKey); + return ZTXOSelector( + AccountZTXOPattern(account, receiverTypes), + requireSpendingKey, + transparentCoinbasePolicy); } else { return std::nullopt; } @@ -1887,11 +1884,12 @@ std::optional CWallet::ZTXOSelectorForAccount( std::optional CWallet::ZTXOSelectorForAddress( const libzcash::PaymentAddress& addr, bool requireSpendingKey, + TransparentCoinbasePolicy transparentCoinbasePolicy, bool allowAddressLinkability) const { auto self = this; std::optional pattern = std::nullopt; - std::visit(match { + examine(addr, match { [&](const CKeyID& addr) { if (!requireSpendingKey || self->HaveKey(addr)) { pattern = addr; @@ -1913,14 +1911,14 @@ std::optional CWallet::ZTXOSelectorForAddress( } }, [&](const libzcash::UnifiedAddress& ua) { - auto ufvkMeta = GetUFVKMetadataForAddress(ua); - if (ufvkMeta.has_value()) { + auto ufvkId = GetUFVKIdForAddress(ua); + if (ufvkId.has_value()) { // TODO: at present, the `false` value for the `requireSpendingKey` argument // is not respected for unified addresses, because we have no notion of // an account for which we do not control the spending key. An alternate // approach would be to use the UFVK directly in the case that we cannot // determine a local account. - auto accountId = this->GetUnifiedAccountId(ufvkMeta.value().GetUFVKId()); + auto accountId = this->GetUnifiedAccountId(ufvkId.value()); if (accountId.has_value()) { if (allowAddressLinkability) { pattern = AccountZTXOPattern(accountId.value(), ua.GetKnownReceiverTypes()); @@ -1928,14 +1926,12 @@ std::optional CWallet::ZTXOSelectorForAddress( pattern = ua; } } - } else { - pattern = ua; } } - }, addr); + }); if (pattern.has_value()) { - return ZTXOSelector(pattern.value(), requireSpendingKey); + return ZTXOSelector(pattern.value(), requireSpendingKey, transparentCoinbasePolicy); } else { return std::nullopt; } @@ -1943,11 +1939,12 @@ std::optional CWallet::ZTXOSelectorForAddress( std::optional CWallet::ZTXOSelectorForViewingKey( const libzcash::ViewingKey& vk, - bool requireSpendingKey) const + bool requireSpendingKey, + TransparentCoinbasePolicy transparentCoinbasePolicy) const { auto self = this; std::optional pattern = std::nullopt; - std::visit(match { + examine(vk, match { [&](const libzcash::SaplingExtendedFullViewingKey& vk) { if (!requireSpendingKey || self->HaveSaplingSpendingKey(vk)) { pattern = vk; @@ -1967,25 +1964,26 @@ std::optional CWallet::ZTXOSelectorForViewingKey( pattern = ufvk; } } - }, vk); + }); if (pattern.has_value()) { - return ZTXOSelector(pattern.value(), requireSpendingKey); + return ZTXOSelector(pattern.value(), requireSpendingKey, transparentCoinbasePolicy); } else { return std::nullopt; } } -ZTXOSelector CWallet::LegacyTransparentZTXOSelector(bool requireSpendingKey) { +ZTXOSelector CWallet::LegacyTransparentZTXOSelector(bool requireSpendingKey, TransparentCoinbasePolicy transparentCoinbasePolicy) { return ZTXOSelector( AccountZTXOPattern(ZCASH_LEGACY_ACCOUNT, {ReceiverType::P2PKH, ReceiverType::P2SH}), - requireSpendingKey); + requireSpendingKey, + transparentCoinbasePolicy); } std::optional CWallet::FindAccountForSelector(const ZTXOSelector& selector) const { auto self = this; std::optional result{}; - std::visit(match { + examine(selector.GetPattern(), match { [&](const CKeyID& addr) { auto meta = self->GetUFVKMetadataForReceiver(addr); if (meta.has_value()) { @@ -2013,9 +2011,9 @@ std::optional CWallet::FindAccountForSelector(const ZTXOSel } }, [&](const libzcash::UnifiedAddress& addr) { - auto meta = GetUFVKMetadataForAddress(addr); - if (meta.has_value()) { - result = self->GetUnifiedAccountId(meta.value().GetUFVKId()); + auto ufvkId = GetUFVKIdForAddress(addr); + if (ufvkId.has_value()) { + result = self->GetUnifiedAccountId(ufvkId.value()); } }, [&](const libzcash::UnifiedFullViewingKey& vk) { @@ -2029,7 +2027,7 @@ std::optional CWallet::FindAccountForSelector(const ZTXOSel result = acct.GetAccountId(); } } - }, selector.GetPattern()); + }); return result; } @@ -2039,7 +2037,7 @@ bool CWallet::SelectorMatchesAddress( const ZTXOSelector& selector, const CTxDestination& address) const { auto self = this; - return std::visit(match { + return examine(selector.GetPattern(), match { [&](const CKeyID& keyId) { CTxDestination keyIdDest = keyId; return address == keyIdDest; @@ -2056,7 +2054,7 @@ bool CWallet::SelectorMatchesAddress( // for a UA selector when matching transparent addresses, we only match addresses // that explicitly appear as receivers in the UA. for (const auto& receiver : uaSelector) { - bool matches = std::visit(match { + bool matches = examine(receiver, match { [&](const libzcash::OrchardRawAddress& orchardAddr) { return false; }, [&](const libzcash::SaplingPaymentAddress& saplingAddr) { return false; }, [&](const libzcash::UnknownReceiver& receiver) { return false; }, @@ -2068,26 +2066,18 @@ bool CWallet::SelectorMatchesAddress( CTxDestination keyIdDest = keyId; return address == keyIdDest; } - }, receiver); + }); if (matches) return true; } return false; }, [&](const libzcash::UnifiedFullViewingKey& ufvk) { - std::optional meta; - std::visit(match { - [&](const CNoDestination& none) { meta = std::nullopt; }, - [&](const auto& addr) { meta = self->GetUFVKMetadataForReceiver(addr); } - }, address); + auto meta = self->GetUFVKMetadataForAddress(address); return (meta.has_value() && meta.value().GetUFVKId() == ufvk.GetKeyID(Params())); }, [&](const AccountZTXOPattern& acct) { if (acct.IncludesP2PKH() || acct.IncludesP2SH()) { - std::optional meta; - std::visit(match { - [&](const CNoDestination& none) { meta = std::nullopt; }, - [&](const auto& addr) { meta = self->GetUFVKMetadataForReceiver(addr); } - }, address); + auto meta = self->GetUFVKMetadataForAddress(address); if (meta.has_value()) { // use the coin if the account id corresponding to the UFVK is // the payment source account. @@ -2101,24 +2091,24 @@ bool CWallet::SelectorMatchesAddress( } return false; } - }, selector.GetPattern()); + }); } // Sprout bool CWallet::SelectorMatchesAddress( const ZTXOSelector& selector, const libzcash::SproutPaymentAddress& a0) const { - return std::visit(match { + return examine(selector.GetPattern(), match { [&](const libzcash::SproutPaymentAddress& a1) { return a0 == a1; }, [&](const libzcash::SproutViewingKey& vk) { return a0 == vk.address(); }, [&](const auto& addr) { return false; }, - }, selector.GetPattern()); + }); } // Sapling bool CWallet::SelectorMatchesAddress( const ZTXOSelector& selector, const libzcash::SaplingPaymentAddress& a0) const { auto self = this; - return std::visit(match { + return examine(selector.GetPattern(), match { [&](const CKeyID& keyId) { return false; }, [&](const CScriptID& scriptId) { return false; }, [&](const libzcash::SproutPaymentAddress& addr) { return false; }, @@ -2165,7 +2155,7 @@ bool CWallet::SelectorMatchesAddress( } return false; } - }, selector.GetPattern()); + }); } std::optional CWallet::GenerateChangeAddressForAccount( @@ -2215,7 +2205,6 @@ std::optional CWallet::GenerateChangeAddressForAccount( SpendableInputs CWallet::FindSpendableInputs( ZTXOSelector selector, - bool allowTransparentCoinbase, uint32_t minDepth, const std::optional& asOfHeight) const { AssertLockHeld(cs_main); @@ -2224,7 +2213,6 @@ SpendableInputs CWallet::FindSpendableInputs( KeyIO keyIO(Params()); bool selectTransparent{selector.SelectsTransparent()}; - bool selectTransparentCoinbase{selector.SelectsTransparentCoinbase()}; bool selectSprout{selector.SelectsSprout()}; bool selectSapling{selector.SelectsSapling()}; bool selectOrchard{selector.SelectsOrchard()}; @@ -2238,10 +2226,18 @@ SpendableInputs CWallet::FindSpendableInputs( if (!CheckFinalTx(wtx)) continue; if (nDepth < 0 || nDepth < minDepth) continue; - if (selectTransparent && - // skip transparent utxo selection if coinbase spend restrictions are not met - (!isCoinbase || (selectTransparentCoinbase && allowTransparentCoinbase && wtx.GetBlocksToMaturity(asOfHeight) <= 0))) { - + if (selectTransparent && ( + ( + // Only select coinbase transparent utxos if spend restrictions are met. + isCoinbase && + selector.transparentCoinbasePolicy != TransparentCoinbasePolicy::Disallow && + wtx.GetBlocksToMaturity(asOfHeight) <= 0 + ) || ( + // Only select non-coinbase transparent utxos if we are allowed to. + !isCoinbase && + selector.transparentCoinbasePolicy != TransparentCoinbasePolicy::Require + ) + )) { for (int i = 0; i < wtx.vout.size(); i++) { const auto& output = wtx.vout[i]; isminetype mine = IsMine(output); @@ -2327,16 +2323,14 @@ SpendableInputs CWallet::FindSpendableInputs( if (selectSapling) { for (auto const& [op, nd] : wtx.mapSaplingNoteData) { - auto optDeserialized = SaplingNotePlaintext::attempt_sapling_enc_decryption_deserialization(wtx.vShieldedOutput[op.n].encCiphertext, nd.ivk, wtx.vShieldedOutput[op.n].ephemeralKey); + auto optDecrypted = wtx.DecryptSaplingNote(Params(), op); // The transaction would not have entered the wallet unless // its plaintext had been successfully decrypted previously. - assert(optDeserialized != std::nullopt); - - auto notePt = optDeserialized.value(); - auto maybe_pa = nd.ivk.address(notePt.d); - assert(maybe_pa.has_value()); - auto pa = maybe_pa.value(); + assert(optDecrypted != std::nullopt); + SaplingNotePlaintext notePt; + SaplingPaymentAddress pa; + std::tie(notePt, pa) = optDecrypted.value(); // skip notes which have been spent if (nd.nullifier.has_value() && IsSaplingSpent(nd.nullifier.value(), asOfHeight)) continue; @@ -2356,7 +2350,7 @@ SpendableInputs CWallet::FindSpendableInputs( if (selectOrchard) { // for Orchard, we select both the internal and external IVKs. - auto orchardIvks = std::visit(match { + auto orchardIvks = examine(selector.GetPattern(), match { [&](const libzcash::UnifiedAddress& selectorUA) -> std::vector { auto orchardReceiver = selectorUA.GetOrchardReceiver(); if (orchardReceiver.has_value()) { @@ -2391,7 +2385,7 @@ SpendableInputs CWallet::FindSpendableInputs( return {}; }, [&](const auto& addr) -> std::vector { return {}; } - }, selector.GetPattern()); + }); for (const auto& ivk : orchardIvks) { std::vector incomingNotes; @@ -3227,21 +3221,16 @@ void CWallet::UpdateSaplingNullifierNoteMapWithTx(CWalletTx& wtx) { else { uint64_t position = nd.witnesses.front().position(); auto extfvk = mapSaplingFullViewingKeys.at(nd.ivk); - OutputDescription output = wtx.vShieldedOutput[op.n]; - auto optDeserialized = SaplingNotePlaintext::attempt_sapling_enc_decryption_deserialization(output.encCiphertext, nd.ivk, output.ephemeralKey); + auto optDecrypted = wtx.DecryptSaplingNote(Params(), op); // The transaction would not have entered the wallet unless // its plaintext had been successfully decrypted previously. - assert(optDeserialized != std::nullopt); - - auto optPlaintext = SaplingNotePlaintext::plaintext_checks_without_height(*optDeserialized, nd.ivk, output.ephemeralKey, output.cmu); + assert(optDecrypted != std::nullopt); + SaplingNotePlaintext notePt; + std::tie(notePt, std::ignore) = optDecrypted.value(); - // An item in mapSaplingNoteData must have already been successfully decrypted, - // otherwise the item would not exist in the first place. - assert(optPlaintext != std::nullopt); - - auto optNote = optPlaintext.value().note(nd.ivk); + auto optNote = notePt.note(nd.ivk); assert(optNote != std::nullopt); auto optNullifier = optNote.value().nullifier(extfvk.fvk, position); @@ -3541,16 +3530,7 @@ bool CWallet::AddToWalletIfInvolvingMe( rust::Box WalletBatchScanner::CreateBatchScanner(CWallet* pwallet) { LOCK(pwallet->cs_KeyStore); - auto chainParams = Params(); - auto consensus = chainParams.GetConsensus(); - auto network = wallet::network( - chainParams.NetworkIDString(), - consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight, - consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight, - consensus.vUpgrades[Consensus::UPGRADE_BLOSSOM].nActivationHeight, - consensus.vUpgrades[Consensus::UPGRADE_HEARTWOOD].nActivationHeight, - consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, - consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight); + auto network = Params().RustNetwork(); // TODO: Pass the map across the FFI once cxx supports it. std::vector> ivks; @@ -3789,7 +3769,7 @@ mapSproutNoteData_t CWallet::FindMySproutNotes(const CTransaction &tx) const * already have been cached in CWalletTx.mapSaplingNoteData. */ std::pair CWallet::FindMySaplingNotes( - const Consensus::Params& consensus, + const CChainParams& params, const CTransaction &tx, int height) const { @@ -3805,21 +3785,35 @@ std::pair CWallet::FindMySap for (auto it = mapSaplingFullViewingKeys.begin(); it != mapSaplingFullViewingKeys.end(); ++it) { SaplingIncomingViewingKey ivk = it->first; - auto result = SaplingNotePlaintext::decrypt(consensus, height, output.encCiphertext, ivk, output.ephemeralKey, output.cmu); - if (!result) { + try { + auto decrypted = wallet::try_sapling_note_decryption( + *params.RustNetwork(), + height, + ivk.GetRawBytes(), + { + output.cv.GetRawBytes(), + output.cmu.GetRawBytes(), + output.ephemeralKey.GetRawBytes(), + output.encCiphertext, + output.outCiphertext, + }); + + SaplingPaymentAddress address( + decrypted->recipient_d(), + uint256::FromRawBytes(decrypted->recipient_pk_d())); + if (mapSaplingIncomingViewingKeys.count(address) == 0) { + viewingKeysToAdd[address] = ivk; + } + // We don't cache the nullifier here as computing it requires knowledge of the note position + // in the commitment tree, which can only be determined when the transaction has been mined. + SaplingOutPoint op {hash, i}; + SaplingNoteData nd; + nd.ivk = ivk; + noteData.insert(std::make_pair(op, nd)); + break; + } catch (const rust::Error &e) { continue; } - auto address = ivk.address(result.value().d); - if (address && mapSaplingIncomingViewingKeys.count(address.value()) == 0) { - viewingKeysToAdd[address.value()] = ivk; - } - // We don't cache the nullifier here as computing it requires knowledge of the note position - // in the commitment tree, which can only be determined when the transaction has been mined. - SaplingOutPoint op {hash, i}; - SaplingNoteData nd; - nd.ivk = ivk; - noteData.insert(std::make_pair(op, nd)); - break; } } @@ -3992,7 +3986,7 @@ bool CWallet::IsChange(const CTxOut& txout) const // We look to key metadata to determine whether the address was generated // using an internal key path. This could fail to identify some legacy // change addresses as change outputs. - return std::visit(match { + return examine(address, match { [&](const CKeyID& key) { auto keyMetaIt = mapKeyMetadata.find(key); return @@ -4005,7 +3999,7 @@ bool CWallet::IsChange(const CTxOut& txout) const IsInternalKeyPath(44, BIP44CoinType(), keyMetaIt->second.hdKeypath); }, [&](const auto& other) { return false; } - }, address); + }); } CAmount CWallet::GetChange(const CTxOut& txout) const @@ -4314,36 +4308,7 @@ std::pair CWalletTx::DecryptSproutNot std::optional> CWalletTx::DecryptSaplingNote(const Consensus::Params& params, int height, SaplingOutPoint op) const -{ - // Check whether we can decrypt this SaplingOutPoint - if (this->mapSaplingNoteData.count(op) == 0) { - return std::nullopt; - } - - auto output = this->vShieldedOutput[op.n]; - auto nd = this->mapSaplingNoteData.at(op); - - auto maybe_pt = SaplingNotePlaintext::decrypt( - params, - height, - output.encCiphertext, - nd.ivk, - output.ephemeralKey, - output.cmu); - assert(maybe_pt != std::nullopt); - auto notePt = maybe_pt.value(); - - auto maybe_pa = nd.ivk.address(notePt.d); - assert(maybe_pa != std::nullopt); - auto pa = maybe_pa.value(); - - return std::make_pair(notePt, pa); -} - -std::optional> CWalletTx::DecryptSaplingNoteWithoutLeadByteCheck(SaplingOutPoint op) const + SaplingPaymentAddress>> CWalletTx::DecryptSaplingNote(const CChainParams& params, SaplingOutPoint op) const { // Check whether we can decrypt this SaplingOutPoint if (this->mapSaplingNoteData.count(op) == 0) { @@ -4353,101 +4318,53 @@ std::optionalvShieldedOutput[op.n]; auto nd = this->mapSaplingNoteData.at(op); - auto optDeserialized = SaplingNotePlaintext::attempt_sapling_enc_decryption_deserialization(output.encCiphertext, nd.ivk, output.ephemeralKey); - - // The transaction would not have entered the wallet unless - // its plaintext had been successfully decrypted previously. - assert(optDeserialized != std::nullopt); - - auto maybe_pt = SaplingNotePlaintext::plaintext_checks_without_height( - *optDeserialized, - nd.ivk, - output.ephemeralKey, - output.cmu); - assert(maybe_pt != std::nullopt); - auto notePt = maybe_pt.value(); - - auto maybe_pa = nd.ivk.address(notePt.d); - assert(static_cast(maybe_pa)); - auto pa = maybe_pa.value(); - - return std::make_pair(notePt, pa); -} - -std::optional> CWalletTx::RecoverSaplingNote(const Consensus::Params& params, int height, SaplingOutPoint op, std::set& ovks) const -{ - auto output = this->vShieldedOutput[op.n]; - - for (auto ovk : ovks) { - auto outPt = SaplingOutgoingPlaintext::decrypt( - output.outCiphertext, - ovk, - output.cv, - output.cmu, - output.ephemeralKey); - if (!outPt) { - // Try decrypting with the next ovk - continue; - } - - auto maybe_pt = SaplingNotePlaintext::decrypt( - params, - height, - output.encCiphertext, - output.ephemeralKey, - outPt->esk, - outPt->pk_d, - output.cmu); - assert(static_cast(maybe_pt)); - auto notePt = maybe_pt.value(); + try { + auto decrypted = wallet::try_sapling_note_decryption( + *params.RustNetwork(), + // Canopy activation is inside the ZIP 212 grace period, + // and so this allows both v1 and v2 note plaintexts. + params.GetConsensus().vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, + nd.ivk.GetRawBytes(), + { + output.cv.GetRawBytes(), + output.cmu.GetRawBytes(), + output.ephemeralKey.GetRawBytes(), + output.encCiphertext, + output.outCiphertext, + }); - return std::make_pair(notePt, SaplingPaymentAddress(notePt.d, outPt->pk_d)); + return SaplingNotePlaintext::from_rust(std::move(decrypted)); + } catch (const rust::Error &e) { + assert(false); } - - // Couldn't recover with any of the provided OutgoingViewingKeys - return std::nullopt; } std::optional> CWalletTx::RecoverSaplingNoteWithoutLeadByteCheck(SaplingOutPoint op, std::set& ovks) const + SaplingPaymentAddress>> CWalletTx::RecoverSaplingNote(const CChainParams& params, SaplingOutPoint op, std::set& ovks) const { auto output = this->vShieldedOutput[op.n]; - // ZIP 216: This wallet method is not called from consensus rules. - bool zip216Enabled = true; for (auto ovk : ovks) { - auto outPt = SaplingOutgoingPlaintext::decrypt( - output.outCiphertext, - ovk, - output.cv, - output.cmu, - output.ephemeralKey); - if (!outPt) { + try { + auto decrypted = wallet::try_sapling_output_recovery( + *params.RustNetwork(), + // Canopy activation is inside the ZIP 212 grace period, + // and so this allows both v1 and v2 note plaintexts. + params.GetConsensus().vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, + ovk.GetRawBytes(), + { + output.cv.GetRawBytes(), + output.cmu.GetRawBytes(), + output.ephemeralKey.GetRawBytes(), + output.encCiphertext, + output.outCiphertext, + }); + + return SaplingNotePlaintext::from_rust(std::move(decrypted)); + } catch (const rust::Error &e) { // Try decrypting with the next ovk - continue; } - - auto optDeserialized = SaplingNotePlaintext::attempt_sapling_enc_decryption_deserialization( - zip216Enabled, output.encCiphertext, output.ephemeralKey, outPt->esk, outPt->pk_d); - - // The transaction would not have entered the wallet unless - // its plaintext had been successfully decrypted previously. - assert(optDeserialized != std::nullopt); - - auto maybe_pt = SaplingNotePlaintext::plaintext_checks_without_height( - zip216Enabled, - *optDeserialized, - output.ephemeralKey, - outPt->esk, - outPt->pk_d, - output.cmu); - assert(static_cast(maybe_pt)); - auto notePt = maybe_pt.value(); - - return std::make_pair(notePt, SaplingPaymentAddress(notePt.d, outPt->pk_d)); } // Couldn't recover with any of the provided OutgoingViewingKeys @@ -4466,45 +4383,6 @@ int64_t CWalletTx::GetTxTime() const return n ? n : nTimeReceived; } -int CWalletTx::GetRequestCount() const -{ - // Returns -1 if it wasn't being tracked - int nRequests = -1; - { - LOCK(pwallet->cs_wallet); - if (IsCoinBase()) - { - // Generated block - if (!hashBlock.IsNull()) - { - map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); - if (mi != pwallet->mapRequestCount.end()) - nRequests = (*mi).second; - } - } - else - { - // Did anyone request this transaction? - map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); - if (mi != pwallet->mapRequestCount.end()) - { - nRequests = (*mi).second; - - // How about the block it's in? - if (nRequests == 0 && !hashBlock.IsNull()) - { - map::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock); - if (_mi != pwallet->mapRequestCount.end()) - nRequests = (*_mi).second; - else - nRequests = 1; // If it's in someone else's block it must have got out - } - } - } - } - return nRequests; -} - // GetAmounts will determine the transparent debits and credits for a given wallet tx. void CWalletTx::GetAmounts(std::list& listReceived, std::list& listSent, CAmount& nFee, const isminefilter& filter) const @@ -5657,7 +5535,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt CAmount nTotalValue = nValue; if (nSubtractFeeFromAmount == 0) nTotalValue += nFeeRet; - double dPriority = 0; + // vouts to the payees for (const CRecipient& recipient : vecSend) { @@ -5674,7 +5552,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt } } - if (txout.IsDust(::minRelayTxFee)) + if (txout.IsDust()) { if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) { @@ -5706,18 +5584,6 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt } return false; } - for (std::pair pcoin : setCoins) - { - CAmount nCredit = pcoin.first->vout[pcoin.second].nValue; - //The coin age after the next block (depth+1) is used instead of the current, - //reflecting an assumption the user would accept a bit more delay for - //a chance at a free transaction. - //But mempool inputs might still be in the mempool, so their age stays 0 - int age = pcoin.first->GetDepthInMainChain(std::nullopt); - if (age != 0) - age += 1; - dPriority += (double)nCredit * age; - } CAmount nChange = nValueIn - nValue; if (nSubtractFeeFromAmount == 0) @@ -5758,16 +5624,16 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt // We do not move dust-change to fees, because the sender would end up paying more than requested. // This would be against the purpose of the all-inclusive feature. // So instead we raise the change and deduct from the recipient. - if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee)) + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust()) { - CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue; + CAmount nDust = newTxOut.GetDustThreshold() - newTxOut.nValue; newTxOut.nValue += nDust; // raise change until no more dust for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient { if (vecSend[i].fSubtractFeeFromAmount) { txNew.vout[i].nValue -= nDust; - if (txNew.vout[i].IsDust(::minRelayTxFee)) + if (txNew.vout[i].IsDust()) { strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); return false; @@ -5779,7 +5645,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt // Never create dust outputs; if we would, just // add the dust to the fee. - if (newTxOut.IsDust(::minRelayTxFee)) + if (newTxOut.IsDust()) { nFeeRet += nChange; reservekey.ReturnKey(); @@ -5837,15 +5703,6 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); - // Remove scriptSigs if we used dummy signatures for fee calculation - if (!sign) { - for (CTxIn& vin : txNew.vin) - vin.scriptSig = CScript(); - } - - // Embed the constructed transaction data in wtxNew. - *static_cast(&wtxNew) = CTransaction(txNew); - // Limit size if (nBytes >= max_tx_size) { @@ -5853,23 +5710,16 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt return false; } - dPriority = wtxNew.ComputePriority(dPriority, nBytes); - - // Can we complete this as a free transaction? - if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) - { - // Not enough fee: enough priority? - double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); - // Not enough mempool history to estimate: use hard-coded AllowFree. - if (dPriorityNeeded <= 0 && AllowFree(dPriority)) - break; + CAmount nFeeNeeded = GetMinimumFee(txNew, nBytes); - // Small enough, and priority high enough, to send for free - if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded) - break; + // Remove scriptSigs if we used dummy signatures for fee calculation + if (!sign) { + for (CTxIn& vin : txNew.vin) + vin.scriptSig = CScript(); } - CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); + // Embed the constructed transaction data in wtxNew. + *static_cast(&wtxNew) = CTransaction(txNew); // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. @@ -5928,9 +5778,6 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, std::optional 0 && nFeeNeeded < payTxFee.GetFeePerK()) - nFeeNeeded = payTxFee.GetFeePerK(); - // User didn't set: use -txconfirmtarget to estimate... - if (nFeeNeeded == 0) - nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes); - // ... unless we don't have enough mempool data, in which case fall - // back to the required fee + CAmount nFeeNeeded = payTxFee.GetFee(std::max((unsigned int)1000, nTxBytes)); + // User didn't set: use conventional fee if (nFeeNeeded == 0) - nFeeNeeded = GetRequiredFee(nTxBytes); - // prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee - if (nFeeNeeded < ::minRelayTxFee.GetFeeForRelay(nTxBytes)) - nFeeNeeded = ::minRelayTxFee.GetFeeForRelay(nTxBytes); - // But always obey the maximum - if (nFeeNeeded > maxTxFee) - nFeeNeeded = maxTxFee; - return nFeeNeeded; + nFeeNeeded = tx.GetConventionalFee(); + return ConstrainFee(nFeeNeeded, nTxBytes); } - - DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { if (!fFileBacked) @@ -6650,16 +6482,14 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); strUsage += HelpMessageOpt("-migration", _("Enable the Sprout to Sapling migration")); strUsage += HelpMessageOpt("-migrationdestaddress=", _("Set the Sapling migration address")); - strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); strUsage += HelpMessageOpt("-orchardactionlimit=", strprintf(_("Set the maximum number of Orchard actions permitted in a transaction (default %u)"), DEFAULT_ORCHARD_ACTION_LIMIT)); - strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), - CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("The preferred fee rate (in %s per 1000 bytes) used for transactions created by legacy APIs (sendtoaddress, sendmany, and fundrawtransaction). " + "If the transaction is less than 1000 bytes then the fee rate is applied as though it were 1000 bytes. When this option is not set, " + "the ZIP 317 fee calculation is used."), + CURRENCY_UNIT)); strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup (implies -rescan)")); - strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); - strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); strUsage += HelpMessageOpt("-txexpirydelta", strprintf(_("Set the number of blocks after which a transaction that has not been mined will become invalid (min: %u, default: %u (pre-Blossom) or %u (post-Blossom))"), TX_EXPIRING_SOON_THRESHOLD + 1, DEFAULT_PRE_BLOSSOM_TX_EXPIRY_DELTA, DEFAULT_POST_BLOSSOM_TX_EXPIRY_DELTA)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file absolute path or a path relative to the data directory") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); @@ -6864,11 +6694,7 @@ bool CWallet::ParameterInteraction(const CChainParams& params) { if (mapArgs.count("-mintxfee")) { - CAmount n = 0; - if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) - CWallet::minTxFee = CFeeRate(n); - else - return UIError(AmountErrMsg("mintxfee", mapArgs["-mintxfee"])); + UIWarning(_("The argument -mintxfee is no longer supported.")); } if (mapArgs.count("-paytxfee")) { @@ -6876,29 +6702,39 @@ bool CWallet::ParameterInteraction(const CChainParams& params) if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK)) return UIError(AmountErrMsg("paytxfee", mapArgs["-paytxfee"])); if (nFeePerK > HIGH_TX_FEE_PER_KB) - UIWarning(_("-paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); + UIWarning(_("-paytxfee is set to a very high fee rate! This is the fee rate you will pay if you send a transaction.")); payTxFee = CFeeRate(nFeePerK, 1000); if (payTxFee < ::minRelayTxFee) { - return UIError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), + return UIError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least the minimum relay fee rate %s)"), mapArgs["-paytxfee"], ::minRelayTxFee.ToString())); } } if (mapArgs.count("-maxtxfee")) { CAmount nMaxFee = 0; + CAmount lowMaxTxFee = CalculateConventionalFee(LOW_LOGICAL_ACTIONS); if (!ParseMoney(mapArgs["-maxtxfee"], nMaxFee)) return UIError(AmountErrMsg("maxtxfee", mapArgs["-maxtxfee"])); if (nMaxFee > HIGH_MAX_TX_FEE) - UIWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); + UIWarning(_("-maxtxfee is set to a very high fee rate! Fee rates this large could be paid on a single transaction.")); maxTxFee = nMaxFee; if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) { - return UIError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + return UIError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minimum relay fee of %s for a 1000-byte transaction, to prevent stuck transactions)"), mapArgs["-maxtxfee"], ::minRelayTxFee.ToString())); } + else if (maxTxFee < lowMaxTxFee) + { + UIWarning(strprintf(_("-maxtxfee is set to a very low fee (%s). The recommendation is to allow for at least %d logical actions at the conventional fee, which would be %s."), + mapArgs["-maxtxfee"], + LOW_LOGICAL_ACTIONS, + DisplayMoney(lowMaxTxFee))); + } + } + if (mapArgs.count("-txconfirmtarget")) { + UIWarning(_("The argument -txconfirmtarget is no longer supported.")); } - nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); if (mapArgs.count("-txexpirydelta")) { int64_t expiryDelta = atoi64(mapArgs["-txexpirydelta"]); uint32_t minExpiryDelta = TX_EXPIRING_SOON_THRESHOLD + 1; @@ -6908,7 +6744,10 @@ bool CWallet::ParameterInteraction(const CChainParams& params) expiryDeltaArg = expiryDelta; } bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS); + + if (GetBoolArg("-sendfreetransactions", false)) { + return UIError(_("The argument -sendfreetransactions is no longer supported.")); + } KeyIO keyIO(params); // Check Sapling migration address if set and is a valid Sapling address @@ -7023,7 +6862,7 @@ bool CMerkleTx::AcceptToMemoryPool(CValidationState& state, bool fLimitFree, boo NoteFilter NoteFilter::ForPaymentAddresses(const std::vector& paymentAddrs) { NoteFilter addrs; for (const auto& addr: paymentAddrs) { - std::visit(match { + examine(addr, match { [&](const CKeyID& keyId) { }, [&](const CScriptID& scriptId) { }, [&](const libzcash::SproutPaymentAddress& addr) { @@ -7034,7 +6873,7 @@ NoteFilter NoteFilter::ForPaymentAddresses(const std::vector(maybe_pa)); - auto pa = maybe_pa.value(); + assert(optDecrypted != std::nullopt); + SaplingNotePlaintext notePt; + SaplingPaymentAddress pa; + std::tie(notePt, pa) = optDecrypted.value(); // skip notes which do not conform to the filter, if supplied if (noteFilter.has_value() && !noteFilter.value().HasSaplingAddress(pa)) { @@ -7425,13 +7262,13 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const { auto hdChain = m_wallet->GetMnemonicHDChain(); - auto ufvkMeta = m_wallet->GetUFVKMetadataForAddress(uaddr); - if (ufvkMeta.has_value()) { + auto ufvkId = m_wallet->GetUFVKIdForAddress(uaddr); + if (ufvkId.has_value()) { // Look through the UFVKs that we have generated, and confirm that the - // seed fingerprint for the key we find for the ufvkMeta corresponds to + // seed fingerprint for the key we find for the ufvkId corresponds to // the wallet's mnemonic seed. for (const auto& [k, v] : m_wallet->mapUnifiedAccountKeys) { - if (v == ufvkMeta.value().GetUFVKId() && hdChain.has_value() && k.first == hdChain.value().GetSeedFingerprint()) { + if (v == ufvkId.value() && hdChain.has_value() && k.first == hdChain.value().GetSeedFingerprint()) { return PaymentAddressSource::MnemonicHDSeed; } } @@ -7617,9 +7454,6 @@ std::optional UFVKForReceiver::operator() auto ufvkMeta = wallet.GetUFVKMetadataForReceiver(keyId); if (ufvkMeta.has_value()) { auto ufvkid = ufvkMeta.value().GetUFVKId(); - // transparent address UFVK metadata is always accompanied by the child - // index at which the address was produced - assert(ufvkMeta.value().GetDiversifierIndex().has_value()); auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid); assert(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value()); return ufvk.value(); @@ -7699,10 +7533,7 @@ std::optional UnifiedAddressForReceiver::operator()(co auto ufvkMeta = wallet.GetUFVKMetadataForReceiver(keyId); if (ufvkMeta.has_value()) { auto ufvkid = ufvkMeta.value().GetUFVKId(); - // transparent address UFVK metadata is always accompanied by the child - // index at which the address was produced - assert(ufvkMeta.value().GetDiversifierIndex().has_value()); - diversifier_index_t j = ufvkMeta.value().GetDiversifierIndex().value(); + diversifier_index_t j = ufvkMeta.value().GetDiversifierIndex(); auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid); if (!(ufvk.has_value() && ufvk.value().GetTransparentKey().has_value())) { throw std::runtime_error("CWallet::UnifiedAddressForReceiver(): UFVK has no P2PKH key part."); @@ -7861,7 +7692,7 @@ bool TransactionStrategy::IsCompatibleWith(PrivacyPolicy policy) const { } bool ZTXOSelector::SelectsTransparent() const { - return std::visit(match { + return examine(this->pattern, match { [](const CKeyID& keyId) { return true; }, [](const CScriptID& scriptId) { return true; }, [](const libzcash::SproutPaymentAddress& addr) { return false; }, @@ -7873,49 +7704,39 @@ bool ZTXOSelector::SelectsTransparent() const { }, [](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetTransparentKey().has_value(); }, [](const AccountZTXOPattern& acct) { return acct.IncludesP2PKH() || acct.IncludesP2SH(); } - }, this->pattern); -} -bool ZTXOSelector::SelectsTransparentCoinbase() const { - return std::visit(match { - [](const CKeyID& keyId) { return true; }, - [](const CScriptID& scriptId) { return true; }, - [](const libzcash::SproutPaymentAddress& addr) { return false; }, - [](const libzcash::SproutViewingKey& vk) { return false; }, - [](const libzcash::SaplingPaymentAddress& addr) { return false; }, - [](const libzcash::SaplingExtendedFullViewingKey& vk) { return false; }, - [](const libzcash::UnifiedAddress& ua) { - return ua.GetP2PKHReceiver().has_value() || ua.GetP2SHReceiver().has_value(); - }, - [](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetTransparentKey().has_value(); }, - [](const AccountZTXOPattern& acct) { - return (acct.IncludesP2PKH() || acct.IncludesP2SH()) && acct.GetAccountId() != ZCASH_LEGACY_ACCOUNT; - } - }, this->pattern); + }); } bool ZTXOSelector::SelectsSprout() const { - return std::visit(match { + return transparentCoinbasePolicy != TransparentCoinbasePolicy::Require && examine(this->pattern, match { [](const libzcash::SproutViewingKey& addr) { return true; }, [](const libzcash::SproutPaymentAddress& extfvk) { return true; }, [](const auto& addr) { return false; } - }, this->pattern); + }); } bool ZTXOSelector::SelectsSapling() const { - return std::visit(match { + return transparentCoinbasePolicy != TransparentCoinbasePolicy::Require && examine(this->pattern, match { [](const libzcash::SaplingPaymentAddress& addr) { return true; }, [](const libzcash::SaplingExtendedSpendingKey& extfvk) { return true; }, [](const libzcash::UnifiedAddress& ua) { return ua.GetSaplingReceiver().has_value(); }, [](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetSaplingKey().has_value(); }, [](const AccountZTXOPattern& acct) { return acct.IncludesSapling(); }, [](const auto& addr) { return false; } - }, this->pattern); + }); } bool ZTXOSelector::SelectsOrchard() const { - return std::visit(match { + return transparentCoinbasePolicy != TransparentCoinbasePolicy::Require && examine(this->pattern, match { [](const libzcash::UnifiedAddress& ua) { return ua.GetOrchardReceiver().has_value(); }, [](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetOrchardKey().has_value(); }, [](const AccountZTXOPattern& acct) { return acct.IncludesOrchard(); }, [](const auto& addr) { return false; } - }, this->pattern); + }); +} + +void SpendableInputs::LimitTransparentUtxos(size_t maxUtxoCount) +{ + while (utxos.size() > maxUtxoCount) { + utxos.pop_back(); + } } bool SpendableInputs::LimitToAmount( @@ -7923,6 +7744,7 @@ bool SpendableInputs::LimitToAmount( const CAmount dustThreshold, const std::set& recipientPools) { + // dustThreshold cannot be zero because it is no longer configured via `-minrelaytxfee`. assert(amountRequired >= 0 && dustThreshold > 0); // Calling this method twice is a programming error. assert(!limited); @@ -8007,7 +7829,7 @@ bool SpendableInputs::LimitToAmount( // - If we have transparent recipients, we prefer to select funds across all // shielded pools before the transparent pool. The address and amount for // these recipients is necessarily revealed, but we can hide the sender. - // - If we don't have suffient funds in shielded pools and are required to + // - If we don't have sufficient funds in shielded pools and are required to // select transparent coins, we always select all transparent coins first. // Given that the transaction will necessarily reveal sender information, // we use it to opportunistically shield transparent coins. @@ -8066,9 +7888,6 @@ bool SpendableInputs::LimitToAmount( // Fully shielded. selectionOrder = { OutputPool::Orchard, - // Pools below here are erased. - OutputPool::Transparent, - OutputPool::Sapling, }; } else if ( recipientPools.count(OutputPool::Transparent) && @@ -8078,9 +7897,6 @@ bool SpendableInputs::LimitToAmount( // Fewer pools. selectionOrder = { OutputPool::Orchard, - // Pools below here are erased. - OutputPool::Transparent, - OutputPool::Sapling, }; } else if (wouldSuffice(availableSapling + availableOrchard)) { // Hide sender. @@ -8090,8 +7906,6 @@ bool SpendableInputs::LimitToAmount( selectionOrder = { OutputPool::Sapling, OutputPool::Orchard, - // Pools below here are erased. - OutputPool::Transparent, }; } else { // Opportunistic shielding. @@ -8103,14 +7917,25 @@ bool SpendableInputs::LimitToAmount( opportunisticShielding = true; } - // Ensure we provided a total selection order (so that all unselected notes - // and coins are erased). + // Erase all notes and coins from pools that aren’t used in selection. for (auto pool : available) { bool poolIsPresent = false; for (auto entry : selectionOrder) { poolIsPresent |= entry == pool; } - assert(poolIsPresent); + if (!poolIsPresent) { + switch (pool) { + case OutputPool::Transparent: + utxos.clear(); + break; + case OutputPool::Sapling: + saplingNoteEntries.clear(); + break; + case OutputPool::Orchard: + orchardNoteMetadata.clear(); + break; + } + } } // Finally, select the remaining notes and coins based on this order. diff --git a/depend/zcash/src/wallet/wallet.h b/depend/zcash/src/wallet/wallet.h index ba4989fb9..d371eeca6 100644 --- a/depend/zcash/src/wallet/wallet.h +++ b/depend/zcash/src/wallet/wallet.h @@ -42,7 +42,7 @@ #include #include -#include +#include #include @@ -52,9 +52,7 @@ extern CWallet* pwalletMain; * Settings */ extern CFeeRate payTxFee; -extern unsigned int nTxConfirmTarget; extern bool bSpendZeroConfChange; -extern bool fSendFreeTransactions; extern bool fPayAtLeastCustomFee; extern unsigned int nAnchorConfirmations; // The maximum number of Orchard actions permitted within a single transaction. @@ -64,18 +62,10 @@ extern unsigned int nOrchardActionLimit; static const unsigned int DEFAULT_KEYPOOL_SIZE = 100; //! -paytxfee default static const CAmount DEFAULT_TRANSACTION_FEE = 0; -//! -mintxfee default -static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000; //! minimum change amount static const CAmount MIN_CHANGE = CENT; //! Default for -spendzeroconfchange static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true; -//! Default for -sendfreetransactions -static const bool DEFAULT_SEND_FREE_TRANSACTIONS = false; -//! -txconfirmtarget default -static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2; -//! Largest (in bytes) free transaction we're willing to create -static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; static const bool DEFAULT_WALLETBROADCAST = true; //! Size of witness cache // Should be large enough that we can expect not to reorg beyond our cache @@ -660,19 +650,34 @@ class CWalletTx : public CMerkleTx std::pair DecryptSproutNote( JSOutPoint jsop) const; + /** + * Decrypt the specified Sapling output of this wallet transaction. + * + * Returns `std::nullopt` if we don't know how to decrypt this output + * (because it could not be decrypted during wallet scanning). + * + * Decryption is always performed as if the ZIP 212 grace window is active + * (accepting both v1 and v2 note plaintexts), because we know that any + * decryptable output will have had its plaintext version checked when it + * first entered the wallet. + */ std::optional> DecryptSaplingNote(const Consensus::Params& params, int height, SaplingOutPoint op) const; - std::optional> DecryptSaplingNoteWithoutLeadByteCheck(SaplingOutPoint op) const; + libzcash::SaplingPaymentAddress>> DecryptSaplingNote(const CChainParams& params, SaplingOutPoint op) const; + /** + * Try to recover the specified Sapling output of this wallet transaction + * using one of the given outgoing viewing keys. + * + * Returns `std::nullopt` if none of the `ovks` can decrypt this output. + * + * Decryption is always performed as if the ZIP 212 grace window is active + * (accepting both v1 and v2 note plaintexts), because the v2 plaintext + * format protects against an attack on the recipient, not the sender. + */ std::optional> RecoverSaplingNote(const Consensus::Params& params, int height, + libzcash::SaplingPaymentAddress>> RecoverSaplingNote(const CChainParams& params, SaplingOutPoint op, std::set& ovks) const; - std::optional> RecoverSaplingNoteWithoutLeadByteCheck(SaplingOutPoint op, std::set& ovks) const; OrchardActions RecoverOrchardActions(const std::vector& ovks) const; //! filter decides which addresses will count towards the debit @@ -691,7 +696,6 @@ class CWalletTx : public CMerkleTx bool IsTrusted(const std::optional& asOfHeight) const; int64_t GetTxTime() const; - int GetRequestCount() const; bool RelayWalletTransaction(); @@ -890,13 +894,27 @@ typedef std::variant< libzcash::UnifiedFullViewingKey, AccountZTXOPattern> ZTXOPattern; +/** + * For transactions, either `Disallow` or `Require` must be used, but `Allow` is generally used when + * calculating balances. + */ +enum class TransparentCoinbasePolicy { + Disallow, //!< Do not select transparent coinbase + Allow, //!< Make transparent coinbase available to the selector + Require //!< Only select transparent coinbase +}; + class ZTXOSelector { private: ZTXOPattern pattern; bool requireSpendingKeys; + TransparentCoinbasePolicy transparentCoinbasePolicy; - ZTXOSelector(ZTXOPattern patternIn, bool requireSpendingKeysIn): - pattern(patternIn), requireSpendingKeys(requireSpendingKeysIn) {} + ZTXOSelector(ZTXOPattern patternIn, bool requireSpendingKeysIn, TransparentCoinbasePolicy transparentCoinbasePolicy): + pattern(patternIn), requireSpendingKeys(requireSpendingKeysIn), transparentCoinbasePolicy(transparentCoinbasePolicy) { + // We can’t require transparent coinbase unless we’re selecting transparent funds. + assert(SelectsTransparent() || transparentCoinbasePolicy != TransparentCoinbasePolicy::Require); +} friend class CWallet; public: @@ -908,8 +926,11 @@ class ZTXOSelector { return requireSpendingKeys; } + TransparentCoinbasePolicy TransparentCoinbasePolicy() const { + return transparentCoinbasePolicy; + } + bool SelectsTransparent() const; - bool SelectsTransparentCoinbase() const; bool SelectsSprout() const; bool SelectsSapling() const; bool SelectsOrchard() const; @@ -932,6 +953,11 @@ class SpendableInputs { std::vector saplingNoteEntries; std::vector orchardNoteMetadata; + /** + * Retain the first `maxUtxoCount` utxos, and discard the rest. + */ + void LimitTransparentUtxos(size_t maxUtxoCount); + /** * Selectively discard notes that are not required to obtain the desired * amount. Returns `false` if the available inputs do not add up to the @@ -953,14 +979,14 @@ class SpendableInputs { */ CAmount Total() const { CAmount result = 0; - result += GetTransparentBalance(); - result += GetSproutBalance(); - result += GetSaplingBalance(); - result += GetOrchardBalance(); + result += GetTransparentTotal(); + result += GetSproutTotal(); + result += GetSaplingTotal(); + result += GetOrchardTotal(); return result; } - CAmount GetTransparentBalance() const { + CAmount GetTransparentTotal() const { CAmount result = 0; for (const auto& t : utxos) { result += t.Value(); @@ -968,7 +994,7 @@ class SpendableInputs { return result; } - CAmount GetSproutBalance() const { + CAmount GetSproutTotal() const { CAmount result = 0; for (const auto& t : sproutNoteEntries) { result += t.note.value(); @@ -976,7 +1002,7 @@ class SpendableInputs { return result; } - CAmount GetSaplingBalance() const { + CAmount GetSaplingTotal() const { CAmount result = 0; for (const auto& t : saplingNoteEntries) { result += t.note.value(); @@ -984,7 +1010,7 @@ class SpendableInputs { return result; } - CAmount GetOrchardBalance() const { + CAmount GetOrchardTotal() const { CAmount result = 0; for (const auto& t : orchardNoteMetadata) { result += t.GetNoteValue(); @@ -1476,7 +1502,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface TxItems wtxOrdered; int64_t nOrderPosNext; - std::map mapRequestCount; std::map mapAddressBook; @@ -1528,6 +1553,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::optional ZTXOSelectorForAccount( libzcash::AccountId account, bool requireSpendingKey, + TransparentCoinbasePolicy transparentCoinbasePolicy, std::set receiverTypes={}) const; /** @@ -1539,6 +1565,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::optional ZTXOSelectorForAddress( const libzcash::PaymentAddress& addr, bool requireSpendingKey, + TransparentCoinbasePolicy transparentCoinbasePolicy, bool allowAddressLinkability) const; /** @@ -1549,13 +1576,14 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ std::optional ZTXOSelectorForViewingKey( const libzcash::ViewingKey& vk, - bool requireSpendingKey) const; + bool requireSpendingKey, + TransparentCoinbasePolicy transparentCoinbasePolicy) const; /** * Returns the ZTXO selector that will select UTXOs sent to legacy * transparent addresses managed by this wallet. */ - static ZTXOSelector LegacyTransparentZTXOSelector(bool requireSpendingKey); + static ZTXOSelector LegacyTransparentZTXOSelector(bool requireSpendingKey, TransparentCoinbasePolicy transparentCoinbasePolicy); /** * Look up the account for a given selector. This resolves the account ID @@ -1585,7 +1613,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface SpendableInputs FindSpendableInputs( ZTXOSelector paymentSource, - bool allowTransparentCoinbase, uint32_t minDepth, const std::optional& asOfHeight) const; @@ -1917,17 +1944,17 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool CommitTransaction(CWalletTx& wtxNew, std::optional> reservekey, CValidationState& state); - static CFeeRate minTxFee; - /** - * Estimate the minimum fee considering user set parameters - * and the required fee + /** Adjust the requested fee by bounding it below to the minimum relay fee required + * for a transaction of the given size and bounding it above to the maximum fee + * configured using the `-maxtxfee` configuration option. */ - static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool); + static CAmount ConstrainFee(CAmount requestedFee, unsigned int nTxBytes); + /** - * Return the minimum required fee taking into account the - * floating relay fee and user set minimum transaction fee + * Estimate the minimum fee considering user set parameters + * and the required fee. */ - static CAmount GetRequiredFee(unsigned int nTxBytes); + static CAmount GetMinimumFee(const CTransaction& tx, unsigned int nTxBytes); /** * The set of default receiver types used when the wallet generates @@ -1956,7 +1983,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface uint8_t n) const; mapSproutNoteData_t FindMySproutNotes(const CTransaction& tx) const; std::pair FindMySaplingNotes( - const Consensus::Params& consensus, + const CChainParams& params, const CTransaction& tx, int height) const; bool IsSproutNullifierFromMe(const uint256& nullifier) const; @@ -2031,22 +2058,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void UpdatedTransaction(const uint256 &hashTx); - void Inventory(const uint256 &hash) - { - { - LOCK(cs_wallet); - std::map::iterator mi = mapRequestCount.find(hash); - if (mi != mapRequestCount.end()) - (*mi).second++; - } - } - void GetAddressForMining(std::optional &minerAddress); - void ResetRequestCount(const uint256 &hash) - { - LOCK(cs_wallet); - mapRequestCount[hash] = 0; - }; unsigned int GetKeyPoolSize() { diff --git a/depend/zcash/src/wallet/wallet_tx_builder.cpp b/depend/zcash/src/wallet/wallet_tx_builder.cpp new file mode 100644 index 000000000..968e90384 --- /dev/null +++ b/depend/zcash/src/wallet/wallet_tx_builder.cpp @@ -0,0 +1,1108 @@ +// Copyright (c) 2022 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "util/moneystr.h" +#include "wallet/wallet_tx_builder.h" +#include "zip317.h" + +using namespace libzcash; + +int GetAnchorHeight(const CChain& chain, uint32_t anchorConfirmations) +{ + int nextBlockHeight = chain.Height() + 1; + return std::max(0, nextBlockHeight - (int) anchorConfirmations); +} + +static size_t PadCount(size_t n) +{ + return n == 1 ? 2 : n; +} + +static CAmount +CalcZIP317Fee( + const std::optional& inputs, + const std::vector& payments, + const std::optional& changeAddr) +{ + std::vector vout{}; + size_t sproutOutputCount{}, saplingOutputCount{}, orchardOutputCount{}; + for (const auto& payment : payments) { + std::visit(match { + [&](const CKeyID& addr) { + vout.emplace_back(payment.amount, GetScriptForDestination(addr)); + }, + [&](const CScriptID& addr) { + vout.emplace_back(payment.amount, GetScriptForDestination(addr)); + }, + [&](const libzcash::SaplingPaymentAddress&) { + ++saplingOutputCount; + }, + [&](const libzcash::OrchardRawAddress&) { + ++orchardOutputCount; + } + }, payment.address); + } + + if (changeAddr.has_value()) { + examine(changeAddr.value(), match { + [&](const SproutPaymentAddress&) { ++sproutOutputCount; }, + [&](const RecipientAddress& addr) { + examine(addr, match { + [&](const CKeyID& taddr) { + vout.emplace_back(0, GetScriptForDestination(taddr)); + }, + [&](const CScriptID taddr) { + vout.emplace_back(0, GetScriptForDestination(taddr)); + }, + [&](const libzcash::SaplingPaymentAddress&) { ++saplingOutputCount; }, + [&](const libzcash::OrchardRawAddress&) { ++orchardOutputCount; } + }); + } + }); + } + + std::vector vin{}; + size_t sproutInputCount = 0; + size_t saplingInputCount = 0; + size_t orchardInputCount = 0; + if (inputs.has_value()) { + for (const auto& utxo : inputs.value().utxos) { + vin.emplace_back( + COutPoint(utxo.tx->GetHash(), utxo.i), + utxo.tx->vout[utxo.i].scriptPubKey); + } + sproutInputCount = inputs.value().sproutNoteEntries.size(); + saplingInputCount = inputs.value().saplingNoteEntries.size(); + orchardInputCount = inputs.value().orchardNoteMetadata.size(); + } + size_t logicalActionCount = CalculateLogicalActionCount( + vin, + vout, + std::max(sproutInputCount, sproutOutputCount), + saplingInputCount, + PadCount(saplingOutputCount), + PadCount(std::max(orchardInputCount, orchardOutputCount))); + + return CalculateConventionalFee(logicalActionCount); +} + +static tl::expected +ResolvePayment( + const Payment& payment, + bool canResolveOrchard, + const TransactionStrategy& strategy, + CAmount& maxSaplingAvailable, + CAmount& maxOrchardAvailable, + uint32_t& orchardOutputs) +{ + return examine(payment.GetAddress(), match { + [&](const CKeyID& p2pkh) -> tl::expected { + if (strategy.AllowRevealedRecipients()) { + return {{std::nullopt, p2pkh, payment.GetAmount(), payment.GetMemo(), false}}; + } else { + return tl::make_unexpected(AddressResolutionError::TransparentRecipientNotAllowed); + } + }, + [&](const CScriptID& p2sh) -> tl::expected { + if (strategy.AllowRevealedRecipients()) { + return {{std::nullopt, p2sh, payment.GetAmount(), payment.GetMemo(), false}}; + } else { + return tl::make_unexpected(AddressResolutionError::TransparentRecipientNotAllowed); + } + }, + [&](const SproutPaymentAddress&) -> tl::expected { + return tl::make_unexpected(AddressResolutionError::SproutRecipientsNotSupported); + }, + [&](const SaplingPaymentAddress& addr) + -> tl::expected { + if (strategy.AllowRevealedAmounts() || payment.GetAmount() <= maxSaplingAvailable) { + if (!strategy.AllowRevealedAmounts()) { + maxSaplingAvailable -= payment.GetAmount(); + } + return {{std::nullopt, addr, payment.GetAmount(), payment.GetMemo(), false}}; + } else { + return tl::make_unexpected(AddressResolutionError::RevealingSaplingAmountNotAllowed); + } + }, + [&](const UnifiedAddress& ua) -> tl::expected { + if (canResolveOrchard + && ua.GetOrchardReceiver().has_value() + && (strategy.AllowRevealedAmounts() || payment.GetAmount() <= maxOrchardAvailable) + ) { + if (!strategy.AllowRevealedAmounts()) { + maxOrchardAvailable -= payment.GetAmount(); + } + orchardOutputs += 1; + return {{ + ua, + ua.GetOrchardReceiver().value(), + payment.GetAmount(), + payment.GetMemo(), + false + }}; + } else if (ua.GetSaplingReceiver().has_value() + && (strategy.AllowRevealedAmounts() || payment.GetAmount() <= maxSaplingAvailable) + ) { + if (!strategy.AllowRevealedAmounts()) { + maxSaplingAvailable -= payment.GetAmount(); + } + return {{ua, ua.GetSaplingReceiver().value(), payment.GetAmount(), payment.GetMemo(), false}}; + } else { + if (strategy.AllowRevealedRecipients()) { + if (ua.GetP2SHReceiver().has_value()) { + return {{ + ua, ua.GetP2SHReceiver().value(), payment.GetAmount(), std::nullopt, false}}; + } else if (ua.GetP2PKHReceiver().has_value()) { + return {{ + ua, ua.GetP2PKHReceiver().value(), payment.GetAmount(), std::nullopt, false}}; + } else { + // This should only occur when we have + // • an Orchard-only UA, + // • `AllowRevealedRecipients`, and + // • can’t resolve Orchard (which means either a Sprout selector or pre-NU5). + return tl::make_unexpected(AddressResolutionError::CouldNotResolveReceiver); + } + } else if (strategy.AllowRevealedAmounts()) { + return tl::make_unexpected(AddressResolutionError::TransparentReceiverNotAllowed); + } else { + return tl::make_unexpected(AddressResolutionError::RevealingReceiverAmountsNotAllowed); + } + } + } + }); +} + +InvalidFundsError ReportInvalidFunds( + const SpendableInputs& spendable, + bool hasPhantomChange, + CAmount fee, + CAmount dustThreshold, + CAmount targetAmount, + CAmount changeAmount) +{ + return InvalidFundsError( + spendable.Total(), + hasPhantomChange + // TODO: NEED TESTS TO EXERCISE THIS + ? InvalidFundsReason(PhantomChangeError(fee, dustThreshold)) + : (changeAmount > 0 && changeAmount < dustThreshold + // TODO: we should provide the option for the caller to explicitly forego change + // (definitionally an amount below the dust amount) and send the extra to the + // recipient or the miner fee to avoid creating dust change, rather than prohibit + // them from sending entirely in this circumstance. (Daira disagrees, as this could + // leak information to the recipient or publicly in the fee.) + ? InvalidFundsReason(DustThresholdError(dustThreshold, changeAmount)) + : InvalidFundsReason(InsufficientFundsError(targetAmount)))); +} + +static tl::expected +ValidateAmount(const SpendableInputs& spendable, const CAmount& fee) +{ + // TODO: The actual requirement should probably be higher than simply `fee` – do we need to + // take into account the dustThreshold when adding an output? But, this was the + // pre-WalletTxBuilder behavior, so it’s fine to maintain it for now. + auto targetAmount = fee; + if (spendable.Total() < targetAmount) + return tl::make_unexpected(ReportInvalidFunds(spendable, false, fee, 0, targetAmount, 0)); + else + return {}; +} + +static tl::expected, InputSelectionError> +ResolveNetPayment( + const ZTXOSelector& selector, + const SpendableInputs& spendable, + const NetAmountRecipient& netpay, + const std::optional& fee, + const TransactionStrategy& strategy, + bool afterNU5) +{ + bool canResolveOrchard = afterNU5 && !selector.SelectsSprout(); + CAmount maxSaplingAvailable = spendable.GetSaplingTotal(); + CAmount maxOrchardAvailable = spendable.GetOrchardTotal(); + uint32_t orchardOutputs{0}; + + // We initially resolve the payment with `MINIMUM_FEE` so that we can use the payment to + // calculate the actual fee. + auto initialFee = fee.value_or(MINIMUM_FEE); + return ValidateAmount(spendable, initialFee) + .and_then([&](void) { + // Needed so that the initial call to `ResolvePayment` (which is just a placeholder used + // to help calculate the fee) doesn’t accidentally decrement funds. + CAmount tempMaxSapling = maxSaplingAvailable; + CAmount tempMaxOrchard = maxOrchardAvailable; + return ResolvePayment( + Payment(netpay.first, spendable.Total() - initialFee, netpay.second), + canResolveOrchard, + strategy, + tempMaxSapling, + tempMaxOrchard, + orchardOutputs) + .map_error([](const auto& error) -> InputSelectionError { return error; }) + .and_then([&](const auto& rpayment) { + auto finalFee = fee.value_or(CalcZIP317Fee(spendable, {rpayment}, std::nullopt)); + return ValidateAmount(spendable, finalFee) + .and_then([&](void) { + return ResolvePayment( + Payment(netpay.first, spendable.Total() - finalFee, netpay.second), + canResolveOrchard, + strategy, + maxSaplingAvailable, + maxOrchardAvailable, + orchardOutputs) + .map([&](const auto& actualPayment) { + return std::make_pair(actualPayment, finalFee); + }) + .map_error([](const auto& error) -> InputSelectionError { return error; }); + }); + }); + }); +} + +tl::expected +WalletTxBuilder::GetChangeAddress( + CWallet& wallet, + const ZTXOSelector& selector, + const SpendableInputs& spendable, + const Payments& resolvedPayments, + const TransactionStrategy& strategy, + bool afterNU5) const +{ + // Determine the account we're sending from. + auto sendFromAccount = wallet.FindAccountForSelector(selector).value_or(ZCASH_LEGACY_ACCOUNT); + + auto getAllowedChangePools = [&](const std::set& receiverTypes) { + std::set result{resolvedPayments.GetRecipientPools()}; + // We always allow shielded change when not sending from the legacy account. + if (sendFromAccount != ZCASH_LEGACY_ACCOUNT) { + result.insert(OutputPool::Sapling); + } + for (ReceiverType rtype : receiverTypes) { + switch (rtype) { + case ReceiverType::P2PKH: + case ReceiverType::P2SH: + if (strategy.AllowRevealedRecipients()) { + result.insert(OutputPool::Transparent); + } + break; + case ReceiverType::Sapling: + if (!spendable.saplingNoteEntries.empty() || strategy.AllowRevealedAmounts()) { + result.insert(OutputPool::Sapling); + } + break; + case ReceiverType::Orchard: + if (afterNU5 + && (!spendable.orchardNoteMetadata.empty() || strategy.AllowRevealedAmounts())) { + result.insert(OutputPool::Orchard); + } + break; + } + } + return result; + }; + + auto changeAddressForTransparentSelector = [&](const std::set& receiverTypes) + -> tl::expected { + auto addr = wallet.GenerateChangeAddressForAccount( + sendFromAccount, + getAllowedChangePools(receiverTypes)); + if (addr.has_value()) { + return {addr.value()}; + } else { + return tl::make_unexpected(AddressResolutionError::TransparentChangeNotAllowed); + } + }; + + auto changeAddressForSaplingAddress = [&](const libzcash::SaplingPaymentAddress& addr) + -> RecipientAddress { + // for Sapling, if using a legacy address, return change to the + // originating address; otherwise return it to the Sapling internal + // address corresponding to the UFVK. + if (sendFromAccount == ZCASH_LEGACY_ACCOUNT) { + return addr; + } else { + auto addr = wallet.GenerateChangeAddressForAccount( + sendFromAccount, + getAllowedChangePools({ReceiverType::Sapling})); + assert(addr.has_value()); + return addr.value(); + } + }; + + auto changeAddressForZUFVK = [&]( + const ZcashdUnifiedFullViewingKey& zufvk, + const std::set& receiverTypes) { + auto addr = zufvk.GetChangeAddress(getAllowedChangePools(receiverTypes)); + assert(addr.has_value()); + return addr.value(); + }; + + return examine(selector.GetPattern(), match { + [&](const CKeyID&) { + return changeAddressForTransparentSelector({ReceiverType::P2PKH}); + }, + [&](const CScriptID&) { + return changeAddressForTransparentSelector({ReceiverType::P2SH}); + }, + [](const libzcash::SproutPaymentAddress& addr) + -> tl::expected { + // for Sprout, we return change to the originating address using the tx builder. + return addr; + }, + [](const libzcash::SproutViewingKey& vk) + -> tl::expected { + // for Sprout, we return change to the originating address using the tx builder. + return vk.address(); + }, + [&](const libzcash::SaplingPaymentAddress& addr) + -> tl::expected { + return changeAddressForSaplingAddress(addr); + }, + [&](const libzcash::SaplingExtendedFullViewingKey& fvk) + -> tl::expected { + return changeAddressForSaplingAddress(fvk.DefaultAddress()); + }, + [&](const libzcash::UnifiedAddress& ua) + -> tl::expected { + auto zufvk = wallet.GetUFVKForAddress(ua); + assert(zufvk.has_value()); + return changeAddressForZUFVK(zufvk.value(), ua.GetKnownReceiverTypes()); + }, + [&](const libzcash::UnifiedFullViewingKey& fvk) + -> tl::expected { + return changeAddressForZUFVK( + ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(params, fvk), + fvk.GetKnownReceiverTypes()); + }, + [&](const AccountZTXOPattern& acct) -> tl::expected { + auto addr = wallet.GenerateChangeAddressForAccount( + acct.GetAccountId(), + getAllowedChangePools(acct.GetReceiverTypes())); + assert(addr.has_value()); + return addr.value(); + } + }); +} + +tl::expected +WalletTxBuilder::PrepareTransaction( + CWallet& wallet, + const ZTXOSelector& selector, + const SpendableInputs& spendable, + const Recipients& payments, + const CChain& chain, + const TransactionStrategy& strategy, + const std::optional& fee, + uint32_t anchorConfirmations) const +{ + if (fee.has_value()) { + if (maxTxFee < fee.value()) { + return tl::make_unexpected(MaxFeeError(fee.value())); + } else if (!MoneyRange(fee.value())) { + // TODO: This check will be obviated by #6579. + return tl::make_unexpected(InvalidFeeError(fee.value())); + } + } + + int anchorHeight = GetAnchorHeight(chain, anchorConfirmations); + bool afterNU5 = params.GetConsensus().NetworkUpgradeActive(anchorHeight, Consensus::UPGRADE_NU5); + auto selected = examine(payments, match { + [&](const std::vector& payments) { + return ResolveInputsAndPayments( + wallet, + selector, + spendable, + payments, + chain, + strategy, + fee, + afterNU5); + }, + [&](const NetAmountRecipient& netRecipient) { + return ResolveNetPayment(selector, spendable, netRecipient, fee, strategy, afterNU5) + .map([&](const auto& pair) { + const auto& [payment, finalFee] = pair; + return InputSelection(spendable, {{payment}}, finalFee, std::nullopt); + }); + }, + }); + return selected.map([&](const InputSelection& resolvedSelection) { + auto ovks = SelectOVKs(wallet, selector, spendable); + + return TransactionEffects( + anchorConfirmations, + resolvedSelection.GetInputs(), + resolvedSelection.GetPayments(), + resolvedSelection.GetChangeAddress(), + resolvedSelection.GetFee(), + ovks.first, + ovks.second, + anchorHeight); + }); +} + +const SpendableInputs& InputSelection::GetInputs() const { + return inputs; +} + +const Payments& InputSelection::GetPayments() const { + return payments; +} + +CAmount InputSelection::GetFee() const { + return fee; +} + +const std::optional InputSelection::GetChangeAddress() const { + return changeAddr; +} + +CAmount WalletTxBuilder::DefaultDustThreshold() const { + CKey secret{CKey::TestOnlyRandomKey(true)}; + CScript scriptPubKey = GetScriptForDestination(secret.GetPubKey().GetID()); + CTxOut txout(CAmount(1), scriptPubKey); + return txout.GetDustThreshold(); +} + +SpendableInputs WalletTxBuilder::FindAllSpendableInputs( + const CWallet& wallet, + const ZTXOSelector& selector, + int32_t minDepth) const +{ + LOCK2(cs_main, wallet.cs_wallet); + return wallet.FindSpendableInputs(selector, minDepth, std::nullopt); +} + +CAmount GetConstrainedFee( + const std::optional& inputs, + const std::vector& payments, + const std::optional& changeAddr) +{ + // We know that minRelayFee <= MINIMUM_FEE <= conventional_fee, so we can use an arbitrary + // transaction size when constraining the fee, because we are guaranteed to already satisfy the + // lower bound. + constexpr unsigned int DUMMY_TX_SIZE = 1; + + return CWallet::ConstrainFee(CalcZIP317Fee(inputs, payments, changeAddr), DUMMY_TX_SIZE); +} + +static tl::expected +AddChangePayment( + const SpendableInputs& spendable, + Payments& resolvedPayments, + const ChangeAddress& changeAddr, + CAmount changeAmount, + CAmount targetAmount) +{ + assert(changeAmount > 0); + + // When spending transparent coinbase outputs, all inputs must be fully consumed. + if (spendable.HasTransparentCoinbase()) { + return tl::make_unexpected(ChangeNotAllowedError(spendable.Total(), targetAmount)); + } + + examine(changeAddr, match { + // TODO: Once we can add Sprout change to `resolvedPayments`, we don’t need to pass + // `changeAddr` around the rest of these functions. + [](const libzcash::SproutPaymentAddress&) {}, + [](const libzcash::SproutViewingKey&) {}, + [&](const auto& sendTo) { + resolvedPayments.AddPayment( + ResolvedPayment(std::nullopt, sendTo, changeAmount, std::nullopt, true)); + } + }); + + return {}; +} + +/// On the initial call, we haven’t yet selected inputs, so we assume the outputs dominate the +/// actions. +/// +/// 1. calc fee using only resolvedPayments to set a lower bound on the actual fee +/// • this also needs to know which pool change is going to, so it can determine what the fee is +/// with change _if_ there is change +/// 2. iterate over LimitToAmount until the updated fee (now including spends) matches the expected +/// fee +tl::expected< + std::tuple>, + InputSelectionError> +WalletTxBuilder::IterateLimit( + CWallet& wallet, + const ZTXOSelector& selector, + const TransactionStrategy& strategy, + CAmount sendAmount, + CAmount dustThreshold, + const SpendableInputs& spendable, + Payments& resolved, + bool afterNU5) const +{ + SpendableInputs spendableMut; + + auto previousFee = MINIMUM_FEE; + auto updatedFee = GetConstrainedFee(std::nullopt, resolved.GetResolvedPayments(), std::nullopt); + // This is used to increase the target amount just enough (generally by 0 or 1) to force + // selection of additional notes. + CAmount bumpTargetAmount{0}; + std::optional changeAddr; + CAmount changeAmount{0}; + CAmount targetAmount{0}; + + do { + // NB: This makes a fresh copy so that we start from the full set of notes when we re-limit. + spendableMut = spendable; + + targetAmount = sendAmount + updatedFee; + + // TODO: the set of recipient pools is not quite sufficient information here; we should + // probably perform note selection at the same time as we're performing resolved payment + // construction above. + bool foundSufficientFunds = + spendableMut.LimitToAmount( + targetAmount + bumpTargetAmount, + dustThreshold, + resolved.GetRecipientPools()); + changeAmount = spendableMut.Total() - targetAmount; + if (foundSufficientFunds) { + // Don’t want to generate a change address if we don’t need one (because it could be + // fresh) and once we generate it, hold onto it. But we still don’t have a guarantee + // that we won’t end up discarding it. + if (changeAmount > 0 && !changeAddr.has_value()) { + auto maybeChangeAddr = GetChangeAddress( + wallet, + selector, + spendableMut, + resolved, + strategy, + afterNU5); + + if (maybeChangeAddr.has_value()) { + changeAddr = maybeChangeAddr.value(); + } else { + return tl::make_unexpected(maybeChangeAddr.error()); + } + } + previousFee = updatedFee; + updatedFee = GetConstrainedFee( + spendableMut, + resolved.GetResolvedPayments(), + changeAmount > 0 ? changeAddr : std::nullopt); + } else { + return tl::make_unexpected( + ReportInvalidFunds( + spendableMut, + bumpTargetAmount != 0, + previousFee, + dustThreshold, + targetAmount, + changeAmount)); + } + // This happens when we have exactly `MARGINAL_FEE` change, then add a change output that + // causes the conventional fee to consume that change, leaving us with no change, which then + // lowers the fee. + if (updatedFee < previousFee) { + // Bump the updated fee so that we don’t exit the loop, but should force us to take an + // extra note (or fail) in the next `LimitToAmount`. + bumpTargetAmount = 1; + } + } while (updatedFee != previousFee); + + if (changeAmount > 0) { + assert(changeAddr.has_value()); + + auto changeRes = + AddChangePayment(spendableMut, resolved, changeAddr.value(), changeAmount, targetAmount); + if (!changeRes.has_value()) { + return tl::make_unexpected(changeRes.error()); + } + } + + return std::make_tuple(spendableMut, updatedFee, changeAddr); +} + +tl::expected +WalletTxBuilder::ResolveInputsAndPayments( + CWallet& wallet, + const ZTXOSelector& selector, + const SpendableInputs& spendable, + const std::vector& payments, + const CChain& chain, + const TransactionStrategy& strategy, + const std::optional& fee, + bool afterNU5) const +{ + LOCK2(cs_main, wallet.cs_wallet); + + // This is a simple greedy algorithm to attempt to preserve requested + // transactional privacy while moving as much value to the most recent pool + // as possible. This will also perform opportunistic shielding if the + // transaction strategy permits. + + CAmount maxSaplingAvailable = spendable.GetSaplingTotal(); + CAmount maxOrchardAvailable = spendable.GetOrchardTotal(); + uint32_t orchardOutputs{0}; + + // we can only select Orchard addresses if we’re not sending from Sprout, since there is no tx + // version where both Sprout and Orchard are valid. + bool canResolveOrchard = afterNU5 && !selector.SelectsSprout(); + std::vector resolvedPayments; + std::optional resolutionError; + for (const auto& payment : payments) { + auto res = ResolvePayment(payment, canResolveOrchard, strategy, maxSaplingAvailable, maxOrchardAvailable, orchardOutputs); + res.map([&](const ResolvedPayment& rpayment) { resolvedPayments.emplace_back(rpayment); }); + if (!res.has_value()) { + return tl::make_unexpected(res.error()); + } + } + auto resolved = Payments(resolvedPayments); + + if (orchardOutputs > this->maxOrchardActions) { + return tl::make_unexpected( + ExcessOrchardActionsError( + ActionSide::Output, + orchardOutputs, + this->maxOrchardActions)); + } + + // Set the dust threshold so that we can select enough inputs to avoid + // creating dust change amounts. + CAmount dustThreshold{this->DefaultDustThreshold()}; + + // Determine the target totals + CAmount sendAmount{0}; + for (const auto& payment : payments) { + sendAmount += payment.GetAmount(); + } + + SpendableInputs spendableMut; + CAmount finalFee; + CAmount targetAmount; + std::optional changeAddr; + if (fee.has_value()) { + spendableMut = spendable; + finalFee = fee.value(); + targetAmount = sendAmount + finalFee; + // TODO: the set of recipient pools is not quite sufficient information here; we should + // probably perform note selection at the same time as we're performing resolved payment + // construction above. + bool foundSufficientFunds = spendableMut.LimitToAmount( + targetAmount, + dustThreshold, + resolved.GetRecipientPools()); + CAmount changeAmount{spendableMut.Total() - targetAmount}; + if (!foundSufficientFunds) { + return tl::make_unexpected( + ReportInvalidFunds( + spendableMut, + false, + finalFee, + dustThreshold, + targetAmount, + changeAmount)); + } + if (changeAmount > 0) { + auto maybeChangeAddr = GetChangeAddress( + wallet, + selector, + spendableMut, + resolved, + strategy, + afterNU5); + + if (maybeChangeAddr.has_value()) { + changeAddr = maybeChangeAddr.value(); + } else { + return tl::make_unexpected(maybeChangeAddr.error()); + } + + // TODO: This duplicates the check in the `else` branch of the containing `if`. Until we + // can add Sprout change to `Payments` (#5660), we need to check this before + // adding the change payment. We can remove this check and make the later one + // unconditional once that’s fixed. + auto conventionalFee = + CalcZIP317Fee(spendableMut, resolved.GetResolvedPayments(), changeAddr); + if (finalFee > WEIGHT_RATIO_CAP * conventionalFee) { + return tl::make_unexpected(AbsurdFeeError(conventionalFee, finalFee)); + } + auto changeRes = + AddChangePayment(spendableMut, resolved, changeAddr.value(), changeAmount, targetAmount); + if (!changeRes.has_value()) { + return tl::make_unexpected(changeRes.error()); + } + } else { + auto conventionalFee = + CalcZIP317Fee(spendableMut, resolved.GetResolvedPayments(), std::nullopt); + if (finalFee > WEIGHT_RATIO_CAP * conventionalFee) { + return tl::make_unexpected(AbsurdFeeError(resolved.Total(), finalFee)); + } + } + } else { + auto limitResult = IterateLimit(wallet, selector, strategy, sendAmount, dustThreshold, spendable, resolved, afterNU5); + if (limitResult.has_value()) { + std::tie(spendableMut, finalFee, changeAddr) = limitResult.value(); + targetAmount = sendAmount + finalFee; + } else { + return tl::make_unexpected(limitResult.error()); + } + } + + // When spending transparent coinbase outputs they may only be sent to shielded recipients. + if (spendableMut.HasTransparentCoinbase() && resolved.HasTransparentRecipient()) { + return tl::make_unexpected(AddressResolutionError::TransparentRecipientNotAllowed); + } + + if (spendableMut.orchardNoteMetadata.size() > this->maxOrchardActions) { + return tl::make_unexpected( + ExcessOrchardActionsError( + ActionSide::Input, + spendableMut.orchardNoteMetadata.size(), + this->maxOrchardActions)); + } + + return InputSelection(spendableMut, resolved, finalFee, changeAddr); +} + +std::pair +GetOVKsForUFVK(const UnifiedFullViewingKey& ufvk, const SpendableInputs& spendable) +{ + if (!spendable.orchardNoteMetadata.empty()) { + auto fvk = ufvk.GetOrchardKey(); + // Orchard notes will not have been selected if the UFVK does not contain an Orchard key. + assert(fvk.has_value()); + return std::make_pair( + fvk.value().ToInternalOutgoingViewingKey(), + fvk.value().ToExternalOutgoingViewingKey()); + } else if (!spendable.saplingNoteEntries.empty()) { + auto dfvk = ufvk.GetSaplingKey(); + // Sapling notes will not have been selected if the UFVK does not contain a Sapling key. + assert(dfvk.has_value()); + return dfvk.value().GetOVKs(); + } else if (!spendable.utxos.empty()) { + // Transparent UTXOs will not have been selected if the UFVK does not contain a transparent + // key. + auto tfvk = ufvk.GetTransparentKey(); + assert(tfvk.has_value()); + return tfvk.value().GetOVKsForShielding(); + } else { + // This should be unreachable. + throw std::runtime_error("No spendable inputs."); + } +} + +std::pair WalletTxBuilder::SelectOVKs( + const CWallet& wallet, + const ZTXOSelector& selector, + const SpendableInputs& spendable) const +{ + return examine(selector.GetPattern(), match { + [&](const CKeyID& keyId) { + return wallet.GetLegacyAccountKey().ToAccountPubKey().GetOVKsForShielding(); + }, + [&](const CScriptID& keyId) { + return wallet.GetLegacyAccountKey().ToAccountPubKey().GetOVKsForShielding(); + }, + [&](const libzcash::SproutPaymentAddress&) { + return wallet.GetLegacyAccountKey().ToAccountPubKey().GetOVKsForShielding(); + }, + [&](const libzcash::SproutViewingKey&) { + return wallet.GetLegacyAccountKey().ToAccountPubKey().GetOVKsForShielding(); + }, + [&](const libzcash::SaplingPaymentAddress& addr) { + libzcash::SaplingExtendedSpendingKey extsk; + assert(wallet.GetSaplingExtendedSpendingKey(addr, extsk)); + return extsk.ToXFVK().GetOVKs(); + }, + [](const libzcash::SaplingExtendedFullViewingKey& sxfvk) { + return sxfvk.GetOVKs(); + }, + [&](const UnifiedAddress& ua) { + auto ufvk = wallet.GetUFVKForAddress(ua); + // This is safe because spending key checks will have ensured that we have a UFVK + // corresponding to this address. + assert(ufvk.has_value()); + return GetOVKsForUFVK(ufvk.value().ToFullViewingKey(), spendable); + }, + [&](const UnifiedFullViewingKey& ufvk) { + return GetOVKsForUFVK(ufvk, spendable); + }, + [&](const AccountZTXOPattern& acct) { + if (acct.GetAccountId() == ZCASH_LEGACY_ACCOUNT) { + return wallet.GetLegacyAccountKey().ToAccountPubKey().GetOVKsForShielding(); + } else { + auto ufvk = wallet.GetUnifiedFullViewingKeyByAccount(acct.GetAccountId()); + // By definition, we have a UFVK for every known non-legacy account. + assert(ufvk.has_value()); + return GetOVKsForUFVK(ufvk.value().ToFullViewingKey(), spendable); + } + }, + }); +} + +PrivacyPolicy TransactionEffects::GetRequiredPrivacyPolicy() const +{ + if (!spendable.utxos.empty()) { + // TODO: Add a check for whether we need AllowLinkingAccountAddresses here. (#6467) + if (payments.HasTransparentRecipient()) { + return PrivacyPolicy::AllowFullyTransparent; + } else { + return PrivacyPolicy::AllowRevealedSenders; + } + } else if (payments.HasTransparentRecipient()) { + return PrivacyPolicy::AllowRevealedRecipients; + } else if (!spendable.orchardNoteMetadata.empty() && payments.HasSaplingRecipient() + || !spendable.saplingNoteEntries.empty() && payments.HasOrchardRecipient() + || !spendable.sproutNoteEntries.empty() && payments.HasSaplingRecipient()) { + // TODO: This should only trigger when there is a non-zero valueBalance. + return PrivacyPolicy::AllowRevealedAmounts; + } else { + return PrivacyPolicy::FullPrivacy; + } +} + +bool TransactionEffects::InvolvesOrchard() const +{ + return spendable.GetOrchardTotal() > 0 || payments.HasOrchardRecipient(); +} + +TransactionBuilderResult TransactionEffects::ApproveAndBuild( + const Consensus::Params& consensus, + const CWallet& wallet, + const CChain& chain, + const TransactionStrategy& strategy) const +{ + auto requiredPrivacy = this->GetRequiredPrivacyPolicy(); + if (!strategy.IsCompatibleWith(requiredPrivacy)) { + return TransactionBuilderResult(strprintf( + "The specified privacy policy, %s, does not permit the creation of " + "the requested transaction. Select %s to allow this transaction " + "to be constructed.", + strategy.PolicyName(), + TransactionStrategy::ToString(requiredPrivacy) + + (requiredPrivacy == PrivacyPolicy::NoPrivacy ? "" : " or weaker"))); + } + + int nextBlockHeight = chain.Height() + 1; + + // Allow Orchard recipients by setting an Orchard anchor. + std::optional orchardAnchor; + if (spendable.sproutNoteEntries.empty() + && (InvolvesOrchard() || nPreferredTxVersion > ZIP225_MIN_TX_VERSION) + && this->anchorConfirmations > 0) + { + LOCK(cs_main); + auto anchorBlockIndex = chain[this->anchorHeight]; + assert(anchorBlockIndex != nullptr); + orchardAnchor = anchorBlockIndex->hashFinalOrchardRoot; + } + + auto builder = TransactionBuilder(consensus, nextBlockHeight, orchardAnchor, &wallet); + builder.SetFee(fee); + + // Track the total of notes that we've added to the builder. This + // shouldn't strictly be necessary, given `spendable.LimitToAmount` + CAmount totalSpend = 0; + + // Create Sapling outpoints + std::vector saplingOutPoints; + std::vector saplingNotes; + std::vector saplingKeys; + + for (const auto& t : spendable.saplingNoteEntries) { + saplingOutPoints.push_back(t.op); + saplingNotes.push_back(t.note); + + libzcash::SaplingExtendedSpendingKey saplingKey; + assert(wallet.GetSaplingExtendedSpendingKey(t.address, saplingKey)); + saplingKeys.push_back(saplingKey); + + totalSpend += t.note.value(); + } + + // Fetch Sapling anchor and witnesses, and Orchard Merkle paths. + uint256 anchor; + std::vector> witnesses; + std::vector> orchardSpendInfo; + { + LOCK(wallet.cs_wallet); + if (!wallet.GetSaplingNoteWitnesses(saplingOutPoints, anchorConfirmations, witnesses, anchor)) { + // This error should not appear once we're nAnchorConfirmations blocks past + // Sapling activation. + return TransactionBuilderResult("Insufficient Sapling witnesses."); + } + if (builder.GetOrchardAnchor().has_value()) { + orchardSpendInfo = wallet.GetOrchardSpendInfo(spendable.orchardNoteMetadata, builder.GetOrchardAnchor().value()); + } + } + + // Add Orchard spends + for (size_t i = 0; i < orchardSpendInfo.size(); i++) { + auto spendInfo = std::move(orchardSpendInfo[i]); + if (!builder.AddOrchardSpend( + std::move(spendInfo.first), + std::move(spendInfo.second))) + { + return TransactionBuilderResult( + strprintf("Failed to add Orchard note to transaction (check %s for details)", GetDebugLogPath()) + ); + } else { + totalSpend += spendInfo.second.Value(); + } + } + + // Add Sapling spends + for (size_t i = 0; i < saplingNotes.size(); i++) { + if (!witnesses[i]) { + return TransactionBuilderResult(strprintf( + "Missing witness for Sapling note at outpoint %s", + spendable.saplingNoteEntries[i].op.ToString() + )); + } + + builder.AddSaplingSpend(saplingKeys[i].expsk, saplingNotes[i], anchor, witnesses[i].value()); + } + + // Add outputs + for (const auto& r : payments.GetResolvedPayments()) { + std::optional result; + examine(r.address, match { + [&](const CKeyID& keyId) { + if (r.memo.has_value()) { + result = TransactionBuilderResult("Memos cannot be sent to transparent addresses."); + } else { + builder.AddTransparentOutput(keyId, r.amount); + } + }, + [&](const CScriptID& scriptId) { + if (r.memo.has_value()) { + result = TransactionBuilderResult("Memos cannot be sent to transparent addresses."); + } else { + builder.AddTransparentOutput(scriptId, r.amount); + } + }, + [&](const libzcash::SaplingPaymentAddress& addr) { + builder.AddSaplingOutput( + r.isInternal ? internalOVK : externalOVK, addr, r.amount, + r.memo.has_value() ? r.memo.value().ToBytes() : Memo::NoMemo().ToBytes()); + }, + [&](const libzcash::OrchardRawAddress& addr) { + builder.AddOrchardOutput( + r.isInternal ? internalOVK : externalOVK, addr, r.amount, + r.memo.has_value() ? std::optional(r.memo.value().ToBytes()) : std::nullopt); + }, + }); + if (result.has_value()) { + return result.value(); + } + } + + // Add transparent utxos + for (const auto& out : spendable.utxos) { + const CTxOut& txOut = out.tx->vout[out.i]; + builder.AddTransparentInput(COutPoint(out.tx->GetHash(), out.i), txOut.scriptPubKey, txOut.nValue); + + totalSpend += txOut.nValue; + } + + // Find Sprout witnesses + // When spending notes, take a snapshot of note witnesses and anchors as the treestate will + // change upon arrival of new blocks which contain joinsplit transactions. This is likely + // to happen as creating a chained joinsplit transaction can take longer than the block interval. + // So, we need to take locks on cs_main and wallet.cs_wallet so that the witnesses aren't + // updated. + // + // TODO: these locks would ideally be shared for selection of Sapling anchors and witnesses + // as well. + std::vector> vSproutWitnesses; + { + LOCK2(cs_main, wallet.cs_wallet); + std::vector vOutPoints; + for (const auto& t : spendable.sproutNoteEntries) { + vOutPoints.push_back(t.jsop); + } + + // inputAnchor is not needed by builder.AddSproutInput as it is for Sapling. + uint256 inputAnchor; + if (!wallet.GetSproutNoteWitnesses(vOutPoints, anchorConfirmations, vSproutWitnesses, inputAnchor)) { + // This error should not appear once we're nAnchorConfirmations blocks past + // Sprout activation. + return TransactionBuilderResult("Insufficient Sprout witnesses."); + } + } + + // Add Sprout spends + for (int i = 0; i < spendable.sproutNoteEntries.size(); i++) { + const auto& t = spendable.sproutNoteEntries[i]; + libzcash::SproutSpendingKey sk; + assert(wallet.GetSproutSpendingKey(t.address, sk)); + + builder.AddSproutInput(sk, t.note, vSproutWitnesses[i].value()); + + totalSpend += t.note.value(); + } + + // TODO: We currently can’t store Sprout change in `Payments`, so we only validate the + // spend/output balance in the case that `TransactionBuilder` doesn’t need to + // (re)calculate the change. In future, we shouldn’t rely on `TransactionBuilder` ever + // calculating change. (#5660) + if (changeAddr.has_value()) { + examine(changeAddr.value(), match { + [&](const SproutPaymentAddress& addr) { + builder.SendChangeToSprout(addr); + }, + [&](const RecipientAddress&) { + assert(totalSpend == payments.Total() + fee); + } + }); + } + + // Build the transaction + auto result = builder.Build(); + + if (result.IsTx()) { + auto minRelayFee = + ::minRelayTxFee.GetFeeForRelay( + ::GetSerializeSize(result.GetTxOrThrow(), SER_NETWORK, PROTOCOL_VERSION)); + // This should only be possible if a user has provided an explicit fee. + if (fee < minRelayFee) { + return TransactionBuilderResult( + strprintf( + "Fee (%s) is below the minimum relay fee for this transaction (%s)", + DisplayMoney(fee), + DisplayMoney(minRelayFee))); + } + } + + return result; +} + +// TODO: Lock Orchard notes (#6226) +void TransactionEffects::LockSpendable(CWallet& wallet) const +{ + LOCK2(cs_main, wallet.cs_wallet); + for (auto utxo : spendable.utxos) { + COutPoint outpt(utxo.tx->GetHash(), utxo.i); + wallet.LockCoin(outpt); + } + for (auto note : spendable.sproutNoteEntries) { + wallet.LockNote(note.jsop); + } + for (auto note : spendable.saplingNoteEntries) { + wallet.LockNote(note.op); + } +} + +// TODO: Unlock Orchard notes (#6226) +void TransactionEffects::UnlockSpendable(CWallet& wallet) const +{ + LOCK2(cs_main, wallet.cs_wallet); + for (auto utxo : spendable.utxos) { + COutPoint outpt(utxo.tx->GetHash(), utxo.i); + wallet.UnlockCoin(outpt); + } + for (auto note : spendable.sproutNoteEntries) { + wallet.UnlockNote(note.jsop); + } + for (auto note : spendable.saplingNoteEntries) { + wallet.UnlockNote(note.op); + } +} diff --git a/depend/zcash/src/wallet/wallet_tx_builder.h b/depend/zcash/src/wallet/wallet_tx_builder.h index 9e543403f..371b0fcc2 100644 --- a/depend/zcash/src/wallet/wallet_tx_builder.h +++ b/depend/zcash/src/wallet/wallet_tx_builder.h @@ -5,24 +5,449 @@ #ifndef ZCASH_WALLET_WALLET_TX_BUILDER_H #define ZCASH_WALLET_WALLET_TX_BUILDER_H +#include "consensus/params.h" +#include "transaction_builder.h" #include "wallet/memo.h" +#include "wallet/wallet.h" using namespace libzcash; +int GetAnchorHeight(const CChain& chain, int anchorConfirmations); + /** - * A payment that has been resolved to send to a specific - * recipient address in a single pool. + * A payment that has been resolved to send to a specific recipient address in a single pool. This + * is an internal type that represents both user-requested payment addresses and generated + * (internal) payments (like change). */ class ResolvedPayment : public RecipientMapping { public: CAmount amount; std::optional memo; + bool isInternal; ResolvedPayment( std::optional ua, libzcash::RecipientAddress address, CAmount amount, + std::optional memo, + bool isInternal) : + RecipientMapping(ua, address), amount(amount), memo(memo), isInternal(isInternal) {} +}; + +/** + * A requested payment that has not yet been resolved to a + * specific recipient address. + */ +class Payment { +private: + PaymentAddress address; + CAmount amount; + std::optional memo; +public: + Payment( + PaymentAddress address, + CAmount amount, std::optional memo) : - RecipientMapping(ua, address), amount(amount), memo(memo) {} + address(address), amount(amount), memo(memo) { + assert(MoneyRange(amount)); + } + + const PaymentAddress& GetAddress() const { + return address; + } + + CAmount GetAmount() const { + return amount; + } + + const std::optional& GetMemo() const { + return memo; + } +}; + +typedef std::pair> NetAmountRecipient; + +typedef std::variant< + std::vector, + NetAmountRecipient> Recipients; + +class Payments { +private: + std::vector payments; + std::set recipientPools; + CAmount t_outputs_total{0}; + CAmount sapling_outputs_total{0}; + CAmount orchard_outputs_total{0}; +public: + Payments(std::vector payments) { + for (const ResolvedPayment& payment : payments) { + AddPayment(payment); + } + } + + void AddPayment(ResolvedPayment payment) { + examine(payment.address, match { + [&](const CKeyID& addr) { + t_outputs_total += payment.amount; + recipientPools.insert(OutputPool::Transparent); + }, + [&](const CScriptID& addr) { + t_outputs_total += payment.amount; + recipientPools.insert(OutputPool::Transparent); + }, + [&](const libzcash::SaplingPaymentAddress& addr) { + sapling_outputs_total += payment.amount; + recipientPools.insert(OutputPool::Sapling); + }, + [&](const libzcash::OrchardRawAddress& addr) { + orchard_outputs_total += payment.amount; + recipientPools.insert(OutputPool::Orchard); + } + }); + payments.push_back(payment); + } + + std::set GetRecipientPools() const { + return recipientPools; + } + + bool HasTransparentRecipient() const { + return recipientPools.count(OutputPool::Transparent) > 0; + } + + bool HasSaplingRecipient() const { + return recipientPools.count(OutputPool::Sapling) > 0; + } + + bool HasOrchardRecipient() const { + return recipientPools.count(OutputPool::Orchard) > 0; + } + + const std::vector& GetResolvedPayments() const { + return payments; + } + + CAmount GetTransparentTotal() const { + return t_outputs_total; + } + + CAmount GetSaplingTotal() const { + return sapling_outputs_total; + } + + CAmount GetOrchardTotal() const { + return orchard_outputs_total; + } + + CAmount Total() const { + return + t_outputs_total + + sapling_outputs_total + + orchard_outputs_total; + } +}; + +typedef std::variant< + RecipientAddress, + SproutPaymentAddress> ChangeAddress; + +class TransactionEffects { +private: + uint32_t anchorConfirmations{1}; + SpendableInputs spendable; + Payments payments; + std::optional changeAddr; + CAmount fee{0}; + uint256 internalOVK; + uint256 externalOVK; + // TODO: This needs to be richer, like an `anchorBlock`, so the `TransactionEffects` can + // be recalculated if the state of the chain has changed significantly between the time of + // preparation and the time of approval. + int anchorHeight; + +public: + TransactionEffects( + uint32_t anchorConfirmations, + SpendableInputs spendable, + Payments payments, + std::optional changeAddr, + CAmount fee, + uint256 internalOVK, + uint256 externalOVK, + int anchorHeight) : + anchorConfirmations(anchorConfirmations), + spendable(spendable), + payments(payments), + changeAddr(changeAddr), + fee(fee), + internalOVK(internalOVK), + externalOVK(externalOVK), + anchorHeight(anchorHeight) {} + + /** + * Returns the strongest `PrivacyPolicy` that is compatible with the transaction’s effects. + */ + PrivacyPolicy GetRequiredPrivacyPolicy() const; + + const SpendableInputs& GetSpendable() const { + return spendable; + } + + /** + * This should be called upon creating `TransactionEffects`, it locks exactly the notes that + * will be spent in the built transaction. + */ + void LockSpendable(CWallet& wallet) const; + + /** + * This should be called when we are finished with the transaction (whether it succeeds or + * fails). + * + * TODO: This currently needs to be called while the `TransactionEffects` exists. In future, it + * would be useful to keep these notes locked until we have confirmation that the tx is on + * the chain or not. + */ + void UnlockSpendable(CWallet& wallet) const; + + const Payments& GetPayments() const { + return payments; + } + + CAmount GetFee() const { + return fee; + } + + bool InvolvesOrchard() const; + + TransactionBuilderResult ApproveAndBuild( + const Consensus::Params& consensus, + const CWallet& wallet, + const CChain& chain, + const TransactionStrategy& strategy) const; +}; + +enum class AddressResolutionError { + //! Zcashd no longer supports sending to Sprout. + SproutRecipientsNotSupported, + //! Requested `PrivacyPolicy` doesn’t include `AllowRevealedRecipients` + TransparentRecipientNotAllowed, + //! Requested `PrivacyPolicy` doesn’t include `AllowRevealedRecipients` + TransparentChangeNotAllowed, + //! Requested `PrivacyPolicy` doesn’t include `AllowRevealedAmounts`, but we don’t have enough + //! Sapling funds to avoid revealing amounts + RevealingSaplingAmountNotAllowed, + //! Despite a lax `PrivacyPolicy`, other factors made it impossible to find a receiver for a + //! recipient UA + CouldNotResolveReceiver, + //! Requested `PrivacyPolicy` doesn’t include `AllowRevealedRecipients`, but we are trying to + //! pay a UA where we can only select a transparent receiver + TransparentReceiverNotAllowed, + //! Requested `PrivacyPolicy` doesn’t include `AllowRevealedAmounts`, but we are trying to pay a + //! UA where we don’t have enough funds in any single pool that it has a receiver for + RevealingReceiverAmountsNotAllowed, +}; + +/// Phantom change is change that appears to exist until we add the output for it, at which point it +/// is consumed by the increase to the conventional fee. When we are at the limit of selectable +/// notes, this makes it impossible to create the transaction without either creating a 0-valued +/// output or overpaying the fee. +class PhantomChangeError { +public: + CAmount finalFee; + CAmount dustThreshold; + + PhantomChangeError(CAmount finalFee, CAmount dustThreshold): + finalFee(finalFee), dustThreshold(dustThreshold) { } +}; + +class InsufficientFundsError { +public: + CAmount required; + + InsufficientFundsError(CAmount required): + required(required) { } }; + +class DustThresholdError { +public: + CAmount dustThreshold; + CAmount changeAmount; + + DustThresholdError(CAmount dustThreshold, CAmount changeAmount): + dustThreshold(dustThreshold), changeAmount(changeAmount) { } +}; + +typedef std::variant< + PhantomChangeError, + InsufficientFundsError, + DustThresholdError> InvalidFundsReason; + +class InvalidFundsError { +public: + CAmount available; + const InvalidFundsReason reason; + + InvalidFundsError(CAmount available, const InvalidFundsReason reason): + available(available), reason(reason) { } +}; + +class ChangeNotAllowedError { +public: + CAmount available; + CAmount required; + + ChangeNotAllowedError(CAmount available, CAmount required): + available(available), required(required) { } +}; + +/// Error when a fee is outside `MoneyRange` +class InvalidFeeError { +public: + CAmount fixedFee; + + InvalidFeeError(CAmount fixedFee): + fixedFee(fixedFee) { } +}; + +/// Error when a fee is higher than can be useful. This reduces the chance of accidentally +/// overpaying with explicit fees. +class AbsurdFeeError { +public: + CAmount conventionalFee; + CAmount fixedFee; + + AbsurdFeeError(CAmount conventionalFee, CAmount fixedFee): + conventionalFee(conventionalFee), fixedFee(fixedFee) { } +}; + +/// Error when a fee is higher than this instance allows. +class MaxFeeError { +public: + CAmount fixedFee; + + MaxFeeError(CAmount fixedFee): + fixedFee(fixedFee) { } +}; + +enum ActionSide { + Input, + Output, + Both, +}; + +class ExcessOrchardActionsError { +public: + ActionSide side; + uint32_t orchardNotes; + uint32_t maxNotes; + + ExcessOrchardActionsError(ActionSide side, uint32_t orchardNotes, uint32_t maxNotes): + side(side), orchardNotes(orchardNotes), maxNotes(maxNotes) { } +}; + +typedef std::variant< + AddressResolutionError, + InvalidFundsError, + ChangeNotAllowedError, + InvalidFeeError, + AbsurdFeeError, + MaxFeeError, + ExcessOrchardActionsError> InputSelectionError; + +class InputSelection { +private: + SpendableInputs inputs; + Payments payments; + CAmount fee; + std::optional changeAddr; + +public: + InputSelection(SpendableInputs inputs, Payments payments, CAmount fee, std::optional changeAddr): + inputs(inputs), payments(payments), fee(fee), changeAddr(changeAddr) {} + + const SpendableInputs& GetInputs() const; + const Payments& GetPayments() const; + CAmount GetFee() const; + const std::optional GetChangeAddress() const; +}; + +class WalletTxBuilder { +private: + const CChainParams& params; + CFeeRate minRelayFee; + uint32_t maxOrchardActions; + + /** + * Compute the default dust threshold + */ + CAmount DefaultDustThreshold() const; + + tl::expected + GetChangeAddress( + CWallet& wallet, + const ZTXOSelector& selector, + const SpendableInputs& spendable, + const Payments& resolvedPayments, + const TransactionStrategy& strategy, + bool afterNU5) const; + + tl::expected< + std::tuple>, + InputSelectionError> + IterateLimit( + CWallet& wallet, + const ZTXOSelector& selector, + const TransactionStrategy& strategy, + CAmount sendAmount, + CAmount dustThreshold, + const SpendableInputs& spendable, + Payments& resolved, + bool afterNU5) const; + + /** + * Select inputs sufficient to fulfill the specified requested payments, + * and choose unified address receivers based upon the available inputs + * and the requested transaction strategy. + */ + tl::expected + ResolveInputsAndPayments( + CWallet& wallet, + const ZTXOSelector& selector, + const SpendableInputs& spendable, + const std::vector& payments, + const CChain& chain, + const TransactionStrategy& strategy, + const std::optional& fee, + bool afterNU5) const; + /** + * Compute the internal and external OVKs to use in transaction construction, given + * the spendable inputs. + */ + std::pair SelectOVKs( + const CWallet& wallet, + const ZTXOSelector& selector, + const SpendableInputs& spendable) const; + +public: + WalletTxBuilder(const CChainParams& params, CFeeRate minRelayFee): + params(params), minRelayFee(minRelayFee), maxOrchardActions(nOrchardActionLimit) {} + + SpendableInputs FindAllSpendableInputs( + const CWallet& wallet, + const ZTXOSelector& selector, + int32_t minDepth) const; + + tl::expected + PrepareTransaction( + CWallet& wallet, + const ZTXOSelector& selector, + const SpendableInputs& spendable, + const Recipients& payments, + const CChain& chain, + const TransactionStrategy& strategy, + /// A fixed fee is used if provided, otherwise it is calculated based on ZIP 317. + const std::optional& fee, + uint32_t anchorConfirmations) const; +}; + #endif diff --git a/depend/zcash/src/wallet/walletdb.h b/depend/zcash/src/wallet/walletdb.h index 096ff6d3c..e05930f8b 100644 --- a/depend/zcash/src/wallet/walletdb.h +++ b/depend/zcash/src/wallet/walletdb.h @@ -336,7 +336,7 @@ class CSerializeRecipientAddress { template void Serialize(Stream& s) const { - std::visit(match { + examine(recipient, match { [&](const CKeyID& keyId) { ReceiverTypeSer(libzcash::ReceiverType::P2PKH).Serialize(s); s << keyId; @@ -353,7 +353,7 @@ class CSerializeRecipientAddress { ReceiverTypeSer(libzcash::ReceiverType::Orchard).Serialize(s); s << orchardAddr; } - }, recipient); + }); } template diff --git a/depend/zcash/src/weighted_map.h b/depend/zcash/src/weighted_map.h new file mode 100644 index 000000000..b641ec57d --- /dev/null +++ b/depend/zcash/src/weighted_map.h @@ -0,0 +1,199 @@ +// Copyright (c) 2019-2023 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_WEIGHTED_MAP_H +#define ZCASH_WEIGHTED_MAP_H + +#include +#include +#include +#include + +// A WeightedMap represents a map from keys (of type K) to values (of type V), +// each entry having a weight (of type W). Elements can be randomly selected and +// removed from the map with probability in proportion to their weight. This is +// used to implement mempool limiting specified in ZIP 401, and the block template +// construction algorithm specified in ZIP 317. +// +// In order to efficiently implement random selection by weight, we keep track +// of the total weight of all keys in the map. For performance reasons, the +// map is represented as a binary tree where each node knows the sum of the +// weights of the children. This allows for addition, removal, and random +// selection/dropping in logarithmic time. +// +// random(w) (which will only be called with positive w) must be defined to +// return a uniform random value between zero inclusive and w exclusive. +// The type W must be a signed numeric type that supports addition, binary +// and unary -, and < and <= comparisons, and W() must construct the zero value +// (these constraints are met for primitive signed integer types). +template +class WeightedMap +{ + // W must be a signed numeric type. + static_assert(std::numeric_limits::min() < W()); + + struct Node { + K key; + V value; + W weight; + W sumOfDescendantWeights; + }; + + // The following vector is the tree representation of this collection. + // For each node, we keep track of the key, its associated value, + // its weight, and the sum of the weights of all its descendants. + std::vector nodes; + + // The following map is to simplify removal. + std::map indexMap; + + static inline size_t leftChild(size_t i) { return i*2 + 1; } + static inline size_t rightChild(size_t i) { return i*2 + 2; } + static inline size_t parent(size_t i) { return (i-1)/2; } + +public: + // Check internal invariants (for tests). + void checkInvariants() const + { + assert(indexMap.size() == nodes.size()); + for (size_t i = 0; i < nodes.size(); i++) { + assert(indexMap.at(nodes.at(i).key) == i); + assert(nodes.at(i).sumOfDescendantWeights == getWeightAt(leftChild(i)) + getWeightAt(rightChild(i))); + } + } + +private: + // Return the sum of weights of the node at a given index and all of its descendants. + W getWeightAt(size_t index) const + { + if (index >= nodes.size()) { + return W(); + } + auto& node = nodes.at(index); + return node.weight + node.sumOfDescendantWeights; + } + + // When adding and removing a node we need to update its parent and all of its + // ancestors to reflect its weight. + void backPropagate(size_t fromIndex, W weightDelta) + { + while (fromIndex > 0) { + fromIndex = parent(fromIndex); + nodes[fromIndex].sumOfDescendantWeights += weightDelta; + } + } + + // For a given random weight, this method recursively finds the index of the + // correct entry. This is used by WeightedMap::takeRandom(). + size_t findByWeight(size_t fromIndex, W weightToFind) const + { + W leftWeight = getWeightAt(leftChild(fromIndex)); + // On Left + if (weightToFind < leftWeight) { + return findByWeight(leftChild(fromIndex), weightToFind); + } + W rightWeight = getWeightAt(fromIndex) - getWeightAt(rightChild(fromIndex)); + // Found + if (weightToFind < rightWeight) { + return fromIndex; + } + // On Right + return findByWeight(rightChild(fromIndex), weightToFind - rightWeight); + } + +public: + WeightedMap() {} + + // Return the total weight of all entries in the map. + W getTotalWeight() const + { + return getWeightAt(0); + } + + // Return true when the map has no entries. + bool empty() const + { + return nodes.empty(); + } + + // Return the number of entries. + size_t size() const + { + return nodes.size(); + } + + // Return false if the key already exists in the map. + // Otherwise, add an entry mapping `key` to `value` with the given weight, + // and return true. The weight must be positive. + bool add(K key, V value, W weight) + { + assert(W() < weight); + if (indexMap.count(key) > 0) { + return false; + } + size_t index = nodes.size(); + nodes.push_back(Node { + .key = key, + .value = value, + .weight = weight, + .sumOfDescendantWeights = W(), + }); + indexMap[key] = index; + backPropagate(index, weight); + return true; + } + + // If the given key is not present in the map, return std::nullopt. + // Otherwise, remove that key's entry and return its associated value. + std::optional remove(K key) + { + auto it = indexMap.find(key); + if (it == indexMap.end()) { + return std::nullopt; + } + + size_t removeIndex = it->second; + V removeValue = nodes.at(removeIndex).value; + + size_t lastIndex = nodes.size()-1; + Node lastNode = nodes.at(lastIndex); + W weightDelta = lastNode.weight - nodes.at(removeIndex).weight; + backPropagate(lastIndex, -lastNode.weight); + + if (removeIndex < lastIndex) { + nodes[removeIndex].key = lastNode.key; + nodes[removeIndex].value = lastNode.value; + nodes[removeIndex].weight = lastNode.weight; + // nodes[removeIndex].sumOfDescendantWeights should not change here. + indexMap[lastNode.key] = removeIndex; + backPropagate(removeIndex, weightDelta); + } + + indexMap.erase(it); + nodes.pop_back(); + return removeValue; + } + + // If the map is empty, return std::nullopt. Otherwise, pick a random entry + // with probability proportional to its weight; remove it and return a tuple of + // the key, its associated value, and its weight. + std::optional> takeRandom() + { + if (empty()) { + return std::nullopt; + } + W totalWeight = getTotalWeight(); + assert(W() < totalWeight); + W randomWeight = random(totalWeight); + assert(W() <= randomWeight && randomWeight < totalWeight); + size_t index = findByWeight(0, randomWeight); + assert(index < nodes.size()); + const Node& drop = nodes.at(index); + auto res = std::make_tuple(drop.key, drop.value, drop.weight); // copy values + remove(drop.key); + return res; + } +}; + +#endif // ZCASH_WEIGHTED_MAP_H diff --git a/depend/zcash/src/zcash/Address.cpp b/depend/zcash/src/zcash/Address.cpp index 3b98c446a..dc7a6fb98 100644 --- a/depend/zcash/src/zcash/Address.cpp +++ b/depend/zcash/src/zcash/Address.cpp @@ -178,13 +178,13 @@ std::optional UnifiedAddress::GetPreferredRecipientAddress( // order from most-preferred to least. std::optional result; for (const auto& receiver : *this) { - std::visit(match { + examine(receiver, match { [&](const OrchardRawAddress& addr) { if (nu5Active) result = addr; }, [&](const SaplingPaymentAddress& addr) { result = addr; }, [&](const CScriptID& addr) { result = addr; }, [&](const CKeyID& addr) { result = addr; }, [&](const UnknownReceiver& addr) { } - }, receiver); + }); if (result.has_value()) { return result; @@ -194,13 +194,13 @@ std::optional UnifiedAddress::GetPreferredRecipientAddress( } bool HasKnownReceiverType(const Receiver& receiver) { - return std::visit(match { + return examine(receiver, match { [](const OrchardRawAddress& addr) { return true; }, [](const SaplingPaymentAddress& addr) { return true; }, [](const CScriptID& addr) { return true; }, [](const CKeyID& addr) { return true; }, [](const UnknownReceiver& addr) { return false; } - }, receiver); + }); } std::pair AddressInfoFromSpendingKey::operator()(const SproutSpendingKey &sk) const { diff --git a/depend/zcash/src/zcash/Address.hpp b/depend/zcash/src/zcash/Address.hpp index 63e17182d..52a5c4de0 100644 --- a/depend/zcash/src/zcash/Address.hpp +++ b/depend/zcash/src/zcash/Address.hpp @@ -102,7 +102,7 @@ class UnifiedAddress { std::set GetKnownReceiverTypes() const { std::set result; for (const auto& receiver : receivers) { - std::visit(match { + examine(receiver, match { [&](const libzcash::OrchardRawAddress &zaddr) { result.insert(ReceiverType::Orchard); }, @@ -117,7 +117,7 @@ class UnifiedAddress { }, [&](const libzcash::UnknownReceiver &uaddr) { } - }, receiver); + }); } return result; } @@ -270,13 +270,13 @@ typedef std::variant< SproutSpendingKey, SaplingExtendedSpendingKey> SpendingKey; -class IsShieldedRecipient { +class HasShieldedRecipient { public: bool operator()(const CKeyID& p2pkh) { return false; } bool operator()(const CScriptID& p2sh) { return false; } bool operator()(const SproutPaymentAddress& addr) { return true; } bool operator()(const SaplingPaymentAddress& addr) { return true; } - bool operator()(const OrchardRawAddress& addr) { return true; } + bool operator()(const UnifiedAddress& addr) { return true; } }; class SelectRecipientAddress { diff --git a/depend/zcash/src/zcash/IncrementalMerkleTree.hpp b/depend/zcash/src/zcash/IncrementalMerkleTree.hpp index 23471f017..56ec6e33d 100644 --- a/depend/zcash/src/zcash/IncrementalMerkleTree.hpp +++ b/depend/zcash/src/zcash/IncrementalMerkleTree.hpp @@ -7,12 +7,13 @@ #include "uint256.h" #include "serialize.h" +#include "streams_rust.h" #include "Zcash.h" #include "zcash/util.h" #include -#include +#include namespace libzcash { @@ -265,19 +266,18 @@ class OrchardMerkleFrontierLegacySer; class OrchardMerkleFrontier { private: - /// An incremental Sinsemilla tree; this pointer may never be null. - /// Memory is allocated by Rust. - std::unique_ptr inner; + /// An incremental Sinsemilla tree. Memory is allocated by Rust. + rust::Box inner; friend class OrchardWallet; friend class OrchardMerkleFrontierLegacySer; public: - OrchardMerkleFrontier() : inner(orchard_merkle_frontier_empty(), orchard_merkle_frontier_free) {} + OrchardMerkleFrontier() : inner(merkle_frontier::new_orchard()) {} OrchardMerkleFrontier(OrchardMerkleFrontier&& frontier) : inner(std::move(frontier.inner)) {} OrchardMerkleFrontier(const OrchardMerkleFrontier& frontier) : - inner(orchard_merkle_frontier_clone(frontier.inner.get()), orchard_merkle_frontier_free) {} + inner(frontier.inner->box_clone()) {} OrchardMerkleFrontier& operator=(OrchardMerkleFrontier&& frontier) { @@ -289,52 +289,47 @@ class OrchardMerkleFrontier OrchardMerkleFrontier& operator=(const OrchardMerkleFrontier& frontier) { if (this != &frontier) { - inner.reset(orchard_merkle_frontier_clone(frontier.inner.get())); + inner = frontier.inner->box_clone(); } return *this; } template void Serialize(Stream& s) const { - RustStream rs(s); - if (!orchard_merkle_frontier_serialize(inner.get(), &rs, RustStream::write_callback)) { - throw std::ios_base::failure("Failed to serialize v5 Orchard tree"); + try { + inner->serialize(*ToRustStream(s)); + } catch (const std::exception& e) { + throw std::ios_base::failure(e.what()); } } template void Unserialize(Stream& s) { - RustStream rs(s); - OrchardMerkleFrontierPtr* tree = orchard_merkle_frontier_parse( - &rs, RustStream::read_callback); - if (tree == nullptr) { - throw std::ios_base::failure("Failed to parse v5 Orchard tree"); + try { + inner = merkle_frontier::parse_orchard(*ToRustStream(s)); + } catch (const std::exception& e) { + throw std::ios_base::failure(e.what()); } - inner.reset(tree); } size_t DynamicMemoryUsage() const { - return orchard_merkle_frontier_dynamic_mem_usage(inner.get()); + return inner->dynamic_memory_usage(); } bool AppendBundle(const OrchardBundle& bundle) { - return orchard_merkle_frontier_append_bundle(inner.get(), bundle.inner.get()); + return inner->append_bundle(*bundle.GetDetails()); } const uint256 root() const { - uint256 value; - orchard_merkle_frontier_root(inner.get(), value.begin()); - return value; + return uint256::FromRawBytes(inner->root()); } static uint256 empty_root() { - uint256 value; - orchard_merkle_tree_empty_root(value.begin()); - return value; + return uint256::FromRawBytes(merkle_frontier::orchard_empty_root()); } size_t size() const { - return orchard_merkle_frontier_num_leaves(inner.get()); + return inner->size(); } }; @@ -346,9 +341,10 @@ class OrchardMerkleFrontierLegacySer { template void Serialize(Stream& s) const { - RustStream rs(s); - if (!orchard_merkle_frontier_serialize_legacy(frontier.inner.get(), &rs, RustStream::write_callback)) { - throw std::ios_base::failure("Failed to serialize Orchard merkle frontier in legacy format."); + try { + frontier.inner->serialize_legacy(*ToRustStream(s)); + } catch (const std::exception& e) { + throw std::ios_base::failure(e.what()); } } }; diff --git a/depend/zcash/src/zcash/JoinSplit.cpp b/depend/zcash/src/zcash/JoinSplit.cpp index b6162d823..624a9c1ec 100644 --- a/depend/zcash/src/zcash/JoinSplit.cpp +++ b/depend/zcash/src/zcash/JoinSplit.cpp @@ -26,7 +26,7 @@ namespace libzcash { std::array& out_notes, std::array& out_ciphertexts, uint256& out_ephemeralKey, - const Ed25519VerificationKey& joinSplitPubKey, + const ed25519::VerificationKey& joinSplitPubKey, uint256& out_randomSeed, std::array& out_macs, std::array& out_nullifiers, @@ -204,7 +204,7 @@ template uint256 JoinSplit::h_sig( const uint256& randomSeed, const std::array& nullifiers, - const Ed25519VerificationKey& joinSplitPubKey + const ed25519::VerificationKey& joinSplitPubKey ) { const unsigned char personalization[blake2b::PERSONALBYTES] = {'Z','c','a','s','h','C','o','m','p','u','t','e','h','S','i','g'}; @@ -215,7 +215,7 @@ uint256 JoinSplit::h_sig( block.insert(block.end(), nullifiers[i].begin(), nullifiers[i].end()); } - block.insert(block.end(), joinSplitPubKey.bytes, joinSplitPubKey.bytes + ED25519_VERIFICATION_KEY_LEN); + block.insert(block.end(), joinSplitPubKey.bytes.begin(), joinSplitPubKey.bytes.end()); uint256 output; diff --git a/depend/zcash/src/zcash/JoinSplit.hpp b/depend/zcash/src/zcash/JoinSplit.hpp index 9bee708d9..99751862f 100644 --- a/depend/zcash/src/zcash/JoinSplit.hpp +++ b/depend/zcash/src/zcash/JoinSplit.hpp @@ -13,7 +13,7 @@ #include -#include +#include namespace libzcash { @@ -50,7 +50,7 @@ class JoinSplit { public: static uint256 h_sig(const uint256& randomSeed, const std::array& nullifiers, - const Ed25519VerificationKey& joinSplitPubKey + const ed25519::VerificationKey& joinSplitPubKey ); // Compute nullifiers, macs, note commitments & encryptions, and SNARK proof @@ -60,7 +60,7 @@ class JoinSplit { std::array& out_notes, std::array& out_ciphertexts, uint256& out_ephemeralKey, - const Ed25519VerificationKey& joinSplitPubKey, + const ed25519::VerificationKey& joinSplitPubKey, uint256& out_randomSeed, std::array& out_hmacs, std::array& out_nullifiers, diff --git a/depend/zcash/src/zcash/Note.cpp b/depend/zcash/src/zcash/Note.cpp index 6e318b4f8..1b8455b72 100644 --- a/depend/zcash/src/zcash/Note.cpp +++ b/depend/zcash/src/zcash/Note.cpp @@ -66,9 +66,10 @@ SaplingNote::SaplingNote( std::optional SaplingNote::cmu() const { uint256 result; uint256 rcm_tmp = rcm(); - // ZIP 216: This method is only called from test code. + // We consider ZIP 216 active all of the time because blocks prior to NU5 + // activation (on mainnet and testnet) did not contain Sapling transactions + // that violated its canonicity rule. if (!librustzcash_sapling_compute_cmu( - true, d.data(), pk_d.begin(), value(), @@ -187,6 +188,23 @@ std::optional SaplingNotePlaintext::note(const SaplingIncomingViewi } } +std::pair SaplingNotePlaintext::from_rust( + rust::Box decrypted) +{ + SaplingPaymentAddress pa( + decrypted->recipient_d(), + uint256::FromRawBytes(decrypted->recipient_pk_d())); + SaplingNote note( + pa.d, + pa.pk_d, + decrypted->note_value(), + uint256::FromRawBytes(decrypted->note_rseed()), + decrypted->zip_212_enabled() ? Zip212Enabled::AfterZip212 : Zip212Enabled::BeforeZip212); + SaplingNotePlaintext notePt(note, decrypted->memo()); + + return std::make_pair(notePt, pa); +} + std::optional SaplingOutgoingPlaintext::decrypt( const SaplingOutCiphertext &ciphertext, const uint256& ovk, @@ -215,109 +233,6 @@ std::optional SaplingOutgoingPlaintext::decrypt( } } -std::optional SaplingNotePlaintext::decrypt( - const Consensus::Params& params, - int height, - const SaplingEncCiphertext &ciphertext, - const uint256 &ivk, - const uint256 &epk, - const uint256 &cmu -) -{ - auto ret = attempt_sapling_enc_decryption_deserialization(ciphertext, ivk, epk); - - if (!ret) { - return std::nullopt; - } else { - const SaplingNotePlaintext plaintext = *ret; - - // Check leadbyte is allowed at block height - if (!plaintext_version_is_valid(params, height, plaintext.get_leadbyte())) { - LogPrint("receiveunsafe", "Received note plaintext with invalid lead byte %d at height %d", - plaintext.get_leadbyte(), height); - return std::nullopt; - } - - return plaintext_checks_without_height(plaintext, ivk, epk, cmu); - } -} - -std::optional SaplingNotePlaintext::attempt_sapling_enc_decryption_deserialization( - const SaplingEncCiphertext &ciphertext, - const uint256 &ivk, - const uint256 &epk -) -{ - auto encPlaintext = AttemptSaplingEncDecryption(ciphertext, ivk, epk); - - if (!encPlaintext) { - return std::nullopt; - } - - // Deserialize from the plaintext - SaplingNotePlaintext ret; - try { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << encPlaintext.value(); - ss >> ret; - assert(ss.size() == 0); - return ret; - } catch (const boost::thread_interrupted&) { - throw; - } catch (...) { - return std::nullopt; - } -} - -std::optional SaplingNotePlaintext::plaintext_checks_without_height( - const SaplingNotePlaintext &plaintext, - const uint256 &ivk, - const uint256 &epk, - const uint256 &cmu -) -{ - // ZIP 216: pk_d here is serialized from Rust, - // and thus has always used the canonical encoding. - uint256 pk_d; - if (!librustzcash_ivk_to_pkd(ivk.begin(), plaintext.d.data(), pk_d.begin())) { - return std::nullopt; - } - - uint256 cmu_expected; - uint256 rcm = plaintext.rcm(); - if (!librustzcash_sapling_compute_cmu( - true, - plaintext.d.data(), - pk_d.begin(), - plaintext.value(), - rcm.begin(), - cmu_expected.begin() - )) - { - return std::nullopt; - } - - if (cmu_expected != cmu) { - return std::nullopt; - } - - if (plaintext.get_leadbyte() != 0x01) { - assert(plaintext.get_leadbyte() == 0x02); - // ZIP 212: Check that epk is consistent to guard against linkability - // attacks without relying on the soundness of the SNARK. - uint256 expected_epk; - uint256 esk = plaintext.generate_or_derive_esk(); - if (!librustzcash_sapling_ka_derivepublic(plaintext.d.data(), esk.begin(), expected_epk.begin())) { - return std::nullopt; - } - if (expected_epk != epk) { - return std::nullopt; - } - } - - return plaintext; -} - std::optional SaplingNotePlaintext::decrypt( const Consensus::Params& params, int height, @@ -328,12 +243,10 @@ std::optional SaplingNotePlaintext::decrypt( const uint256 &cmu ) { - // The nu5Active flag passed in here enables the new consensus rules from ZIP 216 - // (https://zips.z.cash/zip-0216#specification) on the following fields: - // - // - pk_d in the outCiphertext field of Sapling coinbase outputs. - bool nu5Active = params.NetworkUpgradeActive(height, Consensus::UPGRADE_NU5); - auto ret = attempt_sapling_enc_decryption_deserialization(nu5Active, ciphertext, epk, esk, pk_d); + // We consider ZIP 216 active all of the time because blocks prior to NU5 + // activation (on mainnet and testnet) did not contain Sapling transactions + // that violated its canonicity rule. + auto ret = attempt_sapling_enc_decryption_deserialization(ciphertext, epk, esk, pk_d); if (!ret) { return std::nullopt; @@ -347,19 +260,18 @@ std::optional SaplingNotePlaintext::decrypt( return std::nullopt; } - return plaintext_checks_without_height(nu5Active, plaintext, epk, esk, pk_d, cmu); + return plaintext_checks_without_height(plaintext, epk, esk, pk_d, cmu); } } std::optional SaplingNotePlaintext::attempt_sapling_enc_decryption_deserialization( - bool zip216Enabled, const SaplingEncCiphertext &ciphertext, const uint256 &epk, const uint256 &esk, const uint256 &pk_d ) { - auto encPlaintext = AttemptSaplingEncDecryption(zip216Enabled, ciphertext, epk, esk, pk_d); + auto encPlaintext = AttemptSaplingEncDecryption(ciphertext, epk, esk, pk_d); if (!encPlaintext) { return std::nullopt; @@ -381,7 +293,6 @@ std::optional SaplingNotePlaintext::attempt_sapling_enc_de } std::optional SaplingNotePlaintext::plaintext_checks_without_height( - bool zip216Enabled, const SaplingNotePlaintext &plaintext, const uint256 &epk, const uint256 &esk, @@ -411,7 +322,6 @@ std::optional SaplingNotePlaintext::plaintext_checks_witho uint256 cmu_expected; uint256 rcm = plaintext.rcm(); if (!librustzcash_sapling_compute_cmu( - zip216Enabled, plaintext.d.data(), pk_d.begin(), plaintext.value(), diff --git a/depend/zcash/src/zcash/Note.hpp b/depend/zcash/src/zcash/Note.hpp index 4b3f4e972..731ece937 100644 --- a/depend/zcash/src/zcash/Note.hpp +++ b/depend/zcash/src/zcash/Note.hpp @@ -11,6 +11,8 @@ #include #include +#include + namespace libzcash { class BaseNote { @@ -160,27 +162,8 @@ class SaplingNotePlaintext : public BaseNotePlaintext { SaplingNotePlaintext(const SaplingNote& note, std::array memo); - static std::optional decrypt( - const Consensus::Params& params, - int height, - const SaplingEncCiphertext &ciphertext, - const uint256 &ivk, - const uint256 &epk, - const uint256 &cmu - ); - - static std::optional plaintext_checks_without_height( - const SaplingNotePlaintext &plaintext, - const uint256 &ivk, - const uint256 &epk, - const uint256 &cmu - ); - - static std::optional attempt_sapling_enc_decryption_deserialization( - const SaplingEncCiphertext &ciphertext, - const uint256 &ivk, - const uint256 &epk - ); + static std::pair from_rust( + rust::Box decrypted); static std::optional decrypt( const Consensus::Params& params, @@ -193,7 +176,6 @@ class SaplingNotePlaintext : public BaseNotePlaintext { ); static std::optional plaintext_checks_without_height( - bool zip216Enabled, const SaplingNotePlaintext &plaintext, const uint256 &epk, const uint256 &esk, @@ -202,7 +184,6 @@ class SaplingNotePlaintext : public BaseNotePlaintext { ); static std::optional attempt_sapling_enc_decryption_deserialization( - bool zip216Enabled, const SaplingEncCiphertext &ciphertext, const uint256 &epk, const uint256 &esk, diff --git a/depend/zcash/src/zcash/NoteEncryption.cpp b/depend/zcash/src/zcash/NoteEncryption.cpp index 367fd5127..15c943a01 100644 --- a/depend/zcash/src/zcash/NoteEncryption.cpp +++ b/depend/zcash/src/zcash/NoteEncryption.cpp @@ -112,19 +112,20 @@ std::optional SaplingNoteEncryption::encrypt_to_recipient( throw std::logic_error("already encrypted to the recipient using this key"); } - uint256 dhsecret; - + // Construct the symmetric key // The new consensus rules from ZIP 216 (https://zips.z.cash/zip-0216#specification) - // on pk_d are enabled unconditionally, as they MAY be enforced in advance of NU5 - // activation. - if (!librustzcash_sapling_ka_agree(true, pk_d.begin(), esk.begin(), dhsecret.begin())) { + // on pk_d were enabled unconditionally, even before we started to apply them + // retroactively. + unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; + if (!librustzcash_sapling_ka_derive_symmetric_key( + pk_d.begin(), + esk.begin(), + epk.begin(), + K)) + { return std::nullopt; } - // Construct the symmetric key - unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; - KDF_Sapling(K, dhsecret, epk); - // The nonce is zero because we never reuse keys unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {}; @@ -148,18 +149,19 @@ std::optional AttemptSaplingEncDecryption( const uint256 &epk ) { - uint256 dhsecret; - - // ZIP 216: We can enable the rules unconditionally, because ephemeralKey has always - // been required to not be small-order (https://zips.z.cash/zip-0216#specification). - if (!librustzcash_sapling_ka_agree(true, epk.begin(), ivk.begin(), dhsecret.begin())) { + // Construct the symmetric key. + // We consider ZIP 216 active all of the time because blocks prior to NU5 + // activation (on mainnet and testnet) did not contain Sapling transactions + // that violated its canonicity rule. + unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; + if (!librustzcash_sapling_ka_derive_symmetric_key( + epk.begin(), + ivk.begin(), + epk.begin(), K)) + { return std::nullopt; } - // Construct the symmetric key - unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; - KDF_Sapling(K, dhsecret, epk); - // The nonce is zero because we never reuse keys unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {}; @@ -180,23 +182,26 @@ std::optional AttemptSaplingEncDecryption( } std::optional AttemptSaplingEncDecryption ( - bool zip216Enabled, const SaplingEncCiphertext &ciphertext, const uint256 &epk, const uint256 &esk, const uint256 &pk_d ) { - uint256 dhsecret; - - if (!librustzcash_sapling_ka_agree(zip216Enabled, pk_d.begin(), esk.begin(), dhsecret.begin())) { + // Construct the symmetric key. + // We consider ZIP 216 active all of the time because blocks prior to NU5 + // activation (on mainnet and testnet) did not contain Sapling transactions + // that violated its canonicity rule. + unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; + if (!librustzcash_sapling_ka_derive_symmetric_key( + pk_d.begin(), + esk.begin(), + epk.begin(), + K)) + { return std::nullopt; } - // Construct the symmetric key - unsigned char K[NOTEENCRYPTION_CIPHER_KEYSIZE]; - KDF_Sapling(K, dhsecret, epk); - // The nonce is zero because we never reuse keys unsigned char cipher_nonce[crypto_aead_chacha20poly1305_IETF_NPUBBYTES] = {}; diff --git a/depend/zcash/src/zcash/NoteEncryption.hpp b/depend/zcash/src/zcash/NoteEncryption.hpp index 91d3adb79..a4484004b 100644 --- a/depend/zcash/src/zcash/NoteEncryption.hpp +++ b/depend/zcash/src/zcash/NoteEncryption.hpp @@ -77,7 +77,6 @@ std::optional AttemptSaplingEncDecryption( // Attempts to decrypt a Sapling note using outgoing plaintext. // This will not check that the contents of the ciphertext are correct. std::optional AttemptSaplingEncDecryption ( - bool zip216Enabled, const SaplingEncCiphertext &ciphertext, const uint256 &epk, const uint256 &esk, diff --git a/depend/zcash/src/zcash/address/unified.cpp b/depend/zcash/src/zcash/address/unified.cpp index aaa9fc3cc..4b26497d3 100644 --- a/depend/zcash/src/zcash/address/unified.cpp +++ b/depend/zcash/src/zcash/address/unified.cpp @@ -33,16 +33,16 @@ bool libzcash::HasTransparent(const std::set& receiverTypes) { } Receiver libzcash::RecipientAddressToReceiver(const RecipientAddress& recipient) { - return std::visit(match { + return examine(recipient, match { [](const CKeyID& key) { return Receiver(key); }, [](const CScriptID& scriptId) { return Receiver(scriptId); }, [](const libzcash::OrchardRawAddress& addr) { return Receiver(addr); }, [](const libzcash::SaplingPaymentAddress& addr) { return Receiver(addr); } - }, recipient); + }); } std::string libzcash::DebugPrintReceiver(const Receiver& receiver) { - return std::visit(match { + return examine(receiver, match { [&](const OrchardRawAddress &zaddr) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << zaddr; @@ -65,7 +65,7 @@ std::string libzcash::DebugPrintReceiver(const Receiver& receiver) { unknown.typecode, HexStr(unknown.data.begin(), unknown.data.end())); } - }, receiver); + }); }; std::string libzcash::DebugPrintRecipientAddress(const RecipientAddress& addr) { @@ -198,7 +198,7 @@ UnifiedAddressGenerationResult ZcashdUnifiedFullViewingKey::FindAddress( std::optional ZcashdUnifiedFullViewingKey::GetChangeAddress(const ChangeRequest& req) const { std::optional addr; - std::visit(match { + examine(req, match { [&](const TransparentChangeRequest& req) { if (transparentKey.has_value()) { auto changeAddr = transparentKey.value().GetChangeAddress(req.GetIndex()); @@ -217,7 +217,7 @@ std::optional ZcashdUnifiedFullViewingKey::GetChangeAddress(co addr = orchardKey.value().GetChangeAddress(); } } - }, req); + }); return addr; } diff --git a/depend/zcash/src/zcbenchmarks.cpp b/depend/zcash/src/zcbenchmarks.cpp index 6d88221c3..7c73c9dc0 100644 --- a/depend/zcash/src/zcbenchmarks.cpp +++ b/depend/zcash/src/zcbenchmarks.cpp @@ -35,8 +35,8 @@ #include "zcash/Note.hpp" #include "librustzcash.h" -#include -#include +#include +#include using namespace libzcash; // This method is based on Shutdown from init.cpp @@ -98,7 +98,7 @@ double benchmark_sleep() double benchmark_create_joinsplit() { - Ed25519VerificationKey joinSplitPubKey; + ed25519::VerificationKey joinSplitPubKey; /* Get the anchor of an empty commitment tree. */ uint256 anchor = SproutMerkleTree().root(); @@ -145,7 +145,7 @@ double benchmark_verify_joinsplit(const JSDescription &joinsplit) { struct timeval tv_start; timer_start(tv_start); - Ed25519VerificationKey joinSplitPubKey; + ed25519::VerificationKey joinSplitPubKey; auto verifier = ProofVerifier::Strict(); verifier.VerifySprout(joinsplit, joinSplitPubKey); return timer_stop(tv_start); @@ -310,7 +310,7 @@ double benchmark_try_decrypt_sapling_notes(size_t nKeys) struct timeval tv_start; timer_start(tv_start); - auto noteDataMapAndAddressesToAdd = wallet.FindMySaplingNotes(Params().GetConsensus(), tx, 1); + auto noteDataMapAndAddressesToAdd = wallet.FindMySaplingNotes(Params(), tx, 1); assert(noteDataMapAndAddressesToAdd.first.empty()); return timer_stop(tv_start); } @@ -458,6 +458,7 @@ class FakeCoinsViewDB : public CCoinsView { OrchardMerkleFrontier emptyOrchardTree; orchardTrees.push_back(emptyOrchardTree); } + ~FakeCoinsViewDB() {} void SetSaplingTrees(std::vector trees) { saplingTrees.clear(); @@ -546,6 +547,10 @@ class FakeCoinsViewDB : public CCoinsView { } } + HistoryIndex GetHistoryLength(uint32_t epochId) const { return 0; } + HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const { return HistoryNode(); } + uint256 GetHistoryRoot(uint32_t epochId) const { return uint256(); } + bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const uint256 &hashSproutAnchor, diff --git a/depend/zcash/src/zip317.cpp b/depend/zcash/src/zip317.cpp new file mode 100644 index 000000000..b9b65e7c8 --- /dev/null +++ b/depend/zcash/src/zip317.cpp @@ -0,0 +1,39 @@ +// Copyright (c) 2023-2023 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "zip317.h" + +#include + +static size_t ceil_div(size_t num, size_t den) { + return (num + den - 1)/den; +} + +CAmount CalculateConventionalFee(size_t logicalActionCount) { + return MARGINAL_FEE * std::max(GRACE_ACTIONS, logicalActionCount); +} + +template +static size_t GetTxIOFieldSize(const std::vector& txIOs) { + auto size = GetSerializeSize(txIOs, SER_NETWORK, PROTOCOL_VERSION); + auto countSize = GetSizeOfCompactSize(txIOs.size()); + return size - countSize; +} + +size_t CalculateLogicalActionCount( + const std::vector& vin, + const std::vector& vout, + unsigned int joinSplitCount, + unsigned int saplingSpendCount, + unsigned int saplingOutputCount, + unsigned int orchardActionCount) { + const size_t tx_in_total_size = GetTxIOFieldSize(vin); + const size_t tx_out_total_size = GetTxIOFieldSize(vout); + + return std::max(ceil_div(tx_in_total_size, P2PKH_STANDARD_INPUT_SIZE), + ceil_div(tx_out_total_size, P2PKH_STANDARD_OUTPUT_SIZE)) + + 2 * joinSplitCount + + std::max(saplingSpendCount, saplingOutputCount) + + orchardActionCount; +} diff --git a/depend/zcash/src/zip317.h b/depend/zcash/src/zip317.h new file mode 100644 index 000000000..d24c2c4ad --- /dev/null +++ b/depend/zcash/src/zip317.h @@ -0,0 +1,43 @@ +// Copyright (c) 2023-2023 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_ZIP317_H +#define ZCASH_ZIP317_H + +#include "amount.h" +#include "primitives/transaction.h" + +#include +#include +#include + +// Constants for fee calculation. +static const CAmount MARGINAL_FEE = 5000; +static const size_t GRACE_ACTIONS = 2; +static const size_t P2PKH_STANDARD_INPUT_SIZE = 150; +static const size_t P2PKH_STANDARD_OUTPUT_SIZE = 34; + +// Constants for block template construction. +static const int64_t WEIGHT_RATIO_SCALE = INT64_C(10000000000000000); +static const int64_t WEIGHT_RATIO_CAP = 4; +static const size_t DEFAULT_BLOCK_UNPAID_ACTION_LIMIT = 50; + +/// This is the lowest the conventional fee can be in ZIP 317. +static const CAmount MINIMUM_FEE = MARGINAL_FEE * GRACE_ACTIONS; + +/// Return the conventional fee for the given `logicalActionCount` calculated according to +/// . +CAmount CalculateConventionalFee(size_t logicalActionCount); + +/// Return the number of logical actions calculated according to +/// . +size_t CalculateLogicalActionCount( + const std::vector& vin, + const std::vector& vout, + unsigned int joinSplitCount, + unsigned int saplingSpendCount, + unsigned int saplingOutputCount, + unsigned int orchardActionCount); + +#endif // ZCASH_ZIP317_H diff --git a/depend/zcash/zcutil/fetch-params.sh b/depend/zcash/zcutil/fetch-params.sh index b4ef2c063..f17114bd4 100755 --- a/depend/zcash/zcutil/fetch-params.sh +++ b/depend/zcash/zcutil/fetch-params.sh @@ -29,7 +29,7 @@ fi SAPLING_SPEND_NAME='sapling-spend.params' SAPLING_OUTPUT_NAME='sapling-output.params' SAPLING_SPROUT_GROTH16_NAME='sprout-groth16.params' -DOWNLOAD_URL="https://download.z.cash/downloads" +DOWNLOAD_URL="${ALTERNATIVE_DOWNLOAD_URL:-https://download.z.cash/downloads}" IPFS_HASH="/ipfs/QmXRHVGLQBiKwvNq7c2vPxAKz1zRVmMYbmt7G5TQss7tY7" SHA256CMD="$(command -v sha256sum || echo shasum)" @@ -141,12 +141,14 @@ fetch_params() { cat "${dlname}.part.1" "${dlname}.part.2" > "${dlname}" rm "${dlname}.part.1" "${dlname}.part.2" + set +e "$SHA256CMD" $SHA256ARGS -c <&2 + rm "$output" + fetch_params "$filename" "$output" "$expectedhash" + fi + fi + + unset -v filename + unset -v output + unset -v expectedhash +} + # Use flock to prevent parallel execution. lock() { if [ "$uname_S" = "Darwin" ]; then @@ -197,7 +234,10 @@ Zcash - ${SCRIPT_NAME} This script will fetch the Zcash zkSNARK parameters and verify their integrity with sha256sum. -If they already exist locally, it will exit now and do nothing else. +If the files are already present and have the correct sha256sum, no +networking is used. Parameter files with incorrect sha256sums are +deleted and re-downloaded. + EOF # Now create PARAMS_DIR and insert a README if necessary: @@ -234,9 +274,9 @@ EOF #fetch_params "$SPROUT_VKEY_NAME" "$PARAMS_DIR/$SPROUT_VKEY_NAME" "4bd498dae0aacfd8e98dc306338d017d9c08dd0918ead18172bd0aec2fc5df82" # Sapling parameters: - fetch_params "$SAPLING_SPEND_NAME" "$PARAMS_DIR/$SAPLING_SPEND_NAME" "8e48ffd23abb3a5fd9c5589204f32d9c31285a04b78096ba40a79b75677efc13" - fetch_params "$SAPLING_OUTPUT_NAME" "$PARAMS_DIR/$SAPLING_OUTPUT_NAME" "2f0ebbcbb9bb0bcffe95a397e7eba89c29eb4dde6191c339db88570e3f3fb0e4" - fetch_params "$SAPLING_SPROUT_GROTH16_NAME" "$PARAMS_DIR/$SAPLING_SPROUT_GROTH16_NAME" "b685d700c60328498fbde589c8c7c484c722b788b265b72af448a5bf0ee55b50" + check_and_fetch_params "$SAPLING_SPEND_NAME" "$PARAMS_DIR/$SAPLING_SPEND_NAME" "8e48ffd23abb3a5fd9c5589204f32d9c31285a04b78096ba40a79b75677efc13" + check_and_fetch_params "$SAPLING_OUTPUT_NAME" "$PARAMS_DIR/$SAPLING_OUTPUT_NAME" "2f0ebbcbb9bb0bcffe95a397e7eba89c29eb4dde6191c339db88570e3f3fb0e4" + check_and_fetch_params "$SAPLING_SPROUT_GROTH16_NAME" "$PARAMS_DIR/$SAPLING_SPROUT_GROTH16_NAME" "b685d700c60328498fbde589c8c7c484c722b788b265b72af448a5bf0ee55b50" } if [ "${1:-}" = '--testnet' ] diff --git a/depend/zcash/zcutil/make-release.py b/depend/zcash/zcutil/make-release.py index 06b0b6642..c65751921 100755 --- a/depend/zcash/zcutil/make-release.py +++ b/depend/zcash/zcutil/make-release.py @@ -9,6 +9,7 @@ import traceback import unittest import random +from datetime import date, datetime, timedelta, timezone from io import StringIO from functools import wraps @@ -108,6 +109,9 @@ def main_logged(revision, release, releaseprev, releasefrom, releaseheight, hotf ), ) + update_book(release, releaseheight) + commit('Updated book for {}.'.format(release.novtext)) + def phase(message): def deco(f): @@ -333,6 +337,11 @@ def update_debian_changelog(release): ) +@phase('Updating book.') +def update_book(release, releaseheight): + patch_book_release_support(release, releaseheight) + + # Helper code: def commit(message): logging.info('Committing: %r', message) @@ -388,6 +397,60 @@ def patch_gitian_linux_yml(release, releaseprev, path): outf.write(inf.read()) +def patch_book_release_support(release, releaseheight): + with PathPatcher('doc/book/src/user/release-support.md') as (inf, outf): + # Find the start marker. + cur_line = inf.readline() + while not 'RELEASE_SCRIPT_START_MARKER' in cur_line: + outf.write(cur_line) + cur_line = inf.readline() + outf.write(cur_line) + + # The next two lines are the table heading. + for _ in range(2): + outf.write(inf.readline()) + + # The remaining lines before the end marker are table rows. + table_rows = [] + cur_line = inf.readline() + while not 'RELEASE_SCRIPT_END_MARKER' in cur_line: + [row_ver, row_released, row_halt, row_eos] = cur_line.strip('| \n').split(' | ') + row_released = date.fromisoformat(row_released) + row_eos = date.fromisoformat(row_eos) + table_rows.append((row_ver, row_released, int(row_halt), row_eos)) + cur_line = inf.readline() + + # Prune rows for releases that have reached EoS. + today = datetime.now(timezone.utc).date() + table_rows = [row for row in table_rows if row[3] >= today] + + # Add a row for this release. + with open('src/deprecation.h', 'r', encoding='utf8') as f: + weeks_prefix = 'RELEASE_TO_DEPRECATION_WEEKS = ' + halt_prefix = 'DEPRECATION_HEIGHT = ' + for line in f: + if weeks_prefix in line: + weeks_to_eos = int(line.split(weeks_prefix)[1].split(';')[0]) + if halt_prefix in line: + val = line.split(halt_prefix)[1].split(';')[0] + if val == 'APPROX_RELEASE_HEIGHT + ACTIVATION_TO_DEPRECATION_BLOCKS': + halt_height = None + else: + halt_height = int(val) + if halt_height is None: + halt_height = releaseheight + (weeks_to_eos * 7 * 24 * 48) + eos_date = today + timedelta(weeks=weeks_to_eos) + table_rows.append((release.novtext, today, halt_height, eos_date)) + + # Write out the updated table rows. + for row in table_rows: + outf.write('| %s | %s | %s | %s |\n' % row) + + # Write out the end marker and the rest of the page. + outf.write(cur_line) + outf.write(inf.read()) + + def _patch_build_defs(release, path, pattern): rgx = re.compile(pattern) with PathPatcher(path) as (inf, outf): diff --git a/depend/zcash/zcutil/release-notes.py b/depend/zcash/zcutil/release-notes.py index 91b145fb2..8e4ecbd68 100755 --- a/depend/zcash/zcutil/release-notes.py +++ b/depend/zcash/zcutil/release-notes.py @@ -33,6 +33,7 @@ 'MarcoFalke': 'Marco Falke', 'mdr0id': 'Marshall Gaucher', 'paveljanik': 'Pavel Janík', + 'Sasha': 'sasha', 'Simon': 'Simon Liu', 'str4d': 'Jack Grigg', 'zebambam': 'Benjamin Winston', diff --git a/src/bridge.rs b/src/bridge.rs new file mode 100644 index 000000000..83a5aa20e --- /dev/null +++ b/src/bridge.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/bridge.rs"); diff --git a/src/builder_ffi.rs b/src/builder_ffi.rs new file mode 100644 index 000000000..e44fa5bf1 --- /dev/null +++ b/src/builder_ffi.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/builder_ffi.rs"); diff --git a/src/incremental_merkle_tree.rs b/src/incremental_merkle_tree.rs new file mode 100644 index 000000000..c3076ba63 --- /dev/null +++ b/src/incremental_merkle_tree.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/incremental_merkle_tree.rs"); diff --git a/src/lib.rs b/src/lib.rs index e31356846..bef7e56ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,65 @@ +//! Rust bindings for Zcash transparent scripts. + #![doc(html_logo_url = "https://www.zfnd.org/images/zebra-icon.png")] #![doc(html_root_url = "https://docs.rs/zcash_script/0.1.11")] +#![allow(missing_docs)] +#![allow(clippy::needless_lifetimes)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +#![allow(unsafe_code)] +#![allow(unused_imports)] +// Use the generated C++ bindings include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +// Include the items from depend/zcash/src/rust/src/rustzcash.rs (librustzcash/lib.rs) +// that we need + +/// The code that uses this constant is not called by zcash_script. +static mut SAPLING_SPEND_VK: Option> = None; +/// The code that uses this constant is not called by zcash_script. +static mut SAPLING_OUTPUT_VK: Option> = None; +/// The code that uses this constant is not called by zcash_script. +static mut SAPLING_SPEND_PARAMS: Option> = None; +/// The code that uses this constant is not called by zcash_script. +static mut SAPLING_OUTPUT_PARAMS: Option> = None; + +/// The code that uses this constant is not called by zcash_script. +static mut ORCHARD_PK: Option = None; +/// The code that uses this constant is not called by zcash_script. static mut ORCHARD_VK: Option = None; +/// Converts CtOption into Option +fn de_ct(ct: subtle::CtOption) -> Option { + if ct.is_some().into() { + Some(ct.unwrap()) + } else { + None + } +} + +/// The size of a Groth16 Sapling proof. +const GROTH_PROOF_SIZE: usize = 48 // π_A + + 96 // π_B + + 48; // π_C + +// Include the modules from depend/zcash/src/rust (librustzcash) that we need mod blake2b; +mod bridge; mod bundlecache; +mod incremental_merkle_tree; +mod merkle_frontier; +mod note_encryption; +mod orchard_bundle; +mod params; +mod sapling; +mod streams; +mod wallet; +mod wallet_scanner; +mod zcashd_orchard; + +mod builder_ffi; mod orchard_ffi; mod streams_ffi; mod transaction_ffi; diff --git a/src/merkle_frontier.rs b/src/merkle_frontier.rs new file mode 100644 index 000000000..7af85eb83 --- /dev/null +++ b/src/merkle_frontier.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/merkle_frontier.rs"); diff --git a/src/note_encryption.rs b/src/note_encryption.rs new file mode 100644 index 000000000..4657dfa35 --- /dev/null +++ b/src/note_encryption.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/note_encryption.rs"); diff --git a/src/orchard_bundle.rs b/src/orchard_bundle.rs new file mode 100644 index 000000000..0ae3b226f --- /dev/null +++ b/src/orchard_bundle.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/orchard_bundle.rs"); diff --git a/src/params.rs b/src/params.rs new file mode 100644 index 000000000..870533ff7 --- /dev/null +++ b/src/params.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/params.rs"); diff --git a/src/sapling.rs b/src/sapling.rs new file mode 100644 index 000000000..06c4a40f6 --- /dev/null +++ b/src/sapling.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/sapling.rs"); diff --git a/src/streams.rs b/src/streams.rs new file mode 100644 index 000000000..804d00707 --- /dev/null +++ b/src/streams.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/streams.rs"); diff --git a/src/wallet.rs b/src/wallet.rs new file mode 100644 index 000000000..334b3fc4b --- /dev/null +++ b/src/wallet.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/wallet.rs"); diff --git a/src/wallet_scanner.rs b/src/wallet_scanner.rs new file mode 100644 index 000000000..037828f64 --- /dev/null +++ b/src/wallet_scanner.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/wallet_scanner.rs"); diff --git a/src/zcashd_orchard.rs b/src/zcashd_orchard.rs new file mode 100644 index 000000000..bd3ebd06c --- /dev/null +++ b/src/zcashd_orchard.rs @@ -0,0 +1 @@ +include!("../depend/zcash/src/rust/src/zcashd_orchard.rs");