Merge pull request #5440 from nuttycom/cleanup/sendmany_txbuilder-prereq

Prerequisites for replacing manual transaction construction in asyncrpcoperation_sendmany with the transaction builder.
This commit is contained in:
str4d 2022-01-11 15:24:55 +00:00 committed by GitHub
commit 005a8624bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1198 additions and 986 deletions

View File

@ -82,9 +82,11 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
# Node 3 will test that watch only address utxos are not selected
self.nodes[3].importaddress(mytaddr)
recipients= [{"address":myzaddr, "amount": Decimal('1')}]
myopid = self.nodes[3].z_sendmany(mytaddr, recipients)
wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient transparent funds, no UTXOs found for taddr from address.", 10)
try:
myopid = self.nodes[3].z_sendmany(mytaddr, recipients)
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Invalid from address: does not belong to this node, spending key not found.", errorString);
# This send will fail because our wallet does not allow any change when shielding a coinbase utxo,
# as it's currently not possible to specify a change address in z_sendmany.
@ -151,7 +153,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
results = self.nodes[1].z_listunspent(1, 999, False, [myzaddr])
except JSONRPCException as e:
errorString = e.error['message']
assert_equal("Invalid parameter, spending key for address does not belong to wallet" in errorString, True)
assert_equal("Invalid parameter, spending key for an address does not belong to the wallet.", errorString)
# Verify that debug=zrpcunsafe logs params, and that full txid is associated with opid
initialized_line = check_node_log(self, 0, myopid + ": z_sendmany initialized", False)

View File

@ -822,10 +822,10 @@ CScript CChainParams::GetFoundersRewardScriptAtHeight(int nHeight) const {
assert(nHeight > 0 && nHeight <= consensus.GetLastFoundersRewardBlockHeight(nHeight));
KeyIO keyIO(*this);
CTxDestination address = keyIO.DecodeDestination(GetFoundersRewardAddressAtHeight(nHeight).c_str());
assert(IsValidDestination(address));
assert(IsScriptDestination(address));
CScriptID scriptID = std::get<CScriptID>(address); // address is a variant
auto address = keyIO.DecodePaymentAddress(GetFoundersRewardAddressAtHeight(nHeight).c_str());
assert(address.has_value());
assert(std::holds_alternative<CScriptID>(address.value()));
CScriptID scriptID = std::get<CScriptID>(address.value());
CScript script = CScript() << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return script;
}

View File

