Remove the `InvalidEncoding` type from key & address variants.

The presence of this variant results in a situation where more
of the code than necessary needs to be aware of and handle
decoding failures. This change moves all handling of decoding
failures to the point of decoding.
This commit is contained in:
Kris Nuttycombe 2021-12-16 16:28:43 -07:00
parent 0403799be2
commit b305ad2892
23 changed files with 286 additions and 343 deletions

View File

@ -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<libzcash::SaplingPaymentAddress>(zaddr.value()))) {
throw std::runtime_error("Funding stream address was not a valid transparent or Sapling address.");
}
addresses.push_back(std::get<libzcash::SaplingPaymentAddress>(zaddr));
addresses.push_back(std::get<libzcash::SaplingPaymentAddress>(zaddr.value()));
}
}

View File

@ -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<libzcash::SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = std::get<libzcash::SaplingExtendedSpendingKey>(spendingkey2);
EXPECT_EQ(sk, sk2);
auto sk2 = std::get_if<libzcash::SaplingExtendedSpendingKey>(&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<libzcash::SaplingExtendedFullViewingKey>(&viewingkey2) != nullptr);
auto extfvk2 = std::get<libzcash::SaplingExtendedFullViewingKey>(viewingkey2);
EXPECT_EQ(extfvk, extfvk2);
auto extfvk2 = std::get_if<libzcash::SaplingExtendedFullViewingKey>(&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<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr);
auto addr2 = std::get<libzcash::SaplingPaymentAddress>(paymentaddr2);
EXPECT_EQ(addr, addr2);
auto addr2 = std::get_if<libzcash::SaplingPaymentAddress>(&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<libzcash::UnifiedAddress>(decoded));
EXPECT_EQ(std::get<libzcash::UnifiedAddress>(decoded), ua);
EXPECT_TRUE(decoded.has_value());
auto ua_ptr = std::get_if<libzcash::UnifiedAddress>(&decoded.value());
EXPECT_NE(ua_ptr, nullptr);
EXPECT_EQ(*ua_ptr, ua);
auto encoded = keyIO.EncodePaymentAddress(ua);
EXPECT_EQ(encoded, expected);

View File

@ -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=<addr>: '%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) {

View File

@ -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<typename T1, typename T2, typename T3>
T1 DecodeAny(
std::optional<T1> DecodeAny(
const KeyConstants& keyConstants,
const std::string& str,
std::pair<KeyConstants::Base58Type, size_t> 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<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
libzcash::PaymentAddress KeyIO::DecodePaymentAddress(const std::string& str)
std::optional<libzcash::PaymentAddress> 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<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& str)
{
return DecodeAny<libzcash::ViewingKey,
libzcash::SproutViewingKey,
@ -500,7 +494,7 @@ std::string KeyIO::EncodeSpendingKey(const libzcash::SpendingKey& zkey)
return std::visit(SpendingKeyEncoder(keyConstants), zkey);
}
libzcash::SpendingKey KeyIO::DecodeSpendingKey(const std::string& str)
std::optional<libzcash::SpendingKey> KeyIO::DecodeSpendingKey(const std::string& str)
{
return DecodeAny<libzcash::SpendingKey,

View File

@ -37,14 +37,14 @@ public:
bool IsValidDestinationString(const std::string& str);
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr);
libzcash::PaymentAddress DecodePaymentAddress(const std::string& str);
std::optional<libzcash::PaymentAddress> 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<libzcash::ViewingKey> DecodeViewingKey(const std::string& str);
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey);
libzcash::SpendingKey DecodeSpendingKey(const std::string& str);
std::optional<libzcash::SpendingKey> DecodeSpendingKey(const std::string& str);
};
#endif // BITCOIN_KEY_IO_H

View File

@ -117,9 +117,7 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams,
}
bool IsShieldedMinerAddress(const MinerAddress& minerAddr) {
return !(
std::holds_alternative<InvalidMinerAddress>(minerAddr) ||
std::holds_alternative<boost::shared_ptr<CReserveScript>>(minerAddr));
return !std::holds_alternative<boost::shared_ptr<CReserveScript>>(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();
}
}
}
}

