diff --git a/src/consensus/params.cpp b/src/consensus/params.cpp index 2ced730e8..58528b7b4 100644 --- a/src/consensus/params.cpp +++ b/src/consensus/params.cpp @@ -169,10 +169,11 @@ namespace Consensus { addresses.push_back(GetScriptForDestination(taddr)); } else { auto zaddr = keyIO.DecodePaymentAddress(addr); - // If the string is not a valid transparent or Sapling address, we will - // throw here. + if (!(zaddr.has_value() && std::holds_alternative(zaddr.value()))) { + throw std::runtime_error("Funding stream address was not a valid transparent or Sapling address."); + } - addresses.push_back(std::get(zaddr)); + addresses.push_back(std::get(zaddr.value())); } } diff --git a/src/gtest/test_keys.cpp b/src/gtest/test_keys.cpp index b7aef27bb..a632f3dec 100644 --- a/src/gtest/test_keys.cpp +++ b/src/gtest/test_keys.cpp @@ -27,11 +27,11 @@ TEST(Keys, EncodeAndDecodeSapling) Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)); auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string); - EXPECT_TRUE(IsValidSpendingKey(spendingkey2)); + EXPECT_TRUE(spendingkey2.has_value()); - ASSERT_TRUE(std::get_if(&spendingkey2) != nullptr); - auto sk2 = std::get(spendingkey2); - EXPECT_EQ(sk, sk2); + auto sk2 = std::get_if(&spendingkey2.value()); + EXPECT_NE(sk2, nullptr); + EXPECT_EQ(sk, *sk2); } { auto extfvk = sk.ToXFVK(); @@ -41,11 +41,11 @@ TEST(Keys, EncodeAndDecodeSapling) Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_FVK)); auto viewingkey2 = keyIO.DecodeViewingKey(vk_string); - EXPECT_TRUE(IsValidViewingKey(viewingkey2)); + EXPECT_TRUE(viewingkey2.has_value()); - ASSERT_TRUE(std::get_if(&viewingkey2) != nullptr); - auto extfvk2 = std::get(viewingkey2); - EXPECT_EQ(extfvk, extfvk2); + auto extfvk2 = std::get_if(&viewingkey2.value()); + EXPECT_NE(extfvk2, nullptr); + EXPECT_EQ(extfvk, *extfvk2); } { auto addr = sk.DefaultAddress(); @@ -56,11 +56,11 @@ TEST(Keys, EncodeAndDecodeSapling) Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)); auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string); - EXPECT_TRUE(IsValidPaymentAddress(paymentaddr2)); + EXPECT_TRUE(paymentaddr2.has_value()); - ASSERT_TRUE(std::get_if(&paymentaddr2) != nullptr); - auto addr2 = std::get(paymentaddr2); - EXPECT_EQ(addr, addr2); + auto addr2 = std::get_if(&paymentaddr2.value()); + EXPECT_NE(addr2, nullptr); + EXPECT_EQ(addr, *addr2); } } } @@ -152,8 +152,10 @@ TEST(Keys, EncodeAndDecodeUnified) std::string expected(expectedBytes.begin(), expectedBytes.end()); auto decoded = keyIO.DecodePaymentAddress(expected); - ASSERT_TRUE(std::holds_alternative(decoded)); - EXPECT_EQ(std::get(decoded), ua); + EXPECT_TRUE(decoded.has_value()); + auto ua_ptr = std::get_if(&decoded.value()); + EXPECT_NE(ua_ptr, nullptr); + EXPECT_EQ(*ua_ptr, ua); auto encoded = keyIO.EncodePaymentAddress(ua); EXPECT_EQ(encoded, expected); diff --git a/src/init.cpp b/src/init.cpp index b632d7e2b..040ff0707 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1102,7 +1102,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!IsValidDestination(addr)) { // Try a payment address auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]); - if (!std::visit(IsValidMinerAddress(), std::visit(ExtractMinerAddress(), zaddr))) + if (!zaddr.has_value() || std::visit(ExtractMinerAddress(), zaddr.value()).has_value()) { return InitError(strprintf( _("Invalid address for -mineraddress=: '%s' (must be a Sapling or transparent address)"), @@ -1684,8 +1684,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) minerAddressInLocalWallet = pwalletMain->HaveKey(keyID); } else { auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]); + if (!zaddr.has_value()) { + return InitError(_("-mineraddress is not a valid zcash address.")); + } minerAddressInLocalWallet = std::visit( - HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); + HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr.value()); } } if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) { diff --git a/src/key_io.cpp b/src/key_io.cpp index db83bea04..671cc8d34 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -151,8 +151,6 @@ public: zcash_address_string_free(encoded); return res; } - - std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } }; class ViewingKeyEncoder @@ -189,8 +187,6 @@ public: memory_cleanse(data.data(), data.size()); return ret; } - - std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } }; class SpendingKeyEncoder @@ -227,8 +223,6 @@ public: memory_cleanse(data.data(), data.size()); return ret; } - - std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } }; // Sizes of SaplingPaymentAddress, SaplingExtendedFullViewingKey, and @@ -356,7 +350,7 @@ std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) } template -T1 DecodeAny( +std::optional DecodeAny( const KeyConstants& keyConstants, const std::string& str, std::pair sprout, @@ -393,7 +387,7 @@ T1 DecodeAny( } memory_cleanse(data.data(), data.size()); - return libzcash::InvalidEncoding(); + return std::nullopt; } /** @@ -447,7 +441,7 @@ static bool AddUnknownReceiver(void* ua, uint32_t typecode, const unsigned char* return reinterpret_cast(ua)->AddReceiver(receiver); } -libzcash::PaymentAddress KeyIO::DecodePaymentAddress(const std::string& str) +std::optional KeyIO::DecodePaymentAddress(const std::string& str) { // Try parsing as a Unified Address. libzcash::UnifiedAddress ua; @@ -475,7 +469,7 @@ libzcash::PaymentAddress KeyIO::DecodePaymentAddress(const std::string& str) } bool KeyIO::IsValidPaymentAddressString(const std::string& str) { - return IsValidPaymentAddress(DecodePaymentAddress(str)); + return DecodePaymentAddress(str).has_value(); } std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk) @@ -483,7 +477,7 @@ std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk) return std::visit(ViewingKeyEncoder(keyConstants), vk); } -libzcash::ViewingKey KeyIO::DecodeViewingKey(const std::string& str) +std::optional KeyIO::DecodeViewingKey(const std::string& str) { return DecodeAny KeyIO::DecodeSpendingKey(const std::string& str) { return DecodeAny DecodePaymentAddress(const std::string& str); bool IsValidPaymentAddressString(const std::string& str); std::string EncodeViewingKey(const libzcash::ViewingKey& vk); - libzcash::ViewingKey DecodeViewingKey(const std::string& str); + std::optional DecodeViewingKey(const std::string& str); std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey); - libzcash::SpendingKey DecodeSpendingKey(const std::string& str); + std::optional DecodeSpendingKey(const std::string& str); }; #endif // BITCOIN_KEY_IO_H diff --git a/src/miner.cpp b/src/miner.cpp index 0ed57273e..d2a21a987 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -117,9 +117,7 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, } bool IsShieldedMinerAddress(const MinerAddress& minerAddr) { - return !( - std::holds_alternative(minerAddr) || - std::holds_alternative>(minerAddr)); + return !std::holds_alternative>(minerAddr); } class AddFundingStreamValueToTx @@ -241,8 +239,6 @@ public: } } - void operator()(const InvalidMinerAddress &invalid) const {} - // Create shielded output void operator()(const libzcash::SaplingPaymentAddress &pa) const { auto ctx = librustzcash_sapling_proving_ctx_init(); @@ -721,9 +717,12 @@ void GetMinerAddress(MinerAddress &minerAddress) minerAddress = mAddr; } else { // Try a payment address - auto zaddr = std::visit(ExtractMinerAddress(), keyIO.DecodePaymentAddress(mAddrArg)); - if (std::visit(IsValidMinerAddress(), zaddr)) { - minerAddress = zaddr; + auto zaddr0 = keyIO.DecodePaymentAddress(mAddrArg); + if (zaddr0.has_value()) { + auto zaddr = std::visit(ExtractMinerAddress(), zaddr0.value()); + if (zaddr.has_value()) { + minerAddress = zaddr.value(); + } } } } diff --git a/src/miner.h b/src/miner.h index 52ac1a29d..61dcc938b 100644 --- a/src/miner.h +++ b/src/miner.h @@ -23,14 +23,7 @@ static const int DEFAULT_GENERATE_THREADS = 1; static const bool DEFAULT_PRINTPRIORITY = false; -class InvalidMinerAddress { -public: - friend bool operator==(const InvalidMinerAddress &a, const InvalidMinerAddress &b) { return true; } - friend bool operator<(const InvalidMinerAddress &a, const InvalidMinerAddress &b) { return true; } -}; - typedef std::variant< - InvalidMinerAddress, libzcash::SaplingPaymentAddress, boost::shared_ptr> MinerAddress; @@ -39,16 +32,13 @@ class ExtractMinerAddress public: ExtractMinerAddress() {} - MinerAddress operator()(const libzcash::InvalidEncoding &invalid) const { - return InvalidMinerAddress(); + std::optional operator()(const libzcash::SproutPaymentAddress &addr) const { + return std::nullopt; } - MinerAddress operator()(const libzcash::SproutPaymentAddress &addr) const { - return InvalidMinerAddress(); - } - MinerAddress operator()(const libzcash::SaplingPaymentAddress &addr) const { + std::optional operator()(const libzcash::SaplingPaymentAddress &addr) const { return addr; } - MinerAddress operator()(const libzcash::UnifiedAddress &addr) const { + std::optional operator()(const libzcash::UnifiedAddress &addr) const { auto recipient = RecipientForPaymentAddress()(addr); if (recipient) { // This looks like a recursive call, but we are actually calling @@ -66,7 +56,7 @@ public: // Either the UA only contains unknown shielded receivers (unlikely that we // wouldn't know about them), or it only contains transparent receivers // (which are invalid). - return InvalidMinerAddress(); + return std::nullopt; } } }; @@ -76,7 +66,6 @@ class KeepMinerAddress public: KeepMinerAddress() {} - void operator()(const InvalidMinerAddress &invalid) const {} void operator()(const libzcash::SaplingPaymentAddress &pa) const {} void operator()(const boost::shared_ptr &coinbaseScript) const { coinbaseScript->KeepScript(); @@ -90,9 +79,6 @@ class IsValidMinerAddress public: IsValidMinerAddress() {} - bool operator()(const InvalidMinerAddress &invalid) const { - return false; - } bool operator()(const libzcash::SaplingPaymentAddress &pa) const { return true; } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index dffee0b8b..c09b694bf 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -217,8 +217,6 @@ UniValue validateaddress(const UniValue& params, bool fHelp) class DescribePaymentAddressVisitor { public: - UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); } - UniValue operator()(const libzcash::SproutPaymentAddress &zaddr) const { UniValue obj(UniValue::VOBJ); obj.pushKV("type", "sprout"); @@ -288,14 +286,14 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); string strAddress = params[0].get_str(); auto address = keyIO.DecodePaymentAddress(strAddress); - bool isValid = IsValidPaymentAddress(address); + bool isValid = address.has_value(); UniValue ret(UniValue::VOBJ); ret.pushKV("isvalid", isValid); if (isValid) { ret.pushKV("address", strAddress); - UniValue detail = std::visit(DescribePaymentAddressVisitor(), address); + UniValue detail = std::visit(DescribePaymentAddressVisitor(), address.value()); ret.pushKVs(detail); } return ret; diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 959bb25da..2de4e28f4 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -202,9 +202,9 @@ BOOST_AUTO_TEST_CASE(zc_address_test) BOOST_CHECK(sk_string[1] == 'K'); auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string); - BOOST_CHECK(IsValidSpendingKey(spendingkey2)); - BOOST_ASSERT(std::get_if(&spendingkey2) != nullptr); - auto sk2 = std::get(spendingkey2); + BOOST_CHECK(spendingkey2.has_value()); + BOOST_ASSERT(std::get_if(&spendingkey2.value()) != nullptr); + auto sk2 = std::get(spendingkey2.value()); BOOST_CHECK(sk.inner() == sk2.inner()); } { @@ -216,10 +216,10 @@ BOOST_AUTO_TEST_CASE(zc_address_test) BOOST_CHECK(addr_string[1] == 'c'); auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string); - BOOST_ASSERT(IsValidPaymentAddress(paymentaddr2)); + BOOST_ASSERT(paymentaddr2.has_value()); - BOOST_ASSERT(std::get_if(&paymentaddr2) != nullptr); - auto addr2 = std::get(paymentaddr2); + BOOST_ASSERT(std::get_if(&paymentaddr2.value()) != nullptr); + auto addr2 = std::get(paymentaddr2.value()); BOOST_CHECK(addr.a_pk == addr2.a_pk); BOOST_CHECK(addr.pk_enc == addr2.pk_enc); } @@ -240,10 +240,10 @@ BOOST_AUTO_TEST_CASE(zs_address_test) BOOST_CHECK(sk_string.compare(0, 27, Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)) == 0); auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string); - BOOST_CHECK(IsValidSpendingKey(spendingkey2)); + BOOST_CHECK(spendingkey2.has_value()); - BOOST_ASSERT(std::get_if(&spendingkey2) != nullptr); - auto sk2 = std::get(spendingkey2); + BOOST_ASSERT(std::get_if(&spendingkey2.value()) != nullptr); + auto sk2 = std::get(spendingkey2.value()); BOOST_CHECK(sk == sk2); } { @@ -253,10 +253,10 @@ BOOST_AUTO_TEST_CASE(zs_address_test) BOOST_CHECK(addr_string.compare(0, 15, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0); auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string); - BOOST_CHECK(IsValidPaymentAddress(paymentaddr2)); + BOOST_CHECK(paymentaddr2.has_value()); - BOOST_ASSERT(std::get_if(&paymentaddr2) != nullptr); - auto addr2 = std::get(paymentaddr2); + BOOST_ASSERT(std::get_if(&paymentaddr2.value()) != nullptr); + auto addr2 = std::get(paymentaddr2.value()); BOOST_CHECK(addr == addr2); } } diff --git a/src/util/match.h b/src/util/match.h new file mode 100644 index 000000000..d966ae4da --- /dev/null +++ b/src/util/match.h @@ -0,0 +1,24 @@ +// Copyright (c) 2021 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_UTIL_MATCH_H +#define ZCASH_UTIL_MATCH_H + +// Helper for using `std::visit` with Rust-style match syntax. +// +// This can be used in place of defining an explicit visitor. `std::visit` requires an +// exhaustive match; to emulate Rust's catch-all binding, use an `(auto arg)` template +// operator(). +// +// Care must be taken that implicit conversions are handled correctly. For instance, a +// `(double arg)` operator() *will also* bind to `int` and `long`, if there isn't an +// earlier satisfying match. +// +// This corresponds to visitor example #4 in the `std::visit` documentation: +// https://en.cppreference.com/w/cpp/utility/variant/visit +template struct match : Ts... { using Ts::operator()...; }; +// explicit deduction guide (not needed as of C++20) +template match(Ts...) -> match; + +#endif // ZCASH_UTIL_MATCH_H diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index f6faaa473..b3ea22bbe 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -102,9 +102,9 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress( if (!isToTaddr_) { auto address = keyIO.DecodePaymentAddress(std::get<0>(recipient)); - if (IsValidPaymentAddress(address)) { + if (address.has_value()) { isToZaddr_ = true; - toPaymentAddress_ = address; + toPaymentAddress_ = address.value(); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address"); } diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 6671298c7..d7f48fc60 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -85,7 +85,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { // We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying // an anchor at height N-10 for each Sprout JoinSplit description // Consider, should notes be sorted? - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, "", 11); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 11); } CAmount availableFunds = 0; for (const SproutNoteEntry& sproutEntry : sproutEntries) { @@ -197,8 +197,9 @@ libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigration if (mapArgs.count("-migrationdestaddress")) { std::string migrationDestAddress = mapArgs["-migrationdestaddress"]; auto address = keyIO.DecodePaymentAddress(migrationDestAddress); - auto saplingAddress = std::get_if(&address); - assert(saplingAddress != nullptr); // This is checked in init.cpp + assert(address.has_value()); // This is checked in init.cpp + auto saplingAddress = std::get_if(&address.value()); + assert(saplingAddress != nullptr); // This is also checked in init.cpp return *saplingAddress; } // Derive the address for Sapling account 0 diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 155afd700..30b899207 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -98,15 +98,15 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( if (!isfromtaddr_) { auto address = keyIO.DecodePaymentAddress(fromAddress); - if (IsValidPaymentAddress(address)) { + if (address.has_value()) { // We don't need to lock on the wallet as spending key related methods are thread-safe - if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), address)) { + if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), address.value())) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, no spending key found for zaddr"); } isfromzaddr_ = true; - frompaymentaddress_ = address; - spendingkey_ = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address).value(); + frompaymentaddress_ = address.value(); + spendingkey_ = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address.value()).value(); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address"); } @@ -375,8 +375,8 @@ bool AsyncRPCOperation_sendmany::main_impl() { auto hexMemo = r.memo; auto addr = keyIO.DecodePaymentAddress(address); - assert(std::get_if(&addr) != nullptr); - auto to = std::get(addr); + assert(addr.has_value() && std::get_if(&addr.value()) != nullptr); + auto to = std::get(addr.value()); auto memo = get_memo_from_hex_string(hexMemo); @@ -533,12 +533,14 @@ bool AsyncRPCOperation_sendmany::main_impl() { std::string hexMemo = smr.memo; zOutputsDeque.pop_front(); - PaymentAddress pa = keyIO.DecodePaymentAddress(address); - JSOutput jso = JSOutput(std::get(pa), value); - if (hexMemo.size() > 0) { - jso.memo = get_memo_from_hex_string(hexMemo); + std::optional pa = keyIO.DecodePaymentAddress(address); + if (pa.has_value()) { + JSOutput jso = JSOutput(std::get(pa.value()), value); + if (hexMemo.size() > 0) { + jso.memo = get_memo_from_hex_string(hexMemo); + } + info.vjsout.push_back(jso); } - info.vjsout.push_back(jso); // Funds are removed from the value pool and enter the private pool info.vpub_old += value; @@ -800,13 +802,15 @@ bool AsyncRPCOperation_sendmany::main_impl() { assert(value==0); info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new } else { - PaymentAddress pa = keyIO.DecodePaymentAddress(address); - // If we are here, we know we have no Sapling outputs. - JSOutput jso = JSOutput(std::get(pa), value); - if (hexMemo.size() > 0) { - jso.memo = get_memo_from_hex_string(hexMemo); + std::optional pa = keyIO.DecodePaymentAddress(address); + if (pa.has_value()) { + // If we are here, we know we have no Sapling outputs. + JSOutput jso = JSOutput(std::get(pa.value()), value); + if (hexMemo.size() > 0) { + jso.memo = get_memo_from_hex_string(hexMemo); + } + info.vjsout.push_back(jso); } - info.vjsout.push_back(jso); } // create output for any change @@ -927,7 +931,9 @@ bool AsyncRPCOperation_sendmany::load_inputs(TxValues& txValues) { bool AsyncRPCOperation_sendmany::find_unspent_notes() { std::vector sproutEntries; std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress_, mindepth_); + // TODO: move this to the caller + auto zaddr = KeyIO(Params()).DecodePaymentAddress(fromaddress_); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddr, mindepth_); // If using the TransactionBuilder, we only want Sapling notes. // If not using it, we only want Sprout notes. diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index 31098c404..0b1960b76 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -81,8 +81,8 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( // Check the destination address is valid for this network i.e. not testnet being used on mainnet KeyIO keyIO(Params()); auto address = keyIO.DecodePaymentAddress(toAddress); - if (IsValidPaymentAddress(address)) { - tozaddr_ = address; + if (address.has_value()) { + tozaddr_ = address.value(); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid to address"); } @@ -264,11 +264,6 @@ bool ShieldToAddress::operator()(const libzcash::UnifiedAddress &uaddr) const { return false; } -bool ShieldToAddress::operator()(const libzcash::InvalidEncoding& no) const { - return false; -} - - UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInfo & info) { uint32_t consensusBranchId; uint256 anchor; diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.h b/src/wallet/asyncrpcoperation_shieldcoinbase.h index badc7fa5a..c1048c00a 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.h +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.h @@ -106,7 +106,6 @@ public: bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::UnifiedAddress &uaddr) const; - bool operator()(const libzcash::InvalidEncoding& no) const; }; diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 33361a22b..142d83357 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -206,11 +206,11 @@ TEST(WalletTests, FindUnspentSproutNotes) { // We currently have an unspent and unconfirmed note in the wallet (depth of -1) std::vector sproutEntries; std::vector saplingEntries; - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", -1); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, -1); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); @@ -233,15 +233,15 @@ TEST(WalletTests, FindUnspentSproutNotes) { // We now have an unspent and confirmed note in the wallet (depth of 1) - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 1); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); @@ -271,22 +271,22 @@ TEST(WalletTests, FindUnspentSproutNotes) { EXPECT_TRUE(wallet.IsSproutSpent(nullifier)); // The note has been spent. By default, GetFilteredNotes() ignores spent notes. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // Let's include spent notes to retrieve it. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 0, INT_MAX, false); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // The spent note has two confirmations. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2, INT_MAX, false); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // It does not have 3 confirmations. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 3, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 3, INT_MAX, false); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); @@ -329,22 +329,22 @@ TEST(WalletTests, FindUnspentSproutNotes) { wallet.AddToWallet(wtx3, true, NULL); // We now have an unspent note which has one confirmation, in addition to our spent note. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 1); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // Let's return the spent note too. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 1, INT_MAX, false); EXPECT_EQ(2, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // Increasing number of confirmations will exclude our new unspent note. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2, INT_MAX, false); EXPECT_EQ(1, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); // If we also ignore spent notes at this depth, we won't find any notes. - wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, true); + wallet.GetFilteredNotes(sproutEntries, saplingEntries, std::nullopt, 2, INT_MAX, true); EXPECT_EQ(0, sproutEntries.size()); sproutEntries.clear(); saplingEntries.clear(); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d6c3130b0..0ad8e24b1 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -400,9 +400,9 @@ UniValue importwallet_impl(const UniValue& params, bool fImportZKeys) // Only include hdKeypath and seedFpStr if we have both std::optional hdKeypath = (vstr.size() > 3) ? std::optional(vstr[2]) : std::nullopt; std::optional seedFpStr = (vstr.size() > 3) ? std::optional(vstr[3]) : std::nullopt; - if (IsValidSpendingKey(spendingkey)) { + if (spendingkey.has_value()) { auto addResult = std::visit( - AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus(), nTime, hdKeypath, seedFpStr, true), spendingkey); + AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus(), nTime, hdKeypath, seedFpStr, true), spendingkey.value()); if (addResult == KeyAlreadyExists){ LogPrint("zrpc", "Skipping import of zaddr (key already present)\n"); } else if (addResult == KeyNotAdded) { @@ -751,17 +751,17 @@ UniValue z_importkey(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); string strSecret = params[0].get_str(); auto spendingkey = keyIO.DecodeSpendingKey(strSecret); - if (!IsValidSpendingKey(spendingkey)) { + if (!spendingkey.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } - auto addrInfo = std::visit(libzcash::AddressInfoFromSpendingKey{}, spendingkey); + auto addrInfo = std::visit(libzcash::AddressInfoFromSpendingKey{}, spendingkey.value()); UniValue result(UniValue::VOBJ); result.pushKV("type", addrInfo.first); result.pushKV("address", keyIO.EncodePaymentAddress(addrInfo.second)); // Sapling support - auto addResult = std::visit(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey); + auto addResult = std::visit(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey.value()); if (addResult == KeyAlreadyExists && fIgnoreExistingKey) { return result; } @@ -846,17 +846,17 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); string strVKey = params[0].get_str(); auto viewingkey = keyIO.DecodeViewingKey(strVKey); - if (!IsValidViewingKey(viewingkey)) { + if (!viewingkey.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); } - auto addrInfo = std::visit(libzcash::AddressInfoFromViewingKey{}, viewingkey); + auto addrInfo = std::visit(libzcash::AddressInfoFromViewingKey{}, viewingkey.value()); UniValue result(UniValue::VOBJ); const string strAddress = keyIO.EncodePaymentAddress(addrInfo.second); result.pushKV("type", addrInfo.first); result.pushKV("address", strAddress); - auto addResult = std::visit(AddViewingKeyToWallet(pwalletMain), viewingkey); + auto addResult = std::visit(AddViewingKeyToWallet(pwalletMain), viewingkey.value()); if (addResult == SpendingKeyExists) { throw JSONRPCError( RPC_WALLET_ERROR, @@ -905,12 +905,12 @@ UniValue z_exportkey(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); auto address = keyIO.DecodePaymentAddress(strAddress); - if (!IsValidPaymentAddress(address)) { + if (!address.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); } // Sapling support - auto sk = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address); + auto sk = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address.value()); if (!sk) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr"); } @@ -944,11 +944,11 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); auto address = keyIO.DecodePaymentAddress(strAddress); - if (!IsValidPaymentAddress(address)) { + if (!address.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); } - auto vk = std::visit(GetViewingKeyForPaymentAddress(pwalletMain), address); + auto vk = std::visit(GetViewingKeyForPaymentAddress(pwalletMain), address.value()); if (vk) { return keyIO.EncodeViewingKey(vk.value()); } else { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a23336794..83c63e554 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -18,6 +18,7 @@ #include "timedata.h" #include "transaction_builder.h" #include "util.h" +#include "util/match.h" #include "utilmoneystr.h" #include "wallet.h" #include "walletdb.h" @@ -2266,16 +2267,16 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) } string address = o.get_str(); auto zaddr = keyIO.DecodePaymentAddress(address); - if (!IsValidPaymentAddress(zaddr)) { + if (!zaddr.has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid zaddr: ") + address); } - auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); + auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr.value()); if (!fIncludeWatchonly && !hasSpendingKey) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, spending key for address does not belong to wallet: ") + address); } // We want to return unspent notes corresponding to any receiver within a // Unified Address. - for (const auto ra : std::visit(GetRawAddresses(), zaddr)) { + for (const auto ra : std::visit(GetRawAddresses(), zaddr.value())) { zaddrs.insert(ra); } @@ -2597,13 +2598,13 @@ UniValue zc_raw_receive(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); auto spendingkey = keyIO.DecodeSpendingKey(params[0].get_str()); - if (!IsValidSpendingKey(spendingkey)) { + if (!spendingkey.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } - if (std::get_if(&spendingkey) == nullptr) { + if (std::get_if(&spendingkey.value()) == nullptr) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only works with Sprout spending keys"); } - SproutSpendingKey k = std::get(spendingkey); + SproutSpendingKey k = std::get(spendingkey.value()); uint256 epk; unsigned char nonce; @@ -2723,13 +2724,13 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) KeyIO keyIO(Params()); for (const string& name_ : inputs.getKeys()) { auto spendingkey = keyIO.DecodeSpendingKey(inputs[name_].get_str()); - if (!IsValidSpendingKey(spendingkey)) { + if (!spendingkey.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } - if (std::get_if(&spendingkey) == nullptr) { + if (std::get_if(&spendingkey.value()) == nullptr) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only works with Sprout spending keys"); } - SproutSpendingKey k = std::get(spendingkey); + SproutSpendingKey k = std::get(spendingkey.value()); keys.push_back(k); @@ -2770,11 +2771,13 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) } for (const string& name_ : outputs.getKeys()) { - auto addrTo = keyIO.DecodePaymentAddress(name_); - if (!IsValidPaymentAddress(addrTo)) { + auto addrToDecoded = keyIO.DecodePaymentAddress(name_); + if (!addrToDecoded.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address."); } - if (std::get_if(&addrTo) == nullptr) { + + libzcash::PaymentAddress addrTo(addrToDecoded.value()); + if (!std::holds_alternative(addrTo)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only works with Sprout payment addresses"); } CAmount nAmount = AmountFromValue(outputs[name_]); @@ -3123,70 +3126,77 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) auto fromaddress = params[0].get_str(); KeyIO keyIO(Params()); - auto zaddr = keyIO.DecodePaymentAddress(fromaddress); - if (!IsValidPaymentAddress(zaddr)) { + auto decoded = keyIO.DecodePaymentAddress(fromaddress); + if (!decoded.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr."); } // Visitor to support Sprout and Sapling addrs - if (!std::visit(PaymentAddressBelongsToWallet(pwalletMain), zaddr)) { + if (!std::visit(PaymentAddressBelongsToWallet(pwalletMain), decoded.value())) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); } UniValue result(UniValue::VARR); std::vector sproutEntries; std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress, nMinDepth, false, false); + pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, decoded, nMinDepth, false, false); std::set> nullifierSet; - auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); + auto hasSpendingKey = std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), decoded.value()); if (hasSpendingKey) { - nullifierSet = pwalletMain->GetNullifiersForAddresses(std::visit(GetRawAddresses(), zaddr)); + nullifierSet = pwalletMain->GetNullifiersForAddresses(std::visit(GetRawAddresses(), decoded.value())); } - if (std::get_if(&zaddr) != nullptr) { - for (SproutNoteEntry & entry : sproutEntries) { - UniValue obj(UniValue::VOBJ); - obj.pushKV("txid", entry.jsop.hash.ToString()); - obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); - obj.pushKV("amountZat", CAmount(entry.note.value())); - std::string data(entry.memo.begin(), entry.memo.end()); - obj.pushKV("memo", HexStr(data)); - obj.pushKV("jsindex", entry.jsop.js); - obj.pushKV("jsoutindex", entry.jsop.n); - obj.pushKV("confirmations", entry.confirmations); + std::visit(match { + [&](libzcash::SproutPaymentAddress addr) { + for (SproutNoteEntry & entry : sproutEntries) { + UniValue obj(UniValue::VOBJ); + obj.pushKV("txid", entry.jsop.hash.ToString()); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); + obj.pushKV("amountZat", CAmount(entry.note.value())); + std::string data(entry.memo.begin(), entry.memo.end()); + obj.pushKV("memo", HexStr(data)); + obj.pushKV("jsindex", entry.jsop.js); + obj.pushKV("jsoutindex", entry.jsop.n); + obj.pushKV("confirmations", entry.confirmations); - txblock BlockData(entry.jsop.hash); - obj.pushKV("blockheight", BlockData.height); - obj.pushKV("blockindex", BlockData.index); - obj.pushKV("blocktime", BlockData.time); + txblock BlockData(entry.jsop.hash); + obj.pushKV("blockheight", BlockData.height); + obj.pushKV("blockindex", BlockData.index); + obj.pushKV("blocktime", BlockData.time); - if (hasSpendingKey) { - obj.pushKV("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)); + if (hasSpendingKey) { + obj.pushKV("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)); + } + result.push_back(obj); } - result.push_back(obj); - } - } else if (std::get_if(&zaddr) != nullptr) { - for (SaplingNoteEntry & entry : saplingEntries) { - UniValue obj(UniValue::VOBJ); - obj.pushKV("txid", entry.op.hash.ToString()); - obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); - obj.pushKV("amountZat", CAmount(entry.note.value())); - obj.pushKV("memo", HexStr(entry.memo)); - obj.pushKV("outindex", (int)entry.op.n); - obj.pushKV("confirmations", entry.confirmations); + }, + [&](libzcash::SaplingPaymentAddress addr) { + for (SaplingNoteEntry & entry : saplingEntries) { + UniValue obj(UniValue::VOBJ); + obj.pushKV("txid", entry.op.hash.ToString()); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); + obj.pushKV("amountZat", CAmount(entry.note.value())); + obj.pushKV("memo", HexStr(entry.memo)); + obj.pushKV("outindex", (int)entry.op.n); + obj.pushKV("confirmations", entry.confirmations); - txblock BlockData(entry.op.hash); - obj.pushKV("blockheight", BlockData.height); - obj.pushKV("blockindex", BlockData.index); - obj.pushKV("blocktime", BlockData.time); + txblock BlockData(entry.op.hash); + obj.pushKV("blockheight", BlockData.height); + obj.pushKV("blockindex", BlockData.index); + obj.pushKV("blocktime", BlockData.time); - if (hasSpendingKey) { - obj.pushKV("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); + if (hasSpendingKey) { + obj.pushKV("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); + } + result.push_back(obj); } - result.push_back(obj); + }, + [&](libzcash::UnifiedAddress) { + // TODO UNIFIED } - } + }, decoded.value()); + return result; } @@ -3234,10 +3244,10 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) auto pa = keyIO.DecodePaymentAddress(fromaddress); fromTaddr = IsValidDestination(taddr); if (!fromTaddr) { - if (!IsValidPaymentAddress(pa)) { + if (!pa.has_value()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); } - if (!std::visit(PaymentAddressBelongsToWallet(pwalletMain), pa)) { + if (!std::visit(PaymentAddressBelongsToWallet(pwalletMain), pa.value())) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, spending key or viewing key not found."); } } @@ -3247,7 +3257,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) nBalance = getBalanceTaddr(fromaddress, nMinDepth, false); } else { // TODO: Return an error if a UA is provided (once we support UAs). - auto zaddr = std::visit(RecipientForPaymentAddress(), pa).value(); + auto zaddr = std::visit(RecipientForPaymentAddress(), pa.value()).value(); nBalance = getBalanceZaddr(zaddr, nMinDepth, INT_MAX, false); } @@ -3718,19 +3728,20 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) CTxDestination taddr = keyIO.DecodeDestination(fromaddress); fromTaddr = IsValidDestination(taddr); if (!fromTaddr) { - auto res = keyIO.DecodePaymentAddress(fromaddress); - if (!IsValidPaymentAddress(res)) { + auto decoded = keyIO.DecodePaymentAddress(fromaddress); + if (!decoded.has_value()) { // invalid throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); } // Check that we have the spending key - if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), res)) { + libzcash::PaymentAddress addr(decoded.value()); + if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), addr)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); } // Remember whether this is a Sprout or Sapling address - fromSapling = std::get_if(&res) != nullptr; + fromSapling = std::holds_alternative(addr); } } // This logic will need to be updated if we add a new shielded pool @@ -3770,13 +3781,14 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) bool isZaddr = false; CTxDestination taddr = keyIO.DecodeDestination(address); if (!IsValidDestination(taddr)) { - auto res = keyIO.DecodePaymentAddress(address); - if (IsValidPaymentAddress(res)) { + auto decoded = keyIO.DecodePaymentAddress(address); + if (decoded.has_value()) { + libzcash::PaymentAddress addr(decoded.value()); isZaddr = true; - bool toSapling = std::get_if(&res) != nullptr; - bool toSprout = !toSapling; - noSproutAddrs = noSproutAddrs && toSapling; + bool toSapling = std::holds_alternative(addr); + bool toSprout = std::holds_alternative(addr); + noSproutAddrs = !toSprout && noSproutAddrs && toSapling; containsSproutOutput |= toSprout; containsSaplingOutput |= toSapling; @@ -3872,16 +3884,23 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) size_t txsize = 0; for (int i = 0; i < zaddrRecipients.size(); i++) { auto address = zaddrRecipients[i].address; - auto res = keyIO.DecodePaymentAddress(address); - bool toSapling = std::get_if(&res) != nullptr; - if (toSapling) { - mtx.vShieldedOutput.push_back(OutputDescription()); - } else { - JSDescription jsdesc; - if (mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)) { - jsdesc.proof = GrothProof(); - } - mtx.vJoinSplit.push_back(jsdesc); + auto decoded = keyIO.DecodePaymentAddress(address); + if (decoded.has_value()) { + std::visit(match { + [&](libzcash::SaplingPaymentAddress addr) { + mtx.vShieldedOutput.push_back(OutputDescription()); + }, + [&](libzcash::SproutPaymentAddress addr) { + JSDescription jsdesc; + if (mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)) { + jsdesc.proof = GrothProof(); + } + mtx.vJoinSplit.push_back(jsdesc); + }, + [&](libzcash::UnifiedAddress) { + // TODO UNIFIED + } + }, decoded.value()); } } CTransaction tx(mtx); @@ -4174,10 +4193,11 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) if (canopyActive) { auto decodeAddr = keyIO.DecodePaymentAddress(destaddress); - bool isToSproutZaddr = (std::get_if(&decodeAddr) != nullptr); - - if (isToSproutZaddr) { - throw JSONRPCError(RPC_VERIFY_REJECTED, "Sprout shielding is not supported after Canopy activation"); + if (decodeAddr.has_value()) { + libzcash::PaymentAddress addr(decodeAddr.value()); + if (std::holds_alternative(addr)) { + throw JSONRPCError(RPC_VERIFY_REJECTED, "Sprout shielding is not supported after Canopy activation"); + } } } @@ -4427,12 +4447,12 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) isFromNonSprout = true; } else { auto zaddr = keyIO.DecodePaymentAddress(address); - if (IsValidPaymentAddress(zaddr)) { + if (zaddr.has_value()) { // We want to merge notes corresponding to any receiver within a // Unified Address. - for (const auto ra : std::visit(GetRawAddresses(), zaddr)) { + for (const libzcash::RawAddress& ra : std::visit(GetRawAddresses(), zaddr.value())) { zaddrs.insert(ra); - if (std::get_if(&ra) != nullptr) { + if (std::holds_alternative(ra)) { isFromNonSprout = true; } } @@ -4465,17 +4485,23 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) bool isToSaplingZaddr = false; CTxDestination taddr = keyIO.DecodeDestination(destaddress); if (!IsValidDestination(taddr)) { - auto decodeAddr = keyIO.DecodePaymentAddress(destaddress); - if (IsValidPaymentAddress(decodeAddr)) { - if (std::get_if(&decodeAddr) != nullptr) { - 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"); + auto zaddr = keyIO.DecodePaymentAddress(destaddress); + if (zaddr.has_value()) { + std::visit(match { + [&](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"); + } + }, + [&](libzcash::SproutPaymentAddress addr) { + isToSproutZaddr = true; + }, + [&](libzcash::UnifiedAddress) { + // TODO UNIFIED } - } else { - isToSproutZaddr = true; - } + }, zaddr.value()); } else { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 2d411842f..5947c444a 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -602,15 +602,16 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) BOOST_CHECK(addrs.size()==1); // check that we have the spending key for the address - auto address = keyIO.DecodePaymentAddress(testAddr); - BOOST_CHECK(IsValidPaymentAddress(address)); - BOOST_ASSERT(std::get_if(&address) != nullptr); - auto addr = std::get(address); - BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(addr)); + auto decoded = keyIO.DecodePaymentAddress(testAddr); + BOOST_CHECK(decoded.has_value()); + libzcash::PaymentAddress address(decoded.value()); + BOOST_ASSERT(std::holds_alternative(address)); + auto sprout_addr = std::get(address); + BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(sprout_addr)); // Verify the spending key is the same as the test data libzcash::SproutSpendingKey k; - BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, k)); + BOOST_CHECK(pwalletMain->GetSproutSpendingKey(sprout_addr, k)); BOOST_CHECK_EQUAL(testKey, keyIO.EncodeSpendingKey(k)); } @@ -776,10 +777,9 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) // Check if address is of given type and spendable from our wallet. template -void CheckHaveAddr(const libzcash::PaymentAddress& addr) { - - BOOST_CHECK(IsValidPaymentAddress(addr)); - auto addr_of_type = std::get_if(&addr); +void CheckHaveAddr(const std::optional& addr) { + BOOST_CHECK(addr.has_value()); + auto addr_of_type = std::get_if(&(addr.value())); BOOST_ASSERT(addr_of_type != nullptr); HaveSpendingKeyForPaymentAddress test(pwalletMain); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index adc171603..c6fd173fd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4927,8 +4927,8 @@ bool CWallet::ParameterInteraction(const CChainParams& params) // Check Sapling migration address if set and is a valid Sapling address if (mapArgs.count("-migrationdestaddress")) { std::string migrationDestAddress = mapArgs["-migrationdestaddress"]; - libzcash::PaymentAddress address = keyIO.DecodePaymentAddress(migrationDestAddress); - if (std::get_if(&address) == nullptr) { + std::optional address = keyIO.DecodePaymentAddress(migrationDestAddress); + if (!address.has_value() || std::get_if(&address.value()) == nullptr) { return UIError(_("-migrationdestaddress must be a valid Sapling address.")); } } @@ -5032,17 +5032,15 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) void CWallet::GetFilteredNotes( std::vector& sproutEntries, std::vector& saplingEntries, - std::string address, + std::optional address, int minDepth, bool ignoreSpent, bool requireSpendingKey) { std::set filterAddresses; - KeyIO keyIO(Params()); - if (address.length() > 0) { - auto addr = keyIO.DecodePaymentAddress(address); - for (const auto ra : std::visit(GetRawAddresses(), addr)) { + if (address.has_value()) { + for (const auto ra : std::visit(GetRawAddresses(), address.value())) { filterAddresses.insert(ra); } } @@ -5210,11 +5208,6 @@ bool PaymentAddressBelongsToWallet::operator()(const libzcash::UnifiedAddress &u return false; } -bool PaymentAddressBelongsToWallet::operator()(const libzcash::InvalidEncoding& no) const -{ - return false; -} - /// PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const @@ -5253,12 +5246,6 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Unif return AddressNotFound; } -PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const -{ - return AddressNotFound; -} - - /// std::optional GetViewingKeyForPaymentAddress::operator()( @@ -5297,13 +5284,6 @@ std::optional GetViewingKeyForPaymentAddress::operator()( return libzcash::ViewingKey(); } -std::optional GetViewingKeyForPaymentAddress::operator()( - const libzcash::InvalidEncoding& no) const -{ - // Defaults to InvalidEncoding - return libzcash::ViewingKey(); -} - bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const { return m_wallet->HaveSproutSpendingKey(zaddr); @@ -5325,11 +5305,6 @@ bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::UnifiedAddress return false; } -bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const -{ - return false; -} - std::optional GetSpendingKeyForPaymentAddress::operator()( const libzcash::SproutPaymentAddress &zaddr) const { @@ -5359,13 +5334,6 @@ std::optional GetSpendingKeyForPaymentAddress::operator() return libzcash::SpendingKey(); } -std::optional GetSpendingKeyForPaymentAddress::operator()( - const libzcash::InvalidEncoding& no) const -{ - // Defaults to InvalidEncoding - return libzcash::SpendingKey(); -} - KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SproutViewingKey &vkey) const { auto addr = vkey.address(); @@ -5392,10 +5360,6 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFu } } -KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); -} - KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const { auto addr = sk.address(); KeyIO keyIO(Params()); @@ -5447,7 +5411,3 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS } } } - -KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); -} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7045a1ea2..57fda6366 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1304,10 +1304,10 @@ public: /* Set the current encrypted HD seed, without saving it to disk (used by LoadWallet) */ bool LoadCryptedHDSeed(const uint256& seedFp, const std::vector& seed); - /* Find notes filtered by payment address, min depth, ability to spend */ + /* Find notes filtered by (optional) payment address, min depth, ability to spend */ void GetFilteredNotes(std::vector& sproutEntries, std::vector& saplingEntries, - std::string address, + std::optional address, int minDepth=1, bool ignoreSpent=true, bool requireSpendingKey=true); @@ -1372,7 +1372,6 @@ public: bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::UnifiedAddress &uaddr) const; - bool operator()(const libzcash::InvalidEncoding& no) const; }; class GetViewingKeyForPaymentAddress @@ -1385,7 +1384,6 @@ public: std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; - std::optional operator()(const libzcash::InvalidEncoding& no) const; }; class HaveSpendingKeyForPaymentAddress @@ -1398,7 +1396,6 @@ public: bool operator()(const libzcash::SproutPaymentAddress &zaddr) const; bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const; bool operator()(const libzcash::UnifiedAddress &uaddr) const; - bool operator()(const libzcash::InvalidEncoding& no) const; }; class GetSpendingKeyForPaymentAddress @@ -1411,7 +1408,6 @@ public: std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; - std::optional operator()(const libzcash::InvalidEncoding& no) const; }; enum PaymentAddressSource { @@ -1433,7 +1429,6 @@ public: PaymentAddressSource operator()(const libzcash::SproutPaymentAddress &zaddr) const; PaymentAddressSource operator()(const libzcash::SaplingPaymentAddress &zaddr) const; PaymentAddressSource operator()(const libzcash::UnifiedAddress &uaddr) const; - PaymentAddressSource operator()(const libzcash::InvalidEncoding& no) const; }; enum KeyAddResult { @@ -1452,7 +1447,6 @@ public: KeyAddResult operator()(const libzcash::SproutViewingKey &sk) const; KeyAddResult operator()(const libzcash::SaplingExtendedFullViewingKey &sk) const; - KeyAddResult operator()(const libzcash::InvalidEncoding& no) const; }; class AddSpendingKeyToWallet @@ -1479,7 +1473,6 @@ public: KeyAddResult operator()(const libzcash::SproutSpendingKey &sk) const; KeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const; - KeyAddResult operator()(const libzcash::InvalidEncoding& no) const; }; diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index d827a395a..dbd8e7c20 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -41,9 +41,6 @@ std::pair AddressInfoFromSpendingKey::operator()(co std::pair AddressInfoFromSpendingKey::operator()(const SaplingExtendedSpendingKey &sk) const { return std::make_pair("sapling", sk.DefaultAddress()); } -std::pair AddressInfoFromSpendingKey::operator()(const InvalidEncoding&) const { - throw std::invalid_argument("Cannot derive default address from invalid spending key"); -} std::pair AddressInfoFromViewingKey::operator()(const SproutViewingKey &sk) const { return std::make_pair("sprout", sk.address()); @@ -51,23 +48,8 @@ std::pair AddressInfoFromViewingKey::operator()(con std::pair AddressInfoFromViewingKey::operator()(const SaplingExtendedFullViewingKey &sk) const { return std::make_pair("sapling", sk.DefaultAddress()); } -std::pair AddressInfoFromViewingKey::operator()(const InvalidEncoding&) const { - throw std::invalid_argument("Cannot derive default address from invalid viewing key"); -} -} - -bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr) { - return !std::holds_alternative(zaddr); -} - -bool IsValidViewingKey(const libzcash::ViewingKey& vk) { - return !std::holds_alternative(vk); -} - -bool IsValidSpendingKey(const libzcash::SpendingKey& zkey) { - return !std::holds_alternative(zkey); -} +} // namespace libzcash uint32_t TypecodeForReceiver::operator()( const libzcash::SaplingPaymentAddress &zaddr) const @@ -117,12 +99,6 @@ std::optional ReceiverToRawAddress::operator()( return std::nullopt; } -std::optional RecipientForPaymentAddress::operator()( - const libzcash::InvalidEncoding& no) const -{ - return std::nullopt; -} - std::optional RecipientForPaymentAddress::operator()( const libzcash::SproutPaymentAddress &zaddr) const { @@ -146,12 +122,6 @@ std::optional RecipientForPaymentAddress::operator()( return std::nullopt; } -std::set GetRawAddresses::operator()( - const libzcash::InvalidEncoding& no) const -{ - return {}; -} - std::set GetRawAddresses::operator()( const libzcash::SproutPaymentAddress &zaddr) const { diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 38d416ee2..8eec84ee5 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -24,12 +24,6 @@ public: /** Protocol addresses that can receive funds in a transaction. */ typedef std::variant RawAddress; -class InvalidEncoding { -public: - friend bool operator==(const InvalidEncoding &a, const InvalidEncoding &b) { return true; } - friend bool operator<(const InvalidEncoding &a, const InvalidEncoding &b) { return true; } -}; - class UnknownReceiver { public: uint32_t typecode; @@ -137,38 +131,32 @@ public: /** Addresses that can appear in a string encoding. */ typedef std::variant< - InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress, UnifiedAddress> PaymentAddress; -typedef std::variant ViewingKey; -typedef std::variant SpendingKey; +/** Viewing keys that can have a string encoding. */ +typedef std::variant< + SproutViewingKey, + SaplingExtendedFullViewingKey> ViewingKey; +/** Spending keys that can have a string encoding. */ +typedef std::variant< + SproutSpendingKey, + SaplingExtendedSpendingKey> SpendingKey; class AddressInfoFromSpendingKey { public: std::pair operator()(const SproutSpendingKey&) const; std::pair operator()(const struct SaplingExtendedSpendingKey&) const; - std::pair operator()(const InvalidEncoding&) const; }; class AddressInfoFromViewingKey { public: std::pair operator()(const SproutViewingKey&) const; std::pair operator()(const struct SaplingExtendedFullViewingKey&) const; - std::pair operator()(const InvalidEncoding&) const; }; } -/** Check whether a PaymentAddress is not an InvalidEncoding. */ -bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr); - -/** Check whether a ViewingKey is not an InvalidEncoding. */ -bool IsValidViewingKey(const libzcash::ViewingKey& vk); - -/** Check whether a SpendingKey is not an InvalidEncoding. */ -bool IsValidSpendingKey(const libzcash::SpendingKey& zkey); - /** * Gets the typecode for the given UA receiver. */ @@ -202,7 +190,6 @@ class RecipientForPaymentAddress { public: RecipientForPaymentAddress() {} - std::optional operator()(const libzcash::InvalidEncoding& no) const; std::optional operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::optional operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::optional operator()(const libzcash::UnifiedAddress &uaddr) const; @@ -215,7 +202,6 @@ class GetRawAddresses { public: GetRawAddresses() {} - std::set operator()(const libzcash::InvalidEncoding& no) const; std::set operator()(const libzcash::SproutPaymentAddress &zaddr) const; std::set operator()(const libzcash::SaplingPaymentAddress &zaddr) const; std::set operator()(const libzcash::UnifiedAddress &uaddr) const;