@ -9,6 +9,7 @@
#include <script/standard.h>
#include "upgrades.h"
#include "util.h"
#include "util/match.h"
namespace Consensus {
bool Params::NetworkUpgradeActive(int nHeight, Consensus::UpgradeIndex idx) const {
@ -163,17 +164,26 @@ namespace Consensus {
// Parse the address strings into concrete types.
std::vector<FundingStreamAddress> addresses;
for (auto addr : strAddresses) {
auto taddr = keyIO.DecodeDestination(addr);
if (IsValidDestination(taddr)) {
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.
addresses.push_back(std::get<libzcash::SaplingPaymentAddress>(zaddr));
for (const auto& strAddr : strAddresses) {
auto addr = keyIO.DecodePaymentAddress(strAddr);
if (!addr.has_value()) {
throw std::runtime_error("Funding stream address was not a valid " PACKAGE_NAME " address.");
}
std::visit(match {
[&](const CKeyID& keyId) {
addresses.push_back(GetScriptForDestination(keyId));
},
[&](const CScriptID& scriptId) {
addresses.push_back(GetScriptForDestination(scriptId));
},
[&](const libzcash::SaplingPaymentAddress& zaddr) {
addresses.push_back(zaddr);
},
[&](const auto& zaddr) {
throw std::runtime_error("Funding stream address was not a valid transparent P2SH or Sapling address.");
}
}, addr.value());
}
auto validationResult = FundingStream::ValidateFundingStream(params, startHeight, endHeight, addresses);

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);
}
}
}
@ -78,11 +78,11 @@ namespace libzcash {
return tfm::format("Sapling(%s)", HexStr(ss.begin(), ss.end()));
}
std::string operator()(const P2SHAddress &p2sh) const {
std::string operator()(const CScriptID &p2sh) const {
return tfm::format("P2SH(%s)", p2sh.GetHex());
}
std::string operator()(const P2PKHAddress &p2pkh) const {
std::string operator()(const CKeyID &p2pkh) const {
return tfm::format("P2PKH(%s)", p2pkh.GetHex());
}
@ -140,11 +140,11 @@ TEST(Keys, EncodeAndDecodeUnified)
ua.AddReceiver(r);
}
if (!test[1].isNull()) {
libzcash::P2SHAddress r(ParseHex(test[1].get_str()));
CScriptID r(ParseHex(test[1].get_str()));
ua.AddReceiver(r);
}
if (!test[0].isNull()) {
libzcash::P2PKHAddress r(ParseHex(test[0].get_str()));
CKeyID r(ParseHex(test[0].get_str()));
ua.AddReceiver(r);
}
@ -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

@ -1098,16 +1098,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
KeyIO keyIO(chainparams);
#ifdef ENABLE_MINING
if (mapArgs.count("-mineraddress")) {
CTxDestination addr = keyIO.DecodeDestination(mapArgs["-mineraddress"]);
if (!IsValidDestination(addr)) {
// Try a payment address
auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
if (!std::visit(IsValidMinerAddress(), std::visit(ExtractMinerAddress(), zaddr)))
{
return InitError(strprintf(
_("Invalid address for -mineraddress=<addr>: '%s' (must be a Sapling or transparent address)"),
mapArgs["-mineraddress"]));
}
auto addr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
if (!(addr.has_value() && std::visit(ExtractMinerAddress(), addr.value()).has_value())) {
return InitError(strprintf(
_("Invalid address for -mineraddress=<addr>: '%s' (must be a Sapling or transparent P2PKH address)"),
mapArgs["-mineraddress"]));
}
}
#endif
@ -1678,15 +1673,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#ifdef ENABLE_WALLET
bool minerAddressInLocalWallet = false;
if (pwalletMain) {
CTxDestination addr = keyIO.DecodeDestination(mapArgs["-mineraddress"]);
if (IsValidDestination(addr)) {
CKeyID keyID = std::get<CKeyID>(addr);
minerAddressInLocalWallet = pwalletMain->HaveKey(keyID);
} else {
auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
minerAddressInLocalWallet = std::visit(
HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr);
auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
if (!zaddr.has_value()) {
return InitError(_("-mineraddress is not a valid " PACKAGE_NAME " address."));
}
minerAddressInLocalWallet = std::visit(
HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr.value());
}
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {
return InitError(_("-mineraddress is not in the local wallet. Either use a local address, or set -minetolocalwallet=0"));

View File

@ -16,6 +16,7 @@
#include <string.h>
#include <algorithm>
#include <variant>
#include "util/match.h"
namespace
{
@ -56,8 +57,8 @@ public:
DataLenForReceiver() {}
size_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const { return 43; }
size_t operator()(const libzcash::P2SHAddress &p2sh) const { return 20; }
size_t operator()(const libzcash::P2PKHAddress &p2pkh) const { return 20; }
size_t operator()(const CScriptID &p2sh) const { return 20; }
size_t operator()(const CKeyID &p2pkh) const { return 20; }
size_t operator()(const libzcash::UnknownReceiver &unknown) const { return unknown.data.size(); }
};
@ -82,11 +83,11 @@ public:
memcpy(data, ss.data(), ss.size());
}
void operator()(const libzcash::P2SHAddress &p2sh) const {
void operator()(const CScriptID &p2sh) const {
memcpy(data, p2sh.begin(), p2sh.size());
}
void operator()(const libzcash::P2PKHAddress &p2pkh) const {
void operator()(const CKeyID &p2pkh) const {
memcpy(data, p2pkh.begin(), p2pkh.size());
}
@ -113,6 +114,14 @@ private:
public:
PaymentAddressEncoder(const KeyConstants& keyConstants) : keyConstants(keyConstants) {}
std::string operator()(const CKeyID& id) const
{
return DestinationEncoder(keyConstants)(id);
}
std::string operator()(const CScriptID& id) const
{
return DestinationEncoder(keyConstants)(id);
}
std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
@ -151,8 +160,6 @@ public:
zcash_address_string_free(encoded);
return res;
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
class ViewingKeyEncoder
@ -189,8 +196,6 @@ public:
memory_cleanse(data.data(), data.size());
return ret;
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
class SpendingKeyEncoder
@ -227,8 +232,6 @@ public:
memory_cleanse(data.data(), data.size());
return ret;
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
// Sizes of SaplingPaymentAddress, SaplingExtendedFullViewingKey, and
@ -355,17 +358,16 @@ std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
return std::visit(PaymentAddressEncoder(keyConstants), zaddr);
}
template<typename T1, typename T2, typename T3>
T1 DecodeAny(
const KeyConstants& keyConstants,
const std::string& str,
std::pair<KeyConstants::Base58Type, size_t> sprout,
std::pair<KeyConstants::Bech32Type, size_t> sapling)
template<typename T1, typename T2>
std::optional<T1> DecodeSprout(
const KeyConstants& keyConstants,
const std::string& str,
const std::pair<KeyConstants::Base58Type, size_t>& keyMeta)
{
std::vector<unsigned char> data;
if (DecodeBase58Check(str, data)) {
const std::vector<unsigned char>& prefix = keyConstants.Base58Prefix(sprout.first);
if ((data.size() == sprout.second + prefix.size()) &&
const std::vector<unsigned char>& prefix = keyConstants.Base58Prefix(keyMeta.first);
if ((data.size() == keyMeta.second + prefix.size()) &&
std::equal(prefix.begin(), prefix.end(), data.begin())) {
CSerializeData serialized(data.begin() + prefix.size(), data.end());
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
@ -377,15 +379,26 @@ T1 DecodeAny(
}
}
data.clear();
memory_cleanse(data.data(), data.size());
return std::nullopt;
}
template<typename T1, typename T2>
std::optional<T1> DecodeSapling(
const KeyConstants& keyConstants,
const std::string& str,
const std::pair<KeyConstants::Bech32Type, size_t>& keyMeta)
{
std::vector<unsigned char> data;
auto bech = bech32::Decode(str);
if (bech.first == keyConstants.Bech32HRP(sapling.first) &&
bech.second.size() == sapling.second) {
if (bech.first == keyConstants.Bech32HRP(keyMeta.first) &&
bech.second.size() == keyMeta.second) {
// Bech32 decoding
data.reserve((bech.second.size() * 5) / 8);
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
T3 ret;
T2 ret;
ss >> ret;
memory_cleanse(data.data(), data.size());
return ret;
@ -393,7 +406,27 @@ T1 DecodeAny(
}
memory_cleanse(data.data(), data.size());
return libzcash::InvalidEncoding();
return std::nullopt;
}
template<typename T1, typename T2, typename T3>
std::optional<T1> DecodeAny(
const KeyConstants& keyConstants,
const std::string& str,
const std::pair<KeyConstants::Base58Type, size_t>& sproutKeyMeta,
const std::pair<KeyConstants::Bech32Type, size_t>& saplingKeyMeta)
{
auto sprout = DecodeSprout<T1, T2>(keyConstants, str, sproutKeyMeta);
if (sprout.has_value()) {
return sprout.value();
}
auto sapling = DecodeSapling<T1, T3>(keyConstants, str, saplingKeyMeta);
if (sapling.has_value()) {
return sapling.value();
}
return std::nullopt;
}
/**
@ -421,7 +454,7 @@ static bool AddP2SHReceiver(void* ua, const unsigned char* raw)
reinterpret_cast<const char*>(raw + 20),
SER_NETWORK,
PROTOCOL_VERSION);
libzcash::P2SHAddress receiver;
CScriptID receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
@ -436,7 +469,7 @@ static bool AddP2PKHReceiver(void* ua, const unsigned char* raw)
reinterpret_cast<const char*>(raw + 20),
SER_NETWORK,
PROTOCOL_VERSION);
libzcash::P2PKHAddress receiver;
CKeyID receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
@ -447,7 +480,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;
@ -463,19 +496,43 @@ libzcash::PaymentAddress KeyIO::DecodePaymentAddress(const std::string& str)
return ua;
}
// Fall back on trying Sprout or Sapling.
return DecodeAny<libzcash::PaymentAddress,
libzcash::SproutPaymentAddress,
libzcash::SaplingPaymentAddress>(
// Try parsing as a Sapling address
auto sapling = DecodeSapling<libzcash::SaplingPaymentAddress, libzcash::SaplingPaymentAddress>(
keyConstants,
str,
std::make_pair(KeyConstants::ZCPAYMENT_ADDRESS, libzcash::SerializedSproutPaymentAddressSize),
std::make_pair(KeyConstants::SAPLING_PAYMENT_ADDRESS, ConvertedSaplingPaymentAddressSize)
);
std::make_pair(KeyConstants::SAPLING_PAYMENT_ADDRESS, ConvertedSaplingPaymentAddressSize));
if (sapling.has_value()) {
return sapling.value();
}
// Try parsing as a Sprout address
auto sprout = DecodeSprout<libzcash::SproutPaymentAddress, libzcash::SproutPaymentAddress>(
keyConstants,
str,
std::make_pair(KeyConstants::ZCPAYMENT_ADDRESS, libzcash::SerializedSproutPaymentAddressSize));
if (sprout.has_value()) {
return sprout.value();
}
// Finally, try parsing as transparent
return std::visit(match {
[](const CKeyID& keyIdIn) {
std::optional<libzcash::PaymentAddress> keyId = keyIdIn;
return keyId;
},
[](const CScriptID& scriptIdIn) {
std::optional<libzcash::PaymentAddress> scriptId = scriptIdIn;
return scriptId;
},
[](const CNoDestination& d) {
std::optional<libzcash::PaymentAddress> result = std::nullopt;
return result;
}
}, DecodeDestination(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 +540,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 +557,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

@ -151,7 +151,7 @@ bool CBasicKeyStore::AddSproutSpendingKey(const libzcash::SproutSpendingKey &sk)
return true;
}
//! Sapling
//! Sapling
bool CBasicKeyStore::AddSaplingSpendingKey(
const libzcash::SaplingExtendedSpendingKey &sk)
{
@ -187,7 +187,7 @@ bool CBasicKeyStore::AddSaplingFullViewingKey(
return CBasicKeyStore::AddSaplingIncomingViewingKey(ivk, extfvk.DefaultAddress());
}
// This function updates the wallet's internal address->ivk map.
// This function updates the wallet's internal address->ivk map.
// If we add an address that is already in the map, the map will
// remain unchanged as each address only has one ivk.
bool CBasicKeyStore::AddSaplingIncomingViewingKey(
@ -265,8 +265,9 @@ bool CBasicKeyStore::GetSaplingIncomingViewingKey(const libzcash::SaplingPayment
return false;
}
bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymentAddress &addr,
libzcash::SaplingExtendedSpendingKey &extskOut) const {
bool CBasicKeyStore::GetSaplingExtendedSpendingKey(
const libzcash::SaplingPaymentAddress &addr,
libzcash::SaplingExtendedSpendingKey &extskOut) const {
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingExtendedFullViewingKey extfvk;
@ -275,3 +276,13 @@ bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymen
GetSaplingFullViewingKey(ivk, extfvk) &&
GetSaplingSpendingKey(extfvk, extskOut);
}
bool CBasicKeyStore::HaveSaplingSpendingKeyForAddress(
const libzcash::SaplingPaymentAddress &addr) const {
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingExtendedFullViewingKey extfvk;
return GetSaplingIncomingViewingKey(addr, ivk) &&
GetSaplingFullViewingKey(ivk, extfvk) &&
HaveSaplingSpendingKey(extfvk);
}

View File

@ -66,6 +66,8 @@ public:
//! Check whether a Sapling spending key corresponding to a given Sapling viewing key is present in the store.
virtual bool HaveSaplingSpendingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk) const =0;
virtual bool HaveSaplingSpendingKeyForAddress(
const libzcash::SaplingPaymentAddress &addr) const =0;
virtual bool GetSaplingSpendingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk,
libzcash::SaplingExtendedSpendingKey& skOut) const =0;
@ -246,6 +248,7 @@ public:
}
return result;
}
bool HaveSaplingSpendingKeyForAddress(const libzcash::SaplingPaymentAddress &addr) const;
bool GetSaplingSpendingKey(
const libzcash::SaplingExtendedFullViewingKey &extfvk,
libzcash::SaplingExtendedSpendingKey &skOut) const

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();
@ -706,24 +702,40 @@ class MinerAddressScript : public CReserveScript
void KeepScript() {}
};
std::optional<MinerAddress> ExtractMinerAddress::operator()(const CKeyID &keyID) const {
boost::shared_ptr<MinerAddressScript> mAddr(new MinerAddressScript());
mAddr->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
return mAddr;
}
std::optional<MinerAddress> ExtractMinerAddress::operator()(const CScriptID &addr) const {
return std::nullopt;
}
std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::SproutPaymentAddress &addr) const {
return std::nullopt;
}
std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::SaplingPaymentAddress &addr) const {
return addr;
}
std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::UnifiedAddress &addr) const {
for (const auto& receiver: addr) {
if (std::holds_alternative<libzcash::SaplingPaymentAddress>(receiver)) {
return std::get<libzcash::SaplingPaymentAddress>(receiver);
}
}
return std::nullopt;
}
void GetMinerAddress(MinerAddress &minerAddress)
{
KeyIO keyIO(Params());
// Try a transparent address first
auto mAddrArg = GetArg("-mineraddress", "");
CTxDestination addr = keyIO.DecodeDestination(mAddrArg);
if (IsValidDestination(addr)) {
boost::shared_ptr<MinerAddressScript> mAddr(new MinerAddressScript());
CKeyID keyID = std::get<CKeyID>(addr);
mAddr->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
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,36 +32,11 @@ class ExtractMinerAddress
public:
ExtractMinerAddress() {}
MinerAddress operator()(const libzcash::InvalidEncoding &invalid) const {
return InvalidMinerAddress();
}
MinerAddress operator()(const libzcash::SproutPaymentAddress &addr) const {
return InvalidMinerAddress();
}
MinerAddress operator()(const libzcash::SaplingPaymentAddress &addr) const {
return addr;
}
MinerAddress operator()(const libzcash::UnifiedAddress &addr) const {
auto recipient = RecipientForPaymentAddress()(addr);
if (recipient) {
// This looks like a recursive call, but we are actually calling
// ExtractMinerAddress with a different type:
// - libzcash::PaymentAddress has a libzcash::UnifiedAddress
// alternative, which invokes this method.
// - RecipientForPaymentAddress() returns libzcash::RawAddress,
// which does not have a libzcash::UnifiedAddress alternative.
//
// This works because std::visit does not require the visitor to
// solely match the std::variant, only that it can handle all of
// the variant's alternatives.
return std::visit(ExtractMinerAddress(), *recipient);
} else {
// 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();
}
}
std::optional<MinerAddress> operator()(const CKeyID &keyID) const;
std::optional<MinerAddress> operator()(const CScriptID &addr) const;
std::optional<MinerAddress> operator()(const libzcash::SproutPaymentAddress &addr) const;
std::optional<MinerAddress> operator()(const libzcash::SaplingPaymentAddress &addr) const;
std::optional<MinerAddress> operator()(const libzcash::UnifiedAddress &addr) const;
};
class KeepMinerAddress
@ -76,7 +44,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 +57,6 @@ class IsValidMinerAddress
public:
IsValidMinerAddress() {}
bool operator()(const InvalidMinerAddress &invalid) const {
return false;
}
bool operator()(const libzcash::SaplingPaymentAddress &pa) const {
return true;
}

View File

@ -22,6 +22,7 @@ class CKeyID : public uint160
public:
CKeyID() : uint160() {}
CKeyID(const uint160& in) : uint160(in) {}
explicit CKeyID(const std::vector<unsigned char>& vch) : uint160(vch) {}
};
typedef uint256 ChainCode;

View File

@ -217,7 +217,27 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
class DescribePaymentAddressVisitor
{
public:
UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); }
UniValue operator()(const CKeyID &addr) const {
UniValue obj(UniValue::VOBJ);
obj.pushKV("type", "p2pkh");
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.pushKV("ismine", pwalletMain->HaveKey(addr));
}
#endif
return obj;
}
UniValue operator()(const CScriptID &addr) const {
UniValue obj(UniValue::VOBJ);
obj.pushKV("type", "p2sh");
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.pushKV("ismine", pwalletMain->HaveCScript(addr));
}
#endif
return obj;
}
UniValue operator()(const libzcash::SproutPaymentAddress &zaddr) const {
UniValue obj(UniValue::VOBJ);
@ -226,7 +246,7 @@ public:
obj.pushKV("transmissionkey", zaddr.pk_enc.GetHex());
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.pushKV("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr));
obj.pushKV("ismine", pwalletMain->HaveSproutSpendingKey(zaddr));
}
#endif
return obj;
@ -239,7 +259,7 @@ public:
obj.pushKV("diversifiedtransmissionkey", zaddr.pk_d.GetHex());
#ifdef ENABLE_WALLET
if (pwalletMain) {
obj.pushKV("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr));
obj.pushKV("ismine", pwalletMain->HaveSaplingSpendingKeyForAddress(zaddr));
}
#endif
return obj;
@ -257,16 +277,16 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"z_validateaddress \"zaddr\"\n"
"\nReturn information about the given z address.\n"
"z_validateaddress \"address\"\n"
"\nReturn information about the given address.\n"
"\nArguments:\n"
"1. \"zaddr\" (string, required) The z address to validate\n"
"1. \"address\" (string, required) The address to validate\n"
"\nResult:\n"
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
" \"address\" : \"zaddr\", (string) The z address validated\n"
" \"type\" : \"xxxx\", (string) \"sprout\" or \"sapling\"\n"
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
" \"address\" : \"addr\", (string) The address validated\n"
" \"type\" : \"xxxx\", (string) \"p2pkh\", \"p2sh\", \"sprout\" or \"sapling\"\n"
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
" \"payingkey\" : \"hex\", (string) [sprout] The hex value of the paying key, a_pk\n"
" \"transmissionkey\" : \"hex\", (string) [sprout] The hex value of the transmission key, pk_enc\n"
" \"diversifier\" : \"hex\", (string) [sapling] The hex value of the diversifier, d\n"
@ -288,14 +308,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

@ -625,6 +625,16 @@ public:
}
};
/** A reference to a CScript: the Hash160 of its serialization */
class CScriptID : public uint160
{
public:
CScriptID() : uint160() {}
explicit CScriptID(const CScript& in);
CScriptID(const uint160& in) : uint160(in) {}
explicit CScriptID(const std::vector<unsigned char>& vch) : uint160(vch) {}
};
class CReserveScript
{
public:

View File

@ -18,15 +18,6 @@ static const bool DEFAULT_ACCEPT_DATACARRIER = true;
class CKeyID;
class CScript;
/** A reference to a CScript: the Hash160 of its serialization (see script.h) */
class CScriptID : public uint160
{
public:
CScriptID() : uint160() {}
explicit CScriptID(const CScript& in);
CScriptID(const uint160& in) : uint160(in) {}
};
/**
* Default setting for nMaxDatacarrierBytes. 80 bytes of data, +1 for OP_RETURN,
* +2 for the pushdata opcodes.

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

@ -22,6 +22,7 @@
#include "timedata.h"
#include "transaction_builder.h"
#include "util.h"
#include "util/match.h"
#include "utilmoneystr.h"
#include "utiltime.h"
#include "wallet.h"
@ -67,7 +68,7 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
CAmount fee,
UniValue contextInfo) :
tx_(contextualTx), utxoInputs_(utxoInputs), sproutNoteInputs_(sproutNoteInputs),
saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), contextinfo_(contextInfo)
saplingNoteInputs_(saplingNoteInputs), memo_(recipient.second), fee_(fee), contextinfo_(contextInfo)
{
if (fee < 0 || fee > MAX_MONEY) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range");
@ -77,10 +78,6 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs");
}
if (std::get<0>(recipient).size() == 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Recipient parameter missing");
}
if (sproutNoteInputs.size() > 0 && saplingNoteInputs.size() > 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress");
}
@ -96,19 +93,32 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
}
KeyIO keyIO(Params());
toTaddr_ = keyIO.DecodeDestination(std::get<0>(recipient));
isToTaddr_ = IsValidDestination(toTaddr_);
isToTaddr_ = false;
isToZaddr_ = false;
if (!isToTaddr_) {
auto address = keyIO.DecodePaymentAddress(std::get<0>(recipient));
if (IsValidPaymentAddress(address)) {
std::visit(match {
[&](const CKeyID& keyId) {
toTaddr_ = keyId;
isToTaddr_ = true;
},
[&](const CScriptID& scriptId) {
toTaddr_ = scriptId;
isToTaddr_ = true;
},
[&](const libzcash::SproutPaymentAddress& addr) {
toPaymentAddress_ = addr;
isToZaddr_ = true;
toPaymentAddress_ = address;
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address");
}
}
},
[&](const libzcash::SaplingPaymentAddress& addr) {
toPaymentAddress_ = addr;
isToZaddr_ = true;
},
[&](const libzcash::UnifiedAddress& addr) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"z_mergetoaddress does not yet support sending to unified addresses");
},
}, recipient.first);
// Log the context info i.e. the call parameters to z_mergetoaddress
if (LogAcceptCategory("zrpcunsafe")) {
@ -331,9 +341,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
if (isToTaddr_) {
builder_.AddTransparentOutput(toTaddr_, sendAmount);
} else {
std::string zaddr = std::get<0>(recipient_);
std::string memo = std::get<1>(recipient_);
std::array<unsigned char, ZC_MEMO_SIZE> hexMemo = get_memo_from_hex_string(memo);
std::array<unsigned char, ZC_MEMO_SIZE> hexMemo = get_memo_from_hex_string(memo_);
auto saplingPaymentAddress = std::get_if<libzcash::SaplingPaymentAddress>(&toPaymentAddress_);
if (saplingPaymentAddress == nullptr) {
// This should never happen as we have already determined that the payment is to sapling
@ -385,14 +393,11 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
* END SCENARIO #1
*/
// Prepare raw transaction to handle JoinSplits
CMutableTransaction mtx(tx_);
ed25519_generate_keypair(&joinSplitPrivKey_, &joinSplitPubKey_);
mtx.joinSplitPubKey = joinSplitPubKey_;
tx_ = CTransaction(mtx);
std::string hexMemo = std::get<1>(recipient_);
/**
* SCENARIO #2
@ -408,8 +413,8 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
info.vpub_new = 0;
JSOutput jso = JSOutput(std::get<libzcash::SproutPaymentAddress>(toPaymentAddress_), sendAmount);
if (hexMemo.size() > 0) {
jso.memo = get_memo_from_hex_string(hexMemo);
if (memo_.size() > 0) {
jso.memo = get_memo_from_hex_string(memo_);
}
info.vjsout.push_back(jso);
@ -697,8 +702,8 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
if (isToZaddr_ && vpubNewProcessed) {
outputType = "target";
jso.addr = std::get<libzcash::SproutPaymentAddress>(toPaymentAddress_);
if (!hexMemo.empty()) {
jso.memo = get_memo_from_hex_string(hexMemo);
if (!memo_.empty()) {
jso.memo = get_memo_from_hex_string(memo_);
}
}
info.vjsout.push_back(jso);

View File

@ -34,7 +34,7 @@ typedef std::tuple<JSOutPoint, SproutNote, CAmount, SproutSpendingKey> MergeToAd
typedef std::tuple<SaplingOutPoint, SaplingNote, CAmount, SaplingExpandedSpendingKey> MergeToAddressInputSaplingNote;
// A recipient is a tuple of address, memo (optional if zaddr)
typedef std::tuple<std::string, std::string> MergeToAddressRecipient;
typedef std::pair<libzcash::PaymentAddress, std::string> MergeToAddressRecipient;
// Package of info which is passed to perform_joinsplit methods.
struct MergeToAddressJSInfo {
@ -89,11 +89,11 @@ private:
uint32_t consensusBranchId_;
CAmount fee_;
int mindepth_;
MergeToAddressRecipient recipient_;
bool isToTaddr_;
bool isToZaddr_;
CTxDestination toTaddr_;
PaymentAddress toPaymentAddress_;
std::string memo_;
Ed25519VerificationKey joinSplitPubKey_;
Ed25519SigningKey joinSplitPrivKey_;

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

@ -21,6 +21,7 @@
#include "transaction_builder.h"
#include "timedata.h"
#include "util.h"
#include "util/match.h"
#include "utilmoneystr.h"
#include "wallet.h"
#include "walletdb.h"
@ -92,21 +93,41 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
KeyIO keyIO(Params());
useanyutxo_ = fromAddress == "ANY_TADDR";
fromtaddr_ = keyIO.DecodeDestination(fromAddress);
isfromtaddr_ = useanyutxo_ || IsValidDestination(fromtaddr_);
isfromzaddr_ = false;
if (!isfromtaddr_) {
if (useanyutxo_) {
isfromtaddr_ = true;
} else {
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)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, no spending key found for zaddr");
if (!std::visit(HaveSpendingKeyForPaymentAddress(pwalletMain), address.value())) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"Invalid from address, no spending key found for address");
}
isfromzaddr_ = true;
frompaymentaddress_ = address;
spendingkey_ = std::visit(GetSpendingKeyForPaymentAddress(pwalletMain), address).value();
std::visit(match {
[&](const CKeyID& keyId) {
fromtaddr_ = keyId;
isfromtaddr_ = true;
},
[&](const CScriptID& scriptId) {
fromtaddr_ = scriptId;
isfromtaddr_ = true;
},
[&](const libzcash::SproutPaymentAddress& addr) {
frompaymentaddress_ = addr;
isfromzaddr_ = true;
},
[&](const libzcash::SaplingPaymentAddress& addr) {
frompaymentaddress_ = addr;
isfromzaddr_ = true;
},
[&](const libzcash::UnifiedAddress& addr) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"Unified addresses are not yet supported by z_sendmany");
}
}, address.value());
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address");
}
@ -306,9 +327,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
// Get various necessary keys
SaplingExpandedSpendingKey expsk;
uint256 ovk;
if (isfromzaddr_) {
auto sk = std::get<libzcash::SaplingExtendedSpendingKey>(spendingkey_);
expsk = sk.expsk;
auto saplingKey = std::visit(GetSaplingKeyForPaymentAddress(pwalletMain), frompaymentaddress_);
if (saplingKey.has_value()) {
expsk = saplingKey.value().expsk;
ovk = expsk.full_viewing_key().ovk;
} else {
// Sending from a t-address, which we don't have an ovk for. Instead,
@ -375,8 +396,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 +554,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;
@ -639,7 +662,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries)
// Decrypt the change note's ciphertext to retrieve some data we need
ZCNoteDecryption decryptor(std::get<libzcash::SproutSpendingKey>(spendingkey_).receiving_key());
// FIXME: make sure this .value() call is safe
auto sk = std::visit(GetSproutKeyForPaymentAddress(pwalletMain), frompaymentaddress_).value();
ZCNoteDecryption decryptor(sk.receiving_key());
auto hSig = ZCJoinSplit::h_sig(
prevJoinSplit.randomSeed,
prevJoinSplit.nullifiers,
@ -800,13 +825,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 +954,13 @@ 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_);
std::optional<AddrSet> noteFilter = std::nullopt;
if (zaddr.has_value()) {
noteFilter = AddrSet::ForPaymentAddresses(std::vector({zaddr.value()}));
}
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, noteFilter, mindepth_);
// If using the TransactionBuilder, we only want Sapling notes.
// If not using it, we only want Sprout notes.
@ -1017,7 +1050,8 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit(
if (!witnesses[i]) {
throw runtime_error("joinsplit input could not be found in tree");
}
info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], std::get<libzcash::SproutSpendingKey>(spendingkey_)));
auto sk = std::visit(GetSproutKeyForPaymentAddress(pwalletMain), frompaymentaddress_).value();
info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], sk));
}
// Make sure there are two inputs and two outputs

View File

@ -94,17 +94,16 @@ private:
UniValue contextinfo_; // optional data to include in return value from getStatus()
bool isUsingBuilder_; // Indicates that no Sprout addresses are involved
bool isUsingBuilder_{false}; // Indicates that no Sprout addresses are involved
uint32_t consensusBranchId_;
CAmount fee_;
int mindepth_;
int mindepth_{1};
std::string fromaddress_;
bool useanyutxo_;
bool isfromtaddr_;
bool isfromzaddr_;
bool useanyutxo_{false};
bool isfromtaddr_{false};
bool isfromzaddr_{false};
CTxDestination fromtaddr_;
PaymentAddress frompaymentaddress_;
SpendingKey spendingkey_;
Ed25519VerificationKey joinSplitPubKey_;
Ed25519SigningKey joinSplitPrivKey_;

View File

@ -26,6 +26,7 @@
#include "walletdb.h"
#include "script/interpreter.h"
#include "utiltime.h"
#include "util/match.h"
#include "zcash/IncrementalMerkleTree.hpp"
#include "miner.h"
#include "wallet/paymentdisclosuredb.h"
@ -63,7 +64,7 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
TransactionBuilder builder,
CMutableTransaction contextualTx,
std::vector<ShieldCoinbaseUTXO> inputs,
std::string toAddress,
PaymentAddress toAddress,
CAmount fee,
UniValue contextInfo) :
builder_(builder), tx_(contextualTx), inputs_(inputs), fee_(fee), contextinfo_(contextInfo)
@ -79,13 +80,23 @@ 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;
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid to address");
}
std::visit(match {
[&](CKeyID addr) {
throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2pkh address.");
},
[&](CScriptID addr) {
throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a p2sh address.");
},
[&](libzcash::SaplingPaymentAddress addr) {
tozaddr_ = addr;
},
[&](libzcash::SproutPaymentAddress addr) {
tozaddr_ = addr;
},
[&](libzcash::UnifiedAddress) {
throw JSONRPCError(RPC_VERIFY_REJECTED, "Cannot shield coinbase output to a unified address.");
}
}, toAddress);
// Log the context info
if (LogAcceptCategory("zrpcunsafe")) {
@ -202,6 +213,14 @@ bool AsyncRPCOperation_shieldcoinbase::main_impl() {
return std::visit(ShieldToAddress(this, sendAmount), tozaddr_);
}
bool ShieldToAddress::operator()(const CKeyID &addr) const {
return false;
}
bool ShieldToAddress::operator()(const CScriptID &addr) const {
return false;
}
bool ShieldToAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const {
// update the transaction with these inputs
CMutableTransaction rawTx(m_op->tx_);
@ -264,11 +283,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

@ -45,7 +45,7 @@ public:
TransactionBuilder builder,
CMutableTransaction contextualTx,
std::vector<ShieldCoinbaseUTXO> inputs,
std::string toAddress,
PaymentAddress toAddress,
CAmount fee = DEFAULT_FEE,
UniValue contextInfo = NullUniValue);
virtual ~AsyncRPCOperation_shieldcoinbase();
@ -103,10 +103,11 @@ public:
ShieldToAddress(AsyncRPCOperation_shieldcoinbase *op, CAmount sendAmount) :
m_op(op), sendAmount(sendAmount) {}
bool operator()(const CKeyID &zaddr) const;
bool operator()(const CScriptID &zaddr) const;
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::UnifiedAddress &uaddr) const;
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

@ -3,6 +3,7 @@
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include "chain.h"
#include "core_io.h"
#include "key_io.h"
#include "rpc/server.h"
#include "init.h"
@ -11,6 +12,7 @@
#include "script/standard.h"
#include "sync.h"
#include "util.h"
#include "util/match.h"
#include "utiltime.h"
#include "wallet.h"
@ -67,7 +69,7 @@ std::string DecodeDumpString(const std::string &str) {
for (unsigned int pos = 0; pos < str.length(); pos++) {
unsigned char c = str[pos];
if (c == '%' && pos+2 < str.length()) {
c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) |
((str[pos+2]>>6)*9+((str[pos+2]-'0')&15));
pos += 2;
}
@ -80,7 +82,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() < 1 || params.size() > 3)
throw runtime_error(
"importprivkey \"zcashprivkey\" ( \"label\" rescan )\n"
@ -189,7 +191,7 @@ UniValue importaddress(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() < 1 || params.size() > 4)
throw runtime_error(
"importaddress \"address\" ( \"label\" rescan p2sh )\n"
@ -338,7 +340,7 @@ UniValue importwallet(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 1)
throw runtime_error(
"importwallet \"filename\"\n"
@ -400,9 +402,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) {
@ -476,7 +478,7 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 1)
throw runtime_error(
"dumpprivkey \"t-addr\"\n"
@ -520,7 +522,7 @@ UniValue z_exportwallet(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 1)
throw runtime_error(
"z_exportwallet \"filename\"\n"
@ -751,17 +753,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;
}
@ -769,10 +771,10 @@ UniValue z_importkey(const UniValue& params, bool fHelp)
if (addResult == KeyNotAdded) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet");
}
// whenever a key is imported, we need to scan the whole chain
pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'
// We want to scan for transactions and notes
if (fRescan) {
pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true);
@ -846,17 +848,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,16 +907,52 @@ 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);
if (!sk) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr");
}
return keyIO.EncodeSpendingKey(sk.value());
std::string result = std::visit(match {
[&](const CKeyID& addr) {
CKey key;
if (pwalletMain->GetKey(addr, key)) {
return keyIO.EncodeSecret(key);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private key for this address.");
}
},
[&](const CScriptID& addr) {
CScript redeemScript;
if (pwalletMain->GetCScript(addr, redeemScript)) {
return FormatScript(redeemScript);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the redeem script for this P2SH address.");
}
},
[&](const libzcash::SproutPaymentAddress& addr) {
libzcash::SproutSpendingKey key;
if (pwalletMain->GetSproutSpendingKey(addr, key)) {
return keyIO.EncodeSpendingKey(key);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private spending key for this Sprout address");
}
},
[&](const libzcash::SaplingPaymentAddress& addr) {
libzcash::SaplingExtendedSpendingKey extsk;
if (pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk)) {
return keyIO.EncodeSpendingKey(extsk);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold the private spending key for this Sapling address");
}
},
[&](const libzcash::UnifiedAddress& ua) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"No serialized form is defined for unified spending keys. "
"Use the emergency recovery phrase for this wallet for backup purposes instead.");
return std::string(); //unreachable, here to make the compiler happy
}
}, address.value());
return result;
}
UniValue z_exportviewingkey(const UniValue& params, bool fHelp)
@ -944,11 +982,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 {

File diff suppressed because it is too large Load Diff

View File

@ -362,20 +362,25 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_getbalance)
LOCK2(cs_main, pwalletMain->cs_wallet);
UniValue retValue;
BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress"));
std::string taddr1 = retValue.get_str();
BOOST_CHECK_THROW(CallRPC("z_getbalance too many args"), runtime_error);
BOOST_CHECK_THROW(CallRPC("z_getbalance invalidaddress"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab"));
BOOST_CHECK_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab -1"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab 0"));
// address does not belong to wallet
BOOST_CHECK_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC(std::string("z_getbalance ") + taddr1));
// negative minconf not allowed
BOOST_CHECK_THROW(CallRPC(std::string("z_getbalance ") + taddr1 + " -1"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC(std::string("z_getbalance ") + taddr1 + std::string(" 0")));
// don't have the spending key
BOOST_CHECK_THROW(CallRPC("z_getbalance tnRZ8bPq2pff3xBWhTJhNkVUkm2uhzksDeW5PvEa7aFKGT9Qi3YgTALZfjaY4jU3HLVKBtHdSXxoPoLA3naMPcHBcY88FcF 1"), runtime_error);
BOOST_CHECK_THROW(CallRPC("z_gettotalbalance too manyargs"), runtime_error);
BOOST_CHECK_THROW(CallRPC("z_gettotalbalance -1"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC("z_gettotalbalance 0"));
BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress too many args"), runtime_error);
// negative minconf not allowed
BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab -1"), runtime_error);
@ -602,15 +607,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 +782,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);
@ -1217,7 +1222,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters)
std::vector<SendManyRecipient> recipients = { SendManyRecipient("dummy", 1*COIN, "") };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(std::nullopt, mtx, "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP", recipients, {}, 1) );
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "no spending key found for zaddr"));
BOOST_CHECK( find_error(objError, "no spending key found for address"));
}
}
@ -1765,8 +1770,8 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_parameters)
}
// Test constructor of AsyncRPCOperation_shieldcoinbase
std::string testnetzaddr = "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP";
std::string mainnetzaddr = "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U";
KeyIO keyIO(Params());
auto testnetzaddr = std::get<libzcash::SproutPaymentAddress>(keyIO.DecodePaymentAddress("ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP").value());
try {
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, {}, testnetzaddr, -1 ));
@ -1779,15 +1784,6 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_parameters)
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Empty inputs"));
}
// Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix.
try {
std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,0} };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, mainnetzaddr, 1) );
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Invalid to address"));
}
}
@ -1805,14 +1801,12 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_internals)
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight + 1);
// Add keys manually
KeyIO keyIO(Params());
auto pa = pwalletMain->GenerateNewSproutZKey();
std::string zaddr = keyIO.EncodePaymentAddress(pa);
// Insufficient funds
{
std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,0} };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, zaddr) );
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, pa) );
operation->main();
BOOST_CHECK(operation->isFailed());
std::string msg = operation->getErrorMessage();
@ -1823,7 +1817,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_internals)
{
// Dummy input so the operation object can be instantiated.
std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,100000} };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, zaddr) );
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, pa) );
std::shared_ptr<AsyncRPCOperation_shieldcoinbase> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_shieldcoinbase> (operation);
TEST_FRIEND_AsyncRPCOperation_shieldcoinbase proxy(ptr);
static_cast<AsyncRPCOperation_shieldcoinbase *>(operation.get())->testmode = true;
@ -1932,12 +1926,10 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters)
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1);
// Test constructor of AsyncRPCOperation_mergetoaddress
KeyIO keyIO(Params());
MergeToAddressRecipient testnetzaddr(
"ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP",
keyIO.DecodePaymentAddress("ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP").value(),
"testnet memo");
MergeToAddressRecipient mainnetzaddr(
"zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U",
"mainnet memo");
try {
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, {}, {}, {}, testnetzaddr, -1 ));
@ -1955,14 +1947,6 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters)
std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0, CScript()} };
try {
MergeToAddressRecipient badaddr("", "memo");
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, inputs, {}, {}, badaddr, 1));
BOOST_FAIL("Should have caused an error");
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Recipient parameter missing"));
}
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs =
{MergeToAddressInputSproutNote{JSOutPoint(), SproutNote(), 0, SproutSpendingKey()}};
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs =
@ -1982,15 +1966,6 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters)
} catch (const UniValue& objError) {
BOOST_CHECK(find_error(objError, "Sprout notes are not supported by the TransactionBuilder"));
}
// Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix.
try {
std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0, CScript()} };
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, inputs, {}, {}, mainnetzaddr, 1) );
BOOST_FAIL("Should have caused an error");
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Invalid recipient address"));
}
}
@ -1999,6 +1974,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals)
{
SelectParams(CBaseChainParams::TESTNET);
const Consensus::Params& consensusParams = Params().GetConsensus();
KeyIO keyIO(Params());
LOCK2(cs_main, pwalletMain->cs_wallet);
@ -2012,10 +1988,9 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals)
// Add keys manually
BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress"));
MergeToAddressRecipient taddr1(retValue.get_str(), "");
MergeToAddressRecipient taddr1(keyIO.DecodePaymentAddress(retValue.get_str()).value(), "");
auto pa = pwalletMain->GenerateNewSproutZKey();
KeyIO keyIO(Params());
MergeToAddressRecipient zaddr1(keyIO.EncodePaymentAddress(pa), "DEADBEEF");
MergeToAddressRecipient zaddr1(pa, "DEADBEEF");
// Insufficient funds
{

View File

@ -25,6 +25,7 @@
#include "script/sign.h"
#include "timedata.h"
#include "utilmoneystr.h"
#include "util/match.h"
#include "zcash/JoinSplit.hpp"
#include "zcash/Note.hpp"
#include "crypter.h"
@ -683,49 +684,58 @@ void CWallet::SetBestChain(const CBlockLocator& loc)
SetBestChainINTERNAL(walletdb, loc);
}
std::set<std::pair<libzcash::RawAddress, uint256>> CWallet::GetNullifiersForAddresses(
const std::set<libzcash::RawAddress> & addresses)
{
std::set<std::pair<libzcash::RawAddress, uint256>> nullifierSet;
// Sapling ivk -> list of addrs map
// (There may be more than one diversified address for a given ivk.)
std::map<libzcash::SaplingIncomingViewingKey, std::vector<libzcash::SaplingPaymentAddress>> ivkMap;
for (const auto & addr : addresses) {
auto saplingAddr = std::get_if<libzcash::SaplingPaymentAddress>(&addr);
if (saplingAddr != nullptr) {
libzcash::SaplingIncomingViewingKey ivk;
this->GetSaplingIncomingViewingKey(*saplingAddr, ivk);
ivkMap[ivk].push_back(*saplingAddr);
}
}
for (const auto & txPair : mapWallet) {
// Sprout
for (const auto & noteDataPair : txPair.second.mapSproutNoteData) {
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & address = noteData.address;
if (nullifier && addresses.count(address)) {
nullifierSet.insert(std::make_pair(address, nullifier.value()));
}
}
// Sapling
for (const auto & noteDataPair : txPair.second.mapSaplingNoteData) {
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & ivk = noteData.ivk;
if (nullifier && ivkMap.count(ivk)) {
for (const auto & addr : ivkMap[ivk]) {
nullifierSet.insert(std::make_pair(addr, nullifier.value()));
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> CWallet::GetSproutNullifiers(
const std::set<libzcash::SproutPaymentAddress>& addresses) {
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> nullifierSet;
if (!addresses.empty()) {
for (const auto& txPair : mapWallet) {
for (const auto & noteDataPair : txPair.second.mapSproutNoteData) {
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & address = noteData.address;
if (nullifier && addresses.count(address) > 0) {
nullifierSet.insert(std::make_pair(address, nullifier.value()));
}
}
}
}
return nullifierSet;
}
std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> CWallet::GetSaplingNullifiers(
const std::set<libzcash::SaplingPaymentAddress>& addresses) {
std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> nullifierSet;
if (!addresses.empty()) {
// Sapling ivk -> list of addrs map
// (There may be more than one diversified address for a given ivk.)
std::map<libzcash::SaplingIncomingViewingKey, std::vector<libzcash::SaplingPaymentAddress>> ivkMap;
for (const auto & addr : addresses) {
libzcash::SaplingIncomingViewingKey ivk;
this->GetSaplingIncomingViewingKey(addr, ivk);
ivkMap[ivk].push_back(addr);
}
for (const auto& txPair : mapWallet) {
for (const auto& noteDataPair : txPair.second.mapSaplingNoteData) {
auto & noteData = noteDataPair.second;
auto & nullifier = noteData.nullifier;
auto & ivk = noteData.ivk;
if (nullifier && ivkMap.count(ivk) > 0) {
for (const auto & addr : ivkMap[ivk]) {
nullifierSet.insert(std::make_pair(addr, nullifier.value()));
}
}
}
}
}
return nullifierSet;
}
bool CWallet::IsNoteSproutChange(
const std::set<std::pair<libzcash::RawAddress, uint256>> & nullifierSet,
const libzcash::RawAddress & address,
const std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> & nullifierSet,
const libzcash::SproutPaymentAddress& address,
const JSOutPoint & jsop)
{
// A Note is marked as "change" if the address that received it
@ -747,8 +757,9 @@ bool CWallet::IsNoteSproutChange(
return false;
}
bool CWallet::IsNoteSaplingChange(const std::set<std::pair<libzcash::RawAddress, uint256>> & nullifierSet,
const libzcash::RawAddress & address,
bool CWallet::IsNoteSaplingChange(
const std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> & nullifierSet,
const libzcash::SaplingPaymentAddress& address,
const SaplingOutPoint & op)
{
// A Note is marked as "change" if the address that received it
@ -759,6 +770,8 @@ bool CWallet::IsNoteSaplingChange(const std::set<std::pair<libzcash::RawAddress,
// - Notes created by consolidation transactions (e.g. using
// z_mergetoaddress).
// - Notes sent from one address to itself.
// FIXME: This also needs to check against the wallet's change address
// for the associated unified account when we add UA support
for (const SpendDescription &spend : mapWallet[op.hash].vShieldedSpend) {
if (nullifierSet.count(std::make_pair(address, spend.nullifier))) {
return true;
@ -4927,8 +4940,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."));
}
}
@ -5025,31 +5038,48 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee)
return ::AcceptToMemoryPool(Params(), mempool, state, *this, fLimitFree, NULL, fRejectAbsurdFee);
}
/**
* Find notes in the wallet filtered by payment address, min depth and ability to spend.
* These notes are decrypted and added to the output parameter vector, outEntries.
*/
void CWallet::GetFilteredNotes(
std::vector<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::string address,
int minDepth,
bool ignoreSpent,
bool requireSpendingKey)
{
std::set<libzcash::RawAddress> filterAddresses;
AddrSet AddrSet::ForPaymentAddresses(const std::vector<libzcash::PaymentAddress>& paymentAddrs) {
AddrSet addrs;
for (const auto& addr: paymentAddrs) {
std::visit(match {
[&](const CKeyID& keyId) { },
[&](const CScriptID& scriptId) { },
[&](const libzcash::SproutPaymentAddress& addr) {
addrs.sproutAddresses.insert(addr);
},
[&](const libzcash::SaplingPaymentAddress& addr) {
addrs.saplingAddresses.insert(addr);
},
[&](const libzcash::UnifiedAddress& uaddr) {
for (auto& receiver : uaddr) {
std::visit(match {
[&](const libzcash::SaplingPaymentAddress& addr) {
addrs.saplingAddresses.insert(addr);
},
[&](const auto& other) { }
}, receiver);
}
},
}, addr);
}
return addrs;
}
KeyIO keyIO(Params());
if (address.length() > 0) {
auto addr = keyIO.DecodePaymentAddress(address);
for (const auto ra : std::visit(GetRawAddresses(), addr)) {
filterAddresses.insert(ra);
bool CWallet::HasSpendingKeys(const AddrSet& addrSet) const {
for (const auto& zaddr : addrSet.GetSproutAddresses()) {
if (!HaveSproutSpendingKey(zaddr)) {
return false;
}
}
GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey);
for (const auto& zaddr : addrSet.GetSaplingAddresses()) {
if (!HaveSaplingSpendingKeyForAddress(zaddr)) {
return false;
}
}
return true;
}
/**
* Find notes in the wallet filtered by payment addresses, min depth, max depth,
* if the note is spent, if a spending key is required, and if the notes are locked.
@ -5058,7 +5088,7 @@ void CWallet::GetFilteredNotes(
void CWallet::GetFilteredNotes(
std::vector<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::set<libzcash::RawAddress>& filterAddresses,
const std::optional<AddrSet>& noteFilter,
int minDepth,
int maxDepth,
bool ignoreSpent,
@ -5088,8 +5118,8 @@ void CWallet::GetFilteredNotes(
SproutNoteData nd = pair.second;
SproutPaymentAddress pa = nd.address;
// skip notes which belong to a different payment address in the wallet
if (!(filterAddresses.empty() || filterAddresses.count(pa))) {
// skip notes which do not conform to the filter, if supplied
if (noteFilter.has_value() && !noteFilter.value().HasSproutAddress(pa)) {
continue;
}
@ -5158,8 +5188,8 @@ void CWallet::GetFilteredNotes(
assert(static_cast<bool>(maybe_pa));
auto pa = maybe_pa.value();
// skip notes which belong to a different payment address in the wallet
if (!(filterAddresses.empty() || filterAddresses.count(pa))) {
// skip notes which do not conform to the filter, if supplied
if (noteFilter.has_value() && !noteFilter.value().HasSaplingAddress(pa)) {
continue;
}
@ -5168,7 +5198,7 @@ void CWallet::GetFilteredNotes(
}
// skip notes which cannot be spent
if (requireSpendingKey && !HaveSpendingKeyForPaymentAddress(this)(pa)) {
if (requireSpendingKey && !HaveSaplingSpendingKeyForAddress(pa)) {
continue;
}
@ -5186,42 +5216,57 @@ void CWallet::GetFilteredNotes(
//
// Shielded key and address generalizations
// Payment address operations
//
// PaymentAddressBelongsToWallet
bool PaymentAddressBelongsToWallet::operator()(const CKeyID &addr) const
{
CScript script = GetScriptForDestination(addr);
return m_wallet->HaveKey(addr) || m_wallet->HaveWatchOnly(script);
}
bool PaymentAddressBelongsToWallet::operator()(const CScriptID &addr) const
{
CScript script = GetScriptForDestination(addr);
return m_wallet->HaveCScript(addr) || m_wallet->HaveWatchOnly(script);
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::SproutPaymentAddress &zaddr) const
{
return m_wallet->HaveSproutSpendingKey(zaddr) || m_wallet->HaveSproutViewingKey(zaddr);
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingIncomingViewingKey ivk;
// If we have a SaplingExtendedSpendingKey in the wallet, then we will
// also have the corresponding SaplingExtendedFullViewingKey.
return m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
return
m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
m_wallet->HaveSaplingFullViewingKey(ivk);
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return false;
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::InvalidEncoding& no) const
// GetSourceForPaymentAddress
PaymentAddressSource GetSourceForPaymentAddress::operator()(const CKeyID &zaddr) const
{
return false;
// TODO
return AddressNotFound;
}
PaymentAddressSource GetSourceForPaymentAddress::operator()(const CScriptID &zaddr) const
{
// TODO
return AddressNotFound;
}
///
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const
{
return Random;
}
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingIncomingViewingKey ivk;
@ -5234,7 +5279,7 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl
if (m_wallet->mapSaplingZKeyMetadata.count(ivk) > 0 &&
m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath != "") {
return LegacyHDSeed;
} else if (HaveSpendingKeyForPaymentAddress(m_wallet)(zaddr)) {
} else if (m_wallet->HaveSaplingSpendingKeyForAddress(zaddr)) {
return Imported;
} else {
return ImportedWatchOnly;
@ -5246,21 +5291,24 @@ PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::Sapl
return AddressNotFound;
}
}
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return AddressNotFound;
}
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const
// GetViewingKeyForPaymentAddress
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return AddressNotFound;
return std::nullopt;
}
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
///
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
@ -5274,7 +5322,6 @@ std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
}
return libzcash::ViewingKey(vk);
}
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
@ -5289,26 +5336,27 @@ std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
return std::nullopt;
}
}
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return libzcash::ViewingKey();
return std::nullopt;
}
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
const libzcash::InvalidEncoding& no) const
// HaveSpendingKeyForPaymentAddress
bool HaveSpendingKeyForPaymentAddress::operator()(const CKeyID &addr) const
{
// Defaults to InvalidEncoding
return libzcash::ViewingKey();
return m_wallet->HaveKey(addr);
}
bool HaveSpendingKeyForPaymentAddress::operator()(const CScriptID &addr) const
{
return m_wallet->HaveCScript(addr);
}
bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const
{
return m_wallet->HaveSproutSpendingKey(zaddr);
}
bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingIncomingViewingKey ivk;
@ -5318,53 +5366,88 @@ bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SaplingPayment
m_wallet->GetSaplingFullViewingKey(ivk, extfvk) &&
m_wallet->HaveSaplingSpendingKey(extfvk);
}
bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return false;
}
bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const
{
return false;
}
// GetSproutKeyForPaymentAddress
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
libzcash::SproutSpendingKey k;
if (m_wallet->GetSproutSpendingKey(zaddr, k)) {
return libzcash::SpendingKey(k);
return k;
} else {
return std::nullopt;
}
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SproutSpendingKey> GetSproutKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
// GetSaplingKeyForPaymentAddress
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const CKeyID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const CScriptID &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::SproutPaymentAddress &zaddr) const
{
return std::nullopt;
}
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingExtendedSpendingKey extsk;
if (m_wallet->GetSaplingExtendedSpendingKey(zaddr, extsk)) {
return libzcash::SpendingKey(extsk);
return extsk;
} else {
return std::nullopt;
}
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
std::optional<libzcash::SaplingExtendedSpendingKey> GetSaplingKeyForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
// TODO
return libzcash::SpendingKey();
for (const libzcash::Receiver& receiver: uaddr) {
auto saplingAddr = std::get_if<SaplingPaymentAddress>(&receiver);
if (saplingAddr != nullptr) {
libzcash::SaplingExtendedSpendingKey extsk;
if (m_wallet->GetSaplingExtendedSpendingKey(*saplingAddr, extsk)) {
return extsk;
}
}
}
return std::nullopt;
}
std::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
const libzcash::InvalidEncoding& no) const
{
// Defaults to InvalidEncoding
return libzcash::SpendingKey();
}
// AddViewingKeyToWallet
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SproutViewingKey &vkey) const {
auto addr = vkey.address();
@ -5379,7 +5462,6 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SproutViewingKey
return KeyNotAdded;
}
}
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFullViewingKey &extfvk) const {
if (m_wallet->HaveSaplingSpendingKey(extfvk)) {
return SpendingKeyExists;
@ -5392,9 +5474,7 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::SaplingExtendedFu
}
}
KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key");
}
// AddSpendingKeyToWallet
KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const {
auto addr = sk.address();
@ -5411,7 +5491,6 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKe
return KeyNotAdded;
}
}
KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedSpendingKey &sk) const {
auto extfvk = sk.ToXFVK();
auto ivk = extfvk.fvk.in_viewing_key();
@ -5447,7 +5526,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

@ -623,7 +623,31 @@ public:
};
class AddrSet {
private:
std::set<libzcash::SproutPaymentAddress> sproutAddresses;
std::set<libzcash::SaplingPaymentAddress> saplingAddresses;
AddrSet() {}
public:
static AddrSet ForPaymentAddresses(const std::vector<libzcash::PaymentAddress>& addrs);
const std::set<libzcash::SproutPaymentAddress>& GetSproutAddresses() const {
return sproutAddresses;
}
const std::set<libzcash::SaplingPaymentAddress>& GetSaplingAddresses() const {
return saplingAddresses;
}
bool HasSproutAddress(libzcash::SproutPaymentAddress addr) const {
return sproutAddresses.count(addr) > 0;
}
bool HasSaplingAddress(libzcash::SaplingPaymentAddress addr) const {
return saplingAddresses.count(addr) > 0;
}
};
class COutput
{
@ -1194,9 +1218,20 @@ public:
void AddPendingSaplingMigrationTx(const CTransaction& tx);
/** Saves witness caches and best block locator to disk. */
void SetBestChain(const CBlockLocator& loc);
std::set<std::pair<libzcash::RawAddress, uint256>> GetNullifiersForAddresses(const std::set<libzcash::RawAddress> & addresses);
bool IsNoteSproutChange(const std::set<std::pair<libzcash::RawAddress, uint256>> & nullifierSet, const libzcash::RawAddress & address, const JSOutPoint & entry);
bool IsNoteSaplingChange(const std::set<std::pair<libzcash::RawAddress, uint256>> & nullifierSet, const libzcash::RawAddress & address, const SaplingOutPoint & entry);
std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> GetSproutNullifiers(
const std::set<libzcash::SproutPaymentAddress>& addresses);
bool IsNoteSproutChange(
const std::set<std::pair<libzcash::SproutPaymentAddress, uint256>> & nullifierSet,
const libzcash::SproutPaymentAddress& address,
const JSOutPoint & entry);
std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> GetSaplingNullifiers(
const std::set<libzcash::SaplingPaymentAddress>& addresses);
bool IsNoteSaplingChange(
const std::set<std::pair<libzcash::SaplingPaymentAddress, uint256>> & nullifierSet,
const libzcash::SaplingPaymentAddress& address,
const SaplingOutPoint & entry);
DBErrors LoadWallet(bool& fFirstRunRet);
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);
@ -1304,19 +1339,13 @@ 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 */
void GetFilteredNotes(std::vector<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::string address,
int minDepth=1,
bool ignoreSpent=true,
bool requireSpendingKey=true);
bool HasSpendingKeys(const AddrSet& noteFilter) const;
/* Find notes filtered by payment addresses, min depth, max depth, if they are spent,
if a spending key is required, and if they are locked */
void GetFilteredNotes(std::vector<SproutNoteEntry>& sproutEntries,
std::vector<SaplingNoteEntry>& saplingEntries,
std::set<libzcash::RawAddress>& filterAddresses,
const std::optional<AddrSet>& noteFilter,
int minDepth=1,
int maxDepth=INT_MAX,
bool ignoreSpent=true,
@ -1369,10 +1398,11 @@ private:
public:
PaymentAddressBelongsToWallet(CWallet *wallet) : m_wallet(wallet) {}
bool operator()(const CKeyID &zaddr) const;
bool operator()(const CScriptID &zaddr) const;
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::UnifiedAddress &uaddr) const;
bool operator()(const libzcash::InvalidEncoding& no) const;
};
class GetViewingKeyForPaymentAddress
@ -1382,10 +1412,11 @@ private:
public:
GetViewingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
std::optional<libzcash::ViewingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::ViewingKey> operator()(const CScriptID &zaddr) const;
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
@ -1395,23 +1426,39 @@ private:
public:
HaveSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
bool operator()(const CKeyID &addr) const;
bool operator()(const CScriptID &addr) const;
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
class GetSproutKeyForPaymentAddress
{
private:
CWallet *m_wallet;
public:
GetSpendingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
GetSproutKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
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;
std::optional<libzcash::SproutSpendingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SproutSpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
class GetSaplingKeyForPaymentAddress
{
private:
CWallet *m_wallet;
public:
GetSaplingKeyForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const CKeyID &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const CScriptID &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::SproutPaymentAddress &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::SaplingExtendedSpendingKey> operator()(const libzcash::UnifiedAddress &uaddr) const;
};
enum PaymentAddressSource {
@ -1430,10 +1477,11 @@ private:
public:
GetSourceForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
PaymentAddressSource operator()(const CKeyID &zaddr) const;
PaymentAddressSource operator()(const CScriptID &zaddr) const;
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 +1500,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 +1526,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

@ -24,8 +24,8 @@ bool UnifiedAddress::AddReceiver(Receiver receiver) {
auto t = std::visit(TypecodeForReceiver(), r);
if (
(t == typecode) ||
(std::holds_alternative<P2PKHAddress>(r) && std::holds_alternative<P2SHAddress>(receiver)) ||
(std::holds_alternative<P2SHAddress>(r) && std::holds_alternative<P2PKHAddress>(receiver))
(std::holds_alternative<CKeyID>(r) && std::holds_alternative<CScriptID>(receiver)) ||
(std::holds_alternative<CScriptID>(r) && std::holds_alternative<CKeyID>(receiver))
) {
return false;
}
@ -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,128 +48,26 @@ 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
{
return ZCASH_UA_TYPECODE_SAPLING;
}
uint32_t TypecodeForReceiver::operator()(
const libzcash::P2SHAddress &p2sh) const
const CScriptID &p2sh) const
{
return ZCASH_UA_TYPECODE_P2SH;
}
uint32_t TypecodeForReceiver::operator()(
const libzcash::P2PKHAddress &p2sh) const
const CKeyID &p2sh) const
{
return ZCASH_UA_TYPECODE_P2PKH;
}
uint32_t TypecodeForReceiver::operator()(
const libzcash::UnknownReceiver &unknown) const
{
return unknown.typecode;
}
std::optional<libzcash::RawAddress> ReceiverToRawAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return zaddr;
}
std::optional<libzcash::RawAddress> ReceiverToRawAddress::operator()(
const libzcash::P2SHAddress &p2sh) const
{
return std::nullopt;
}
std::optional<libzcash::RawAddress> ReceiverToRawAddress::operator()(
const libzcash::P2PKHAddress &p2sh) const
{
return std::nullopt;
}
std::optional<libzcash::RawAddress> ReceiverToRawAddress::operator()(
const libzcash::UnknownReceiver &p2sh) const
{
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
{
return zaddr;
}
std::optional<libzcash::RawAddress> RecipientForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return zaddr;
}
std::optional<libzcash::RawAddress> RecipientForPaymentAddress::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
for (auto& receiver : uaddr) {
// Return the first one.
return std::visit(ReceiverToRawAddress(), receiver);
}
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
{
return {zaddr};
}
std::set<libzcash::RawAddress> GetRawAddresses::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
return {zaddr};
}
std::set<libzcash::RawAddress> GetRawAddresses::operator()(
const libzcash::UnifiedAddress &uaddr) const
{
std::set<libzcash::RawAddress> ret;
for (auto& receiver : uaddr) {
auto ra = std::visit(ReceiverToRawAddress(), receiver);
if (ra) {
ret.insert(*ra);
}
}
return ret;
}

View File

@ -2,6 +2,8 @@
#define ZC_ADDRESS_H_
#include "uint256.h"
#include "pubkey.h"
#include "script/script.h"
#include "zcash/address/sapling.hpp"
#include "zcash/address/sprout.hpp"
#include "zcash/address/zip32.h"
@ -9,26 +11,6 @@
#include <variant>
namespace libzcash {
// We use new classes here instead of CKeyID and CScriptID to prevent a cyclic dependency.
class P2PKHAddress: public uint160 {
public:
P2PKHAddress() {}
explicit P2PKHAddress(const std::vector<unsigned char>& vch) : uint160(vch) {}
};
class P2SHAddress: public uint160 {
public:
P2SHAddress() {}
explicit P2SHAddress(const std::vector<unsigned char>& vch) : uint160(vch) {}
};
/** 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:
@ -59,8 +41,8 @@ public:
*/
typedef std::variant<
SaplingPaymentAddress,
P2SHAddress,
P2PKHAddress,
CScriptID,
CKeyID,
UnknownReceiver> Receiver;
struct ReceiverIterator {
@ -137,38 +119,34 @@ public:
/** Addresses that can appear in a string encoding. */
typedef std::variant<
InvalidEncoding,
CKeyID,
CScriptID,
SproutPaymentAddress,
SaplingPaymentAddress,
UnifiedAddress> PaymentAddress;
typedef std::variant<InvalidEncoding, SproutViewingKey, SaplingExtendedFullViewingKey> ViewingKey;
typedef std::variant<InvalidEncoding, SproutSpendingKey, SaplingExtendedSpendingKey> SpendingKey;
/** Viewing keys that can be decoded from a string representation. */
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.
*/
@ -177,48 +155,9 @@ public:
TypecodeForReceiver() {}
uint32_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
uint32_t operator()(const libzcash::P2SHAddress &p2sh) const;
uint32_t operator()(const libzcash::P2PKHAddress &p2pkh) const;
uint32_t operator()(const CScriptID &p2sh) const;
uint32_t operator()(const CKeyID &p2pkh) const;
uint32_t operator()(const libzcash::UnknownReceiver &p2pkh) const;
};
/**
* Converts the given UA receiver to a protocol address, if it is a shielded receiver.
*/
class ReceiverToRawAddress {
public:
ReceiverToRawAddress() {}
std::optional<libzcash::RawAddress> operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::P2SHAddress &p2sh) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::P2PKHAddress &p2pkh) const;
std::optional<libzcash::RawAddress> operator()(const libzcash::UnknownReceiver &p2pkh) const;
};
/**
* Returns the protocol address that should be used in transaction outputs.
*/
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;
};
/**
* Returns all protocol addresses contained within the given payment address.
*/
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;
};
#endif // ZC_ADDRESS_H_