View File

@ -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<CReserveScript>> MinerAddress;
@ -39,16 +32,13 @@ class ExtractMinerAddress
public:
ExtractMinerAddress() {}
MinerAddress operator()(const libzcash::InvalidEncoding &invalid) const {
return InvalidMinerAddress();
std::optional<MinerAddress> 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<MinerAddress> operator()(const libzcash::SaplingPaymentAddress &addr) const {
return addr;
}
MinerAddress operator()(const libzcash::UnifiedAddress &addr) const {
std::optional<MinerAddress> 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<CReserveScript> &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;
}

View File

@ -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;

View File

@ -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<SproutSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = std::get<SproutSpendingKey>(spendingkey2);
BOOST_CHECK(spendingkey2.has_value());
BOOST_ASSERT(std::get_if<SproutSpendingKey>(&spendingkey2.value()) != nullptr);
auto sk2 = std::get<SproutSpendingKey>(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<SproutPaymentAddress>(&paymentaddr2) != nullptr);
auto addr2 = std::get<SproutPaymentAddress>(paymentaddr2);
BOOST_ASSERT(std::get_if<SproutPaymentAddress>(&paymentaddr2.value()) != nullptr);
auto addr2 = std::get<SproutPaymentAddress>(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<SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = std::get<SaplingExtendedSpendingKey>(spendingkey2);
BOOST_ASSERT(std::get_if<SaplingExtendedSpendingKey>(&spendingkey2.value()) != nullptr);
auto sk2 = std::get<SaplingExtendedSpendingKey>(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<SaplingPaymentAddress>(&paymentaddr2) != nullptr);
auto addr2 = std::get<SaplingPaymentAddress>(paymentaddr2);
BOOST_ASSERT(std::get_if<SaplingPaymentAddress>(&paymentaddr2.value()) != nullptr);
auto addr2 = std::get<SaplingPaymentAddress>(paymentaddr2.value());
BOOST_CHECK(addr == addr2);
}
}

24
src/util/match.h Normal file
View File

@ -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<class... Ts> struct match : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> match(Ts...) -> match<Ts...>;
#endif // ZCASH_UTIL_MATCH_H

View File

@ -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");
}

View File

@ -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<libzcash::SaplingPaymentAddress>(&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<libzcash::SaplingPaymentAddress>(&address.value());
assert(saplingAddress != nullptr); // This is also checked in init.cpp
return *saplingAddress;
}
// Derive the address for Sapling account 0

View File

@ -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<libzcash::SaplingPaymentAddress>(&addr) != nullptr);
auto to = std::get<libzcash::SaplingPaymentAddress>(addr);
assert(addr.has_value() && std::get_if<libzcash::SaplingPaymentAddress>(&addr.value()) != nullptr);
auto to = std::get<libzcash::SaplingPaymentAddress>(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<libzcash::SproutPaymentAddress>(pa), value);
if (hexMemo.size() > 0) {
jso.memo = get_memo_from_hex_string(hexMemo);
std::optional<PaymentAddress> pa = keyIO.DecodePaymentAddress(address);
if (pa.has_value()) {
JSOutput jso = JSOutput(std::get<libzcash::SproutPaymentAddress>(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<libzcash::SproutPaymentAddress>(pa), value);
if (hexMemo.size() > 0) {
jso.memo = get_memo_from_hex_string(hexMemo);
std::optional<PaymentAddress> pa = keyIO.DecodePaymentAddress(address);
if (pa.has_value()) {
// If we are here, we know we have no Sapling outputs.
JSOutput jso = JSOutput(std::get<libzcash::SproutPaymentAddress>(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<SproutNoteEntry> sproutEntries;
std::vector<SaplingNoteEntry> 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.

View File

@ -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;

View File

@ -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;
};

View File

@ -206,11 +206,11 @@ TEST(WalletTests, FindUnspentSproutNotes) {
// We currently have an unspent and unconfirmed note in the wallet (depth of -1)
std::vector<SproutNoteEntry> sproutEntries;
std::vector<SaplingNoteEntry> 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();

View File

@ -400,9 +400,9 @@ UniValue importwallet_impl(const UniValue& params, bool fImportZKeys)
// Only include hdKeypath and seedFpStr if we have both
std::optional<std::string> hdKeypath = (vstr.size() > 3) ? std::optional<std::string>(vstr[2]) : std::nullopt;
std::optional<std::string> seedFpStr = (vstr.size() > 3) ? std::optional<std::string>(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 {

View File

@ -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<libzcash::SproutSpendingKey>(&spendingkey) == nullptr) {
if (std::get_if<libzcash::SproutSpendingKey>(&spendingkey.value()) == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only works with Sprout spending keys");
}
SproutSpendingKey k = std::get<libzcash::SproutSpendingKey>(spendingkey);
SproutSpendingKey k = std::get<libzcash::SproutSpendingKey>(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<libzcash::SproutSpendingKey>(&spendingkey) == nullptr) {
if (std::get_if<libzcash::SproutSpendingKey>(&spendingkey.value()) == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only works with Sprout spending keys");
}
SproutSpendingKey k = std::get<libzcash::SproutSpendingKey>(spendingkey);
SproutSpendingKey k = std::get<libzcash::SproutSpendingKey>(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<libzcash::SproutPaymentAddress>(&addrTo) == nullptr) {
libzcash::PaymentAddress addrTo(addrToDecoded.value());
if (!std::holds_alternative<libzcash::SproutPaymentAddress>(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<SproutNoteEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries;
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress, nMinDepth, false, false);
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, decoded, nMinDepth, false, false);
std::set<std::pair<libzcash::RawAddress, uint256>> 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<libzcash::SproutPaymentAddress>(&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<libzcash::SaplingPaymentAddress>(&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<libzcash::SaplingPaymentAddress>(&res) != nullptr;
fromSapling = std::holds_alternative<libzcash::SaplingPaymentAddress>(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<libzcash::SaplingPaymentAddress>(&res) != nullptr;
bool toSprout = !toSapling;
noSproutAddrs = noSproutAddrs && toSapling;
bool toSapling = std::holds_alternative<libzcash::SaplingPaymentAddress>(addr);
bool toSprout = std::holds_alternative<libzcash::SproutPaymentAddress>(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<libzcash::SaplingPaymentAddress>(&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<libzcash::SproutPaymentAddress>(&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<libzcash::SproutPaymentAddress>(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<libzcash::SaplingPaymentAddress>(&ra) != nullptr) {
if (std::holds_alternative<libzcash::SaplingPaymentAddress>(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<libzcash::SaplingPaymentAddress>(&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 );
}

View File

@ -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<libzcash::SproutPaymentAddress>(&address) != nullptr);
auto addr = std::get<libzcash::SproutPaymentAddress>(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<libzcash::SproutPaymentAddress>(address));
auto sprout_addr = std::get<libzcash::SproutPaymentAddress>(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 <typename ADDR_TYPE>
void CheckHaveAddr(const libzcash::PaymentAddress& addr) {
BOOST_CHECK(IsValidPaymentAddress(addr));
auto addr_of_type = std::get_if<ADDR_TYPE>(&addr);
void CheckHaveAddr(const std::optional<libzcash::PaymentAddress>& addr) {
BOOST_CHECK(addr.has_value());
auto addr_of_type = std::get_if<ADDR_TYPE>(&(addr.value()));
BOOST_ASSERT(addr_of_type != nullptr);
HaveSpendingKeyForPaymentAddress test(pwalletMain);

View File

@ -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<libzcash::SaplingPaymentAddress>(&address) == nullptr) {
std::optional<libzcash::PaymentAddress> address = keyIO.DecodePaymentAddress(migrationDestAddress);
if (!address.has_value() || std::get_if<libzcash::SaplingPaymentAddress>(&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<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::string address,
std::optional<libzcash::PaymentAddress> address,
int minDepth,
bool ignoreSpent,
bool requireSpendingKey)
{
std::set<libzcash::RawAddress> 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<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
@ -5297,13 +5284,6 @@ std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
return libzcash::ViewingKey();
}
std::optional<libzcash::ViewingKey> 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<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
@ -5359,13 +5334,6 @@ std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()
return libzcash::SpendingKey();
}
std::optional<libzcash::SpendingKey> 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");
}

View File

@ -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<unsigned char>& 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<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::string address,
std::optional<libzcash::PaymentAddress> 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<libzcash::ViewingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::ViewingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::ViewingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
std::optional<libzcash::ViewingKey> 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<libzcash::SpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
std::optional<libzcash::SpendingKey> 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;
};

View File

@ -41,9 +41,6 @@ std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(co
std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(const SaplingExtendedSpendingKey &sk) const {
return std::make_pair("sapling", sk.DefaultAddress());
}
std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(const InvalidEncoding&) const {
throw std::invalid_argument("Cannot derive default address from invalid spending key");
}
std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(const SproutViewingKey &sk) const {
return std::make_pair("sprout", sk.address());
@ -51,23 +48,8 @@ std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(con
std::pair<std::string, PaymentAddress> AddressInfoFromViewingKey::operator()(const SaplingExtendedFullViewingKey &sk) const {
return std::make_pair("sapling", sk.DefaultAddress());
}
std::pair<std::string, PaymentAddress> 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<libzcash::InvalidEncoding>(zaddr);
}
bool IsValidViewingKey(const libzcash::ViewingKey& vk) {
return !std::holds_alternative<libzcash::InvalidEncoding>(vk);
}
bool IsValidSpendingKey(const libzcash::SpendingKey& zkey) {
return !std::holds_alternative<libzcash::InvalidEncoding>(zkey);
}
} // namespace libzcash
uint32_t TypecodeForReceiver::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
@ -117,12 +99,6 @@ std::optional<libzcash::RawAddress> ReceiverToRawAddress::operator()(
return std::nullopt;
}
std::optional<libzcash::RawAddress> RecipientForPaymentAddress::operator()(
const libzcash::InvalidEncoding& no) const
{
return std::nullopt;
}
std::optional<libzcash::RawAddress> RecipientForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
@ -146,12 +122,6 @@ std::optional<libzcash::RawAddress> RecipientForPaymentAddress::operator()(
return std::nullopt;
}
std::set<libzcash::RawAddress> GetRawAddresses::operator()(
const libzcash::InvalidEncoding& no) const
{
return {};
}
std::set<libzcash::RawAddress> GetRawAddresses::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{

View File

@ -24,12 +24,6 @@ public:
/** Protocol addresses that can receive funds in a transaction. */
typedef std::variant<SproutPaymentAddress, SaplingPaymentAddress> 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<InvalidEncoding, SproutViewingKey, SaplingExtendedFullViewingKey> ViewingKey;
typedef std::variant<InvalidEncoding, SproutSpendingKey, SaplingExtendedSpendingKey> 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<std::string, PaymentAddress> operator()(const SproutSpendingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const struct SaplingExtendedSpendingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const InvalidEncoding&) const;
};
class AddressInfoFromViewingKey {
public:
std::pair<std::string, PaymentAddress> operator()(const SproutViewingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const struct SaplingExtendedFullViewingKey&) const;
std::pair<std::string, PaymentAddress> 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<libzcash::RawAddress> operator()(const libzcash::InvalidEncoding& no) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::UnifiedAddress &uaddr) const;
@ -215,7 +202,6 @@ class GetRawAddresses {
public:
GetRawAddresses() {}
std::set<libzcash::RawAddress> operator()(const libzcash::InvalidEncoding& no) const;
std::set<libzcash::RawAddress> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::set<libzcash::RawAddress> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::set<libzcash::RawAddress> operator()(const libzcash::UnifiedAddress &uaddr) const;