Merge pull request #5522 from nuttycom/feature/wallet_unified_addresses-balance_for_vk
Replace z_getbalanceforaddress with z_getbalanceforviewingkey
This commit is contained in:
commit
09593559f8
|
@ -47,6 +47,11 @@ New RPC Methods
|
|||
being confirmed might be leaked via the user's shell history or the system
|
||||
process table; `zcashd-wallet-tool` is specifically provided to avoid this
|
||||
problem.
|
||||
- 'z_getbalanceforviewingkey' This newly created API allows a user to obtain
|
||||
balance information for funds visible to a Sapling or Unified full
|
||||
viewing key; if a Sprout viewing key is provided, this method allows
|
||||
retrieval of the balance only in the case that the wallet controls the
|
||||
corresponding spending key.
|
||||
|
||||
RPC Changes
|
||||
-----------
|
||||
|
|
|
@ -43,10 +43,11 @@ class WalletAccountsTest(BitcoinTestFramework):
|
|||
# Check we only have balances in the expected pools.
|
||||
# Remember that empty pools are omitted from the output.
|
||||
def check_address_balance(self, address, expected, minconf=None):
|
||||
fvk = self.nodes[0].z_exportviewingkey(address)
|
||||
if minconf is None:
|
||||
actual = self.nodes[0].z_getbalanceforaddress(address)
|
||||
actual = self.nodes[0].z_getbalanceforviewingkey(fvk)
|
||||
else:
|
||||
actual = self.nodes[0].z_getbalanceforaddress(address, minconf)
|
||||
actual = self.nodes[0].z_getbalanceforviewingkey(fvk, minconf)
|
||||
assert_equal(set(expected), set(actual['pools']))
|
||||
for pool in expected:
|
||||
assert_equal(expected[pool] * COIN, actual['pools'][pool]['valueZat'])
|
||||
|
|
|
@ -35,7 +35,8 @@ class WalletZSendmanyTest(BitcoinTestFramework):
|
|||
|
||||
def check_balance(self, node, account, address, expected, minconf=None):
|
||||
self._check_balance_for_rpc('z_getbalanceforaccount', node, account, expected, minconf)
|
||||
self._check_balance_for_rpc('z_getbalanceforaddress', node, address, expected, minconf)
|
||||
fvk = self.nodes[node].z_exportviewingkey(address)
|
||||
self._check_balance_for_rpc('z_getbalanceforviewingkey', node, fvk, expected, minconf)
|
||||
|
||||
def run_test(self):
|
||||
# z_sendmany is expected to fail if tx size breaks limit
|
||||
|
|
|
@ -1683,7 +1683,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||
if (!zaddr.has_value()) {
|
||||
return InitError(_("-mineraddress is not a valid " PACKAGE_NAME " address."));
|
||||
}
|
||||
auto ztxoSelector = pwalletMain->ToZTXOSelector(zaddr.value(), true);
|
||||
auto ztxoSelector = pwalletMain->ZTXOSelectorForAddress(zaddr.value(), true);
|
||||
minerAddressInLocalWallet = ztxoSelector.has_value();
|
||||
}
|
||||
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {
|
||||
|
|
|
@ -373,6 +373,32 @@ CBasicKeyStore::GetUFVKMetadataForReceiver(const libzcash::Receiver& receiver) c
|
|||
return std::visit(FindUFVKId(*this), receiver);
|
||||
}
|
||||
|
||||
std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const libzcash::ViewingKey& vk) const
|
||||
{
|
||||
std::optional<libzcash::UFVKId> result;
|
||||
std::visit(match {
|
||||
[&](const libzcash::SproutViewingKey& vk) {},
|
||||
[&](const libzcash::SaplingExtendedFullViewingKey& extfvk) {
|
||||
const auto saplingIvk = extfvk.ToIncomingViewingKey();
|
||||
const auto ufvkId = mapSaplingKeyUnified.find(saplingIvk);
|
||||
if (ufvkId != mapSaplingKeyUnified.end()) {
|
||||
result = ufvkId->second;
|
||||
}
|
||||
},
|
||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
||||
const auto saplingDfvk = ufvk.GetSaplingKey();
|
||||
if (saplingDfvk.has_value()) {
|
||||
const auto saplingIvk = saplingDfvk.value().ToIncomingViewingKey();
|
||||
const auto ufvkId = mapSaplingKeyUnified.find(saplingIvk);
|
||||
if (ufvkId != mapSaplingKeyUnified.end()) {
|
||||
result = ufvkId->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, vk);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
|
||||
FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
|
||||
const auto saplingIvk = keystore.mapSaplingIncomingViewingKeys.find(saplingAddr);
|
||||
|
|
|
@ -132,6 +132,11 @@ public:
|
|||
GetUFVKMetadataForReceiver(
|
||||
const libzcash::Receiver& receiver
|
||||
) const = 0;
|
||||
|
||||
virtual std::optional<libzcash::UFVKId>
|
||||
GetUFVKIdForViewingKey(
|
||||
const libzcash::ViewingKey& vk
|
||||
) const = 0;
|
||||
};
|
||||
|
||||
typedef std::map<CKeyID, CKey> KeyMap;
|
||||
|
@ -372,6 +377,11 @@ public:
|
|||
GetUFVKMetadataForReceiver(
|
||||
const libzcash::Receiver& receiver
|
||||
) const;
|
||||
|
||||
virtual std::optional<libzcash::UFVKId>
|
||||
GetUFVKIdForViewingKey(
|
||||
const libzcash::ViewingKey& vk
|
||||
) const;
|
||||
};
|
||||
|
||||
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
|
||||
|
|
|
@ -290,6 +290,10 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
|||
// for Sprout, we return change to the originating address.
|
||||
builder_.SendChangeToSprout(addr);
|
||||
},
|
||||
[&](const libzcash::SproutViewingKey& vk) {
|
||||
// for Sprout, we return change to the originating address.
|
||||
builder_.SendChangeToSprout(vk.address());
|
||||
},
|
||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||
// for Sapling, if using a legacy address, return change to the
|
||||
// originating address; otherwise return it to the Sapling internal
|
||||
|
@ -303,6 +307,29 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
|||
builder_.SendChangeTo(changeAddr.value(), ovks.first);
|
||||
}
|
||||
},
|
||||
[&](const libzcash::SaplingExtendedFullViewingKey& fvk) {
|
||||
// for Sapling, if using a legacy address, return change to the
|
||||
// originating address; otherwise return it to the Sapling internal
|
||||
// address corresponding to the UFVK.
|
||||
if (sendFromAccount_ == ZCASH_LEGACY_ACCOUNT) {
|
||||
builder_.SendChangeTo(fvk.DefaultAddress(), ovks.first);
|
||||
} else {
|
||||
auto changeAddr = pwalletMain->GenerateChangeAddressForAccount(
|
||||
sendFromAccount_, allowedChangeTypes_);
|
||||
assert(changeAddr.has_value());
|
||||
builder_.SendChangeTo(changeAddr.value(), ovks.first);
|
||||
}
|
||||
},
|
||||
[&](const libzcash::UnifiedFullViewingKey& fvk) {
|
||||
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), fvk);
|
||||
auto changeAddr = zufvk.GetChangeAddress();
|
||||
if (!changeAddr.has_value()) {
|
||||
throw JSONRPCError(
|
||||
RPC_WALLET_ERROR,
|
||||
"Could not generate a change address from the specified full viewing key ");
|
||||
}
|
||||
builder_.SendChangeTo(changeAddr.value(), ovks.first);
|
||||
},
|
||||
[&](const AccountZTXOPattern& acct) {
|
||||
for (ReceiverType rtype : acct.GetReceiverTypes()) {
|
||||
switch (rtype) {
|
||||
|
|
|
@ -3551,7 +3551,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
|||
"\nReturns the balance of a taddr or zaddr belonging to the node's wallet.\n"
|
||||
"\nCAUTION: If the wallet has only an incoming viewing key for this address, then spends cannot be"
|
||||
"\ndetected, and so the returned balance may be larger than the actual balance."
|
||||
"\nThe argument address may not be a Unified Address; please use z_getbalanceforaddress instead.\n"
|
||||
"\nThe argument address may not be a Unified Address; please use z_getbalanceforviewingkey instead.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string) The selected address. It may be a transparent or shielded address.\n"
|
||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||
|
@ -3618,32 +3618,33 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
|||
return ValueFromAmount(nBalance);
|
||||
}
|
||||
|
||||
UniValue z_getbalanceforaddress(const UniValue& params, bool fHelp)
|
||||
UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"z_getbalanceforaddress \"address\" ( minconf )\n"
|
||||
"\nReturns the per-pool balances of a Unified Address belonging to the node's wallet."
|
||||
"z_getbalanceforviewingkey \"fvk\" ( minconf )\n"
|
||||
"\nReturns the per-pool balances viewable by a full viewing key known to the node's wallet."
|
||||
"\nSprout viewing keys may be used only if the wallet controls the corresponding spending key."
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string) The selected address. It may be a transparent or shielded address.\n"
|
||||
"1. \"fvk\" (string) The selected full viewing key.\n"
|
||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"pools\": {\n"
|
||||
" \"transparent\": {\n"
|
||||
" \"valueZat\": amount (numeric) The amount held in the transparent pool by this account\n"
|
||||
" \"valueZat\": amount (numeric) The amount held in the transparent pool viewable by this fvk\n"
|
||||
" \"},\n"
|
||||
" \"sprout\": {\n"
|
||||
" \"valueZat\": amount (numeric) The amount held in the sprout pool by this account\n"
|
||||
" \"valueZat\": amount (numeric) The amount held in the sprout pool viewable by this fvk\n"
|
||||
" \"},\n"
|
||||
" \"sapling\": {\n"
|
||||
" \"valueZat\": amount (numeric) The amount held in the sapling pool by this account\n"
|
||||
" \"valueZat\": amount (numeric) The amount held in the sapling pool viewable by this fvk\n"
|
||||
" \"},\n"
|
||||
" \"orchard\": {\n"
|
||||
" \"valueZat\": amount (numeric) The amount held in the orchard pool by this account\n"
|
||||
" \"valueZat\": amount (numeric) The amount held in the orchard pool viewable by this fvk\n"
|
||||
" \"}\n"
|
||||
" \"},\n"
|
||||
" \"minimum_confirmations\": n (numeric) The given minconf argument\n"
|
||||
|
@ -3651,12 +3652,12 @@ UniValue z_getbalanceforaddress(const UniValue& params, bool fHelp)
|
|||
"Result amounts are in units of " + MINOR_CURRENCY_UNIT + ".\n"
|
||||
"Pools for which the balance is zero are not shown.\n"
|
||||
"\nExamples:\n"
|
||||
"\nThe per-pool amount received by address \"myaddress\" with at least 1 block confirmed\n"
|
||||
+ HelpExampleCli("z_getbalanceforaddress", "\"myaddress\"") +
|
||||
"\nThe per-pool amount received by address \"myaddress\" with at least 5 blocks confirmed\n"
|
||||
+ HelpExampleCli("z_getbalanceforaddress", "\"myaddress\" 5") +
|
||||
"\nThe per-pool amount viewable by key \"myfvk\" with at least 1 block confirmed\n"
|
||||
+ HelpExampleCli("z_getbalanceforviewingkey", "\"myfvk\"") +
|
||||
"\nThe per-pool amount viewable by key \"myfvk\" with at least 5 blocks confirmed\n"
|
||||
+ HelpExampleCli("z_getbalanceforviewingkey", "\"myfvk\" 5") +
|
||||
"\nAs a JSON RPC call\n"
|
||||
+ HelpExampleRpc("z_getbalanceforaddress", "\"myaddress\", 5")
|
||||
+ HelpExampleRpc("z_getbalanceforviewingkey", "\"myfvk\", 5")
|
||||
);
|
||||
|
||||
if (!fExperimentalOrchardWallet) {
|
||||
|
@ -3664,11 +3665,11 @@ UniValue z_getbalanceforaddress(const UniValue& params, bool fHelp)
|
|||
}
|
||||
|
||||
KeyIO keyIO(Params());
|
||||
auto decoded = keyIO.DecodePaymentAddress(params[0].get_str());
|
||||
auto decoded = keyIO.DecodeViewingKey(params[0].get_str());
|
||||
if (!decoded.has_value()) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid full viewing key");
|
||||
}
|
||||
auto address = decoded.value();
|
||||
auto fvk = decoded.value();
|
||||
|
||||
int minconf = 1;
|
||||
if (params.size() > 1) {
|
||||
|
@ -3680,14 +3681,17 @@ UniValue z_getbalanceforaddress(const UniValue& params, bool fHelp)
|
|||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
// Get the receivers for this address.
|
||||
auto selector = pwalletMain->ToZTXOSelector(address, false);
|
||||
// Sprout viewing keys cannot provide accurate balance information because they
|
||||
// cannot detect spends, so we require that the wallet control the spending key
|
||||
// in the case that a Sprout viewing key is provided. Sapling and unified
|
||||
// FVKs make it possible to correctly determine balance without having the
|
||||
// spending key, so we permit that here.
|
||||
bool requireSpendingKey = std::holds_alternative<libzcash::SproutViewingKey>(fvk);
|
||||
auto selector = pwalletMain->ZTXOSelectorForViewingKey(fvk, requireSpendingKey);
|
||||
if (!selector.has_value()) {
|
||||
// The only way we'd reach this is if the address is a unified address for which
|
||||
// we do not know its UFVK.
|
||||
throw JSONRPCError(
|
||||
RPC_INVALID_PARAMETER,
|
||||
"Error: wallet does not have the Unified Full Viewing Key for the given address");
|
||||
"Error: the wallet does not recognize the specified viewing key.");
|
||||
}
|
||||
|
||||
auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, minconf);
|
||||
|
@ -4278,11 +4282,20 @@ size_t EstimateTxSize(
|
|||
[&](const CScriptID& scriptId) {
|
||||
return true;
|
||||
},
|
||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
||||
return ufvk.GetTransparentKey().has_value();
|
||||
},
|
||||
[&](const libzcash::SproutPaymentAddress& addr) {
|
||||
return false;
|
||||
},
|
||||
[&](const libzcash::SproutViewingKey& addr) {
|
||||
return false;
|
||||
},
|
||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||
return false;
|
||||
},
|
||||
[&](const libzcash::SaplingExtendedFullViewingKey& addr) {
|
||||
return false;
|
||||
}
|
||||
}, ztxoSelector.GetPattern());
|
||||
|
||||
|
@ -4380,7 +4393,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
|||
auto fromaddress = params[0].get_str();
|
||||
ZTXOSelector ztxoSelector = [&]() {
|
||||
if (fromaddress == "ANY_TADDR") {
|
||||
return CWallet::LegacyTransparentZTXOSelector();
|
||||
return CWallet::LegacyTransparentZTXOSelector(true);
|
||||
} else {
|
||||
auto decoded = keyIO.DecodePaymentAddress(fromaddress);
|
||||
if (!decoded.has_value()) {
|
||||
|
@ -4389,7 +4402,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
|||
"Invalid from address: should be a taddr, a zaddr, or the string 'ANY_TADDR'.");
|
||||
}
|
||||
|
||||
auto ztxoSelectorOpt = pwalletMain->ToZTXOSelector(decoded.value(), true);
|
||||
auto ztxoSelectorOpt = pwalletMain->ZTXOSelectorForAddress(decoded.value(), true);
|
||||
if (!ztxoSelectorOpt.has_value()) {
|
||||
throw JSONRPCError(
|
||||
RPC_INVALID_ADDRESS_OR_KEY,
|
||||
|
@ -5542,7 +5555,7 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "z_listunspent", &z_listunspent, false },
|
||||
{ "wallet", "z_getbalance", &z_getbalance, false },
|
||||
{ "wallet", "z_gettotalbalance", &z_gettotalbalance, false },
|
||||
{ "wallet", "z_getbalanceforaddress", &z_getbalanceforaddress, false },
|
||||
{ "wallet", "z_getbalanceforviewingkey",&z_getbalanceforviewingkey,false },
|
||||
{ "wallet", "z_getbalanceforaccount", &z_getbalanceforaccount, false },
|
||||
{ "wallet", "z_mergetoaddress", &z_mergetoaddress, false },
|
||||
{ "wallet", "z_sendmany", &z_sendmany, false },
|
||||
|
|
|
@ -801,7 +801,7 @@ void CheckHaveAddr(const std::optional<libzcash::PaymentAddress>& addr) {
|
|||
auto addr_of_type = std::get_if<ADDR_TYPE>(&(addr.value()));
|
||||
BOOST_ASSERT(addr_of_type != nullptr);
|
||||
|
||||
BOOST_CHECK(pwalletMain->ToZTXOSelector(*addr_of_type, true).has_value());
|
||||
BOOST_CHECK(pwalletMain->ZTXOSelectorForAddress(*addr_of_type, true).has_value());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) {
|
||||
|
@ -1235,7 +1235,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
|
|||
|
||||
// there are no utxos to spend
|
||||
{
|
||||
auto selector = pwalletMain->ToZTXOSelector(taddr1, true).value();
|
||||
auto selector = pwalletMain->ZTXOSelectorForAddress(taddr1, true).value();
|
||||
TransactionBuilder builder(consensusParams, nHeight + 1, pwalletMain);
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 100*COIN, "DEADBEEF") };
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, selector, recipients, 1));
|
||||
|
@ -1247,7 +1247,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
|
|||
|
||||
// there are no unspent notes to spend
|
||||
{
|
||||
auto selector = pwalletMain->ToZTXOSelector(zaddr1, true).value();
|
||||
auto selector = pwalletMain->ZTXOSelectorForAddress(zaddr1, true).value();
|
||||
TransactionBuilder builder(consensusParams, nHeight + 1, pwalletMain);
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(taddr1, 100*COIN, "DEADBEEF") };
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, selector, recipients, 1));
|
||||
|
@ -1259,7 +1259,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
|
|||
|
||||
// get_memo_from_hex_string())
|
||||
{
|
||||
auto selector = pwalletMain->ToZTXOSelector(zaddr1, true).value();
|
||||
auto selector = pwalletMain->ZTXOSelectorForAddress(zaddr1, true).value();
|
||||
TransactionBuilder builder(consensusParams, nHeight + 1, pwalletMain);
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 100*COIN, "DEADBEEF") };
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, selector, recipients, 1));
|
||||
|
@ -1360,7 +1360,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
|
|||
auto builder = TransactionBuilder(consensusParams, nextBlockHeight, pwalletMain);
|
||||
mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight);
|
||||
|
||||
auto selector = pwalletMain->ToZTXOSelector(taddr, true).value();
|
||||
auto selector = pwalletMain->ZTXOSelectorForAddress(taddr, true).value();
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(pa, 1*COIN, "ABCD") };
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(builder, selector, recipients, 0));
|
||||
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
|
||||
|
|
|
@ -1466,7 +1466,7 @@ std::optional<ZTXOSelector> CWallet::ZTXOSelectorForAccount(
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<ZTXOSelector> CWallet::ToZTXOSelector(const libzcash::PaymentAddress& addr, bool requireSpendingKey) const {
|
||||
std::optional<ZTXOSelector> CWallet::ZTXOSelectorForAddress(const libzcash::PaymentAddress& addr, bool requireSpendingKey) const {
|
||||
auto self = this;
|
||||
std::optional<ZTXOPattern> pattern = std::nullopt;
|
||||
std::visit(match {
|
||||
|
@ -1513,10 +1513,45 @@ std::optional<ZTXOSelector> CWallet::ToZTXOSelector(const libzcash::PaymentAddre
|
|||
}
|
||||
}
|
||||
|
||||
ZTXOSelector CWallet::LegacyTransparentZTXOSelector() {
|
||||
std::optional<ZTXOSelector> CWallet::ZTXOSelectorForViewingKey(
|
||||
const libzcash::ViewingKey& vk,
|
||||
bool requireSpendingKey) const
|
||||
{
|
||||
auto self = this;
|
||||
std::optional<ZTXOPattern> pattern = std::nullopt;
|
||||
std::visit(match {
|
||||
[&](const libzcash::SaplingExtendedFullViewingKey& vk) {
|
||||
if (!requireSpendingKey || self->HaveSaplingSpendingKey(vk)) {
|
||||
pattern = vk;
|
||||
}
|
||||
},
|
||||
[&](const libzcash::SproutViewingKey& vk) {
|
||||
if (!requireSpendingKey || self->HaveSproutSpendingKey(vk.address())) {
|
||||
pattern = vk;
|
||||
}
|
||||
},
|
||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
||||
auto ufvkId = ufvk.GetKeyID(Params());
|
||||
auto accountId = this->GetUnifiedAccountId(ufvkId);
|
||||
if (accountId.has_value()) {
|
||||
pattern = AccountZTXOPattern(accountId.value(), ufvk.GetKnownReceiverTypes());
|
||||
} else {
|
||||
pattern = ufvk;
|
||||
}
|
||||
}
|
||||
}, vk);
|
||||
|
||||
if (pattern.has_value()) {
|
||||
return ZTXOSelector(pattern.value(), requireSpendingKey);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
ZTXOSelector CWallet::LegacyTransparentZTXOSelector(bool requireSpendingKey) {
|
||||
return ZTXOSelector(
|
||||
AccountZTXOPattern(ZCASH_LEGACY_ACCOUNT, {ReceiverType::P2PKH, ReceiverType::P2SH}),
|
||||
true);
|
||||
requireSpendingKey);
|
||||
}
|
||||
|
||||
std::optional<libzcash::AccountId> CWallet::FindAccountForSelector(const ZTXOSelector& selector) const {
|
||||
|
@ -1536,12 +1571,22 @@ std::optional<libzcash::AccountId> CWallet::FindAccountForSelector(const ZTXOSel
|
|||
}
|
||||
},
|
||||
[&](const libzcash::SproutPaymentAddress& addr) { },
|
||||
[&](const libzcash::SproutViewingKey& vk) { },
|
||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||
auto meta = GetUFVKMetadataForReceiver(addr);
|
||||
if (meta.has_value()) {
|
||||
result = self->GetUnifiedAccountId(meta.value().first);
|
||||
}
|
||||
},
|
||||
[&](const libzcash::SaplingExtendedFullViewingKey& vk) {
|
||||
auto ufvkid = GetUFVKIdForViewingKey(vk);
|
||||
if (ufvkid.has_value()) {
|
||||
result = self->GetUnifiedAccountId(ufvkid.value());
|
||||
}
|
||||
},
|
||||
[&](const libzcash::UnifiedFullViewingKey& vk) {
|
||||
result = self->GetUnifiedAccountId(vk.GetKeyID(Params()));
|
||||
},
|
||||
[&](const AccountZTXOPattern& acct) {
|
||||
if (self->mnemonicHDChain.has_value() &&
|
||||
self->mapUnifiedAccountKeys.count(
|
||||
|
@ -1554,6 +1599,8 @@ std::optional<libzcash::AccountId> CWallet::FindAccountForSelector(const ZTXOSel
|
|||
return result;
|
||||
}
|
||||
|
||||
// SelectorMatchesAddress is overloaded for:
|
||||
// Transparent
|
||||
bool CWallet::SelectorMatchesAddress(
|
||||
const ZTXOSelector& selector,
|
||||
const CTxDestination& address) const {
|
||||
|
@ -1568,7 +1615,17 @@ bool CWallet::SelectorMatchesAddress(
|
|||
return address == scriptIdDest;
|
||||
},
|
||||
[&](const libzcash::SproutPaymentAddress& addr) { return false; },
|
||||
[&](const libzcash::SproutViewingKey& vk) { return false; },
|
||||
[&](const libzcash::SaplingPaymentAddress& addr) { return false; },
|
||||
[&](const libzcash::SaplingExtendedFullViewingKey& extfvk) { return false; },
|
||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>> meta;
|
||||
std::visit(match {
|
||||
[&](const CNoDestination& none) { meta = std::nullopt; },
|
||||
[&](const auto& addr) { meta = self->GetUFVKMetadataForReceiver(addr); }
|
||||
}, address);
|
||||
return (meta.has_value() && meta.value().first == ufvk.GetKeyID(Params()));
|
||||
},
|
||||
[&](const AccountZTXOPattern& acct) {
|
||||
if (acct.IncludesP2PKH() || acct.IncludesP2SH()) {
|
||||
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>> meta;
|
||||
|
@ -1591,16 +1648,17 @@ bool CWallet::SelectorMatchesAddress(
|
|||
}
|
||||
}, selector.GetPattern());
|
||||
}
|
||||
|
||||
// Sprout
|
||||
bool CWallet::SelectorMatchesAddress(
|
||||
const ZTXOSelector& selector,
|
||||
const libzcash::SproutPaymentAddress& a0) const {
|
||||
return std::visit(match {
|
||||
[&](const libzcash::SproutPaymentAddress& a1) { return a0 == a1; },
|
||||
[&](const libzcash::SproutViewingKey& vk) { return a0 == vk.address(); },
|
||||
[&](const auto& addr) { return false; },
|
||||
}, selector.GetPattern());
|
||||
}
|
||||
|
||||
// Sapling
|
||||
bool CWallet::SelectorMatchesAddress(
|
||||
const ZTXOSelector& selector,
|
||||
const libzcash::SaplingPaymentAddress& a0) const {
|
||||
|
@ -1609,7 +1667,25 @@ bool CWallet::SelectorMatchesAddress(
|
|||
[&](const CKeyID& keyId) { return false; },
|
||||
[&](const CScriptID& scriptId) { return false; },
|
||||
[&](const libzcash::SproutPaymentAddress& addr) { return false; },
|
||||
[&](const libzcash::SaplingPaymentAddress& a1) { return a0 == a1; },
|
||||
[&](const libzcash::SproutViewingKey& vk) { return false; },
|
||||
[&](const libzcash::SaplingPaymentAddress& a1) {
|
||||
return a0 == a1;
|
||||
},
|
||||
[&](const libzcash::SaplingExtendedFullViewingKey& extfvk) {
|
||||
auto j = extfvk.DecryptDiversifier(a0.d);
|
||||
auto addr = extfvk.Address(j);
|
||||
return addr.has_value() && addr.value() == a0;
|
||||
},
|
||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
||||
auto saplingKey = ufvk.GetSaplingKey();
|
||||
if (saplingKey.has_value()) {
|
||||
auto j = saplingKey.value().DecryptDiversifier(a0.d);
|
||||
auto addr = saplingKey.value().Address(j);
|
||||
return addr.has_value() && addr.value() == a0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[&](const AccountZTXOPattern& acct) {
|
||||
if (acct.IncludesSapling()) {
|
||||
const auto meta = self->GetUFVKMetadataForReceiver(a0);
|
||||
|
@ -6218,8 +6294,11 @@ std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
|
|||
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
|
||||
const libzcash::UnifiedAddress &uaddr) const
|
||||
{
|
||||
// TODO
|
||||
return std::nullopt;
|
||||
auto ufvkid = m_wallet->FindUnifiedFullViewingKey(uaddr);
|
||||
if (!ufvkid.has_value()) return std::nullopt;
|
||||
auto zufvk = m_wallet->GetUnifiedFullViewingKey(ufvkid.value());
|
||||
if (!zufvk.has_value()) return std::nullopt;
|
||||
return zufvk.value().ToFullViewingKey();
|
||||
}
|
||||
|
||||
// AddViewingKeyToWallet
|
||||
|
@ -6355,33 +6434,25 @@ std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(co
|
|||
if (ufvkPair.has_value()) {
|
||||
auto ufvkid = ufvkPair.value().first;
|
||||
auto ufvk = wallet.GetUnifiedFullViewingKey(ufvkid);
|
||||
assert(ufvk.has_value() && ufvk.value().GetSaplingKey().has_value());
|
||||
assert(ufvk.has_value());
|
||||
|
||||
diversifier_index_t j;
|
||||
// If the wallet is missing metadata at this UFVK id, it is probably
|
||||
// corrupt and the node should shut down.
|
||||
const auto& metadata = wallet.mapUfvkAddressMetadata.at(ufvkid);
|
||||
librustzcash_sapling_diversifier_index(
|
||||
ufvk.value().GetSaplingKey().value().dk.begin(),
|
||||
saplingAddr.d.begin(),
|
||||
j.begin());
|
||||
auto saplingKey = ufvk.value().GetSaplingKey();
|
||||
if (saplingKey.has_value()) {
|
||||
diversifier_index_t j = saplingKey.value().DecryptDiversifier(saplingAddr.d);
|
||||
auto receivers = metadata.GetReceivers(j);
|
||||
if (receivers.has_value()) {
|
||||
auto addr = ufvk.value().Address(j, receivers.value());
|
||||
auto addrPtr = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&addr);
|
||||
if (addrPtr == nullptr) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
if (addrPtr != nullptr) {
|
||||
return addrPtr->first;
|
||||
}
|
||||
} else {
|
||||
// If we don't know the receiver types at which the address was originally
|
||||
// generated, we can't reconstruct the address.
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(const CScriptID& scriptId) const {
|
||||
// We do not currently generate unified addresses containing P2SH components,
|
||||
|
@ -6429,16 +6500,25 @@ bool ZTXOSelector::SelectsTransparent() {
|
|||
[](const CKeyID& keyId) { return true; },
|
||||
[](const CScriptID& scriptId) { return true; },
|
||||
[](const libzcash::SproutPaymentAddress& addr) { return false; },
|
||||
[](const libzcash::SproutViewingKey& vk) { return false; },
|
||||
[](const libzcash::SaplingPaymentAddress& addr) { return false; },
|
||||
[](const libzcash::SaplingExtendedFullViewingKey& vk) { return false; },
|
||||
[](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetTransparentKey().has_value(); },
|
||||
[](const AccountZTXOPattern& acct) { return acct.IncludesP2PKH() || acct.IncludesP2SH(); }
|
||||
}, this->pattern);
|
||||
}
|
||||
bool ZTXOSelector::SelectsSprout() {
|
||||
return std::holds_alternative<libzcash::SproutPaymentAddress>(this->pattern);
|
||||
return std::visit(match {
|
||||
[](const libzcash::SproutViewingKey& addr) { return true; },
|
||||
[](const libzcash::SproutPaymentAddress& extfvk) { return true; },
|
||||
[](const auto& addr) { return false; }
|
||||
}, this->pattern);
|
||||
}
|
||||
bool ZTXOSelector::SelectsSapling() {
|
||||
return std::visit(match {
|
||||
[](const libzcash::SaplingPaymentAddress& addr) { return true; },
|
||||
[](const libzcash::SaplingExtendedSpendingKey& extfvk) { return true; },
|
||||
[](const libzcash::UnifiedFullViewingKey& ufvk) { return ufvk.GetSaplingKey().has_value(); },
|
||||
[](const AccountZTXOPattern& acct) { return acct.IncludesSapling(); },
|
||||
[](const auto& addr) { return false; }
|
||||
}, this->pattern);
|
||||
|
|
|
@ -738,7 +738,10 @@ typedef std::variant<
|
|||
CKeyID,
|
||||
CScriptID,
|
||||
libzcash::SproutPaymentAddress,
|
||||
libzcash::SproutViewingKey,
|
||||
libzcash::SaplingPaymentAddress,
|
||||
libzcash::SaplingExtendedFullViewingKey,
|
||||
libzcash::UnifiedFullViewingKey,
|
||||
AccountZTXOPattern> ZTXOPattern;
|
||||
|
||||
class ZTXOSelector {
|
||||
|
@ -1220,7 +1223,7 @@ public:
|
|||
static bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet);
|
||||
|
||||
/**
|
||||
* Obtain the ZTXO selector for the specified account ID.
|
||||
* Returns the ZTXO selector for the specified account ID.
|
||||
*
|
||||
* Returns `std::nullopt` if the account ID has not been generated yet by
|
||||
* the wallet.
|
||||
|
@ -1234,15 +1237,30 @@ public:
|
|||
std::set<libzcash::ReceiverType> receiverTypes={}) const;
|
||||
|
||||
/**
|
||||
* Obtain the ZTXO selector for the specified payment address. If the
|
||||
* `requireSpendingKey` flag is set, this will only return a selector
|
||||
* that will choose outputs for which this wallet holds the spending keys.
|
||||
* Returns the ZTXO selector for the specified payment address, if the
|
||||
* address is known to the wallet. If the `requireSpendingKey` flag is set,
|
||||
* this will only return a selector that will choose outputs for which this
|
||||
* wallet holds the spending keys.
|
||||
*/
|
||||
std::optional<ZTXOSelector> ToZTXOSelector(
|
||||
std::optional<ZTXOSelector> ZTXOSelectorForAddress(
|
||||
const libzcash::PaymentAddress& addr,
|
||||
bool requireSpendingKey) const;
|
||||
|
||||
static ZTXOSelector LegacyTransparentZTXOSelector();
|
||||
/**
|
||||
* Returns the ZTXO selector for the specified viewing key, if that key
|
||||
* is known to the wallet. If the `requireSpendingKey` flag is set, this
|
||||
* will only return a selector that will choose outputs for which this
|
||||
* wallet holds the spending keys.
|
||||
*/
|
||||
std::optional<ZTXOSelector> ZTXOSelectorForViewingKey(
|
||||
const libzcash::ViewingKey& vk,
|
||||
bool requireSpendingKey) const;
|
||||
|
||||
/**
|
||||
* Returns the ZTXO selector that will select UTXOs sent to legacy
|
||||
* transparent addresses managed by this wallet.
|
||||
*/
|
||||
static ZTXOSelector LegacyTransparentZTXOSelector(bool requireSpendingKey);
|
||||
|
||||
/**
|
||||
* Look up the account for a given selector. This resolves the account ID
|
||||
|
|
|
@ -177,6 +177,17 @@ public:
|
|||
|
||||
UFVKId GetKeyID(const KeyConstants& keyConstants) const;
|
||||
|
||||
std::set<ReceiverType> GetKnownReceiverTypes() const {
|
||||
std::set<ReceiverType> result;
|
||||
if (GetTransparentKey().has_value()) {
|
||||
result.insert(ReceiverType::P2PKH);
|
||||
}
|
||||
if (GetSaplingKey().has_value()) {
|
||||
result.insert(ReceiverType::Sapling);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
UnifiedFullViewingKey& operator=(UnifiedFullViewingKey&& key)
|
||||
{
|
||||
if (this != &key) {
|
||||
|
|
|
@ -157,3 +157,31 @@ std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress(co
|
|||
}, req);
|
||||
return addr;
|
||||
}
|
||||
|
||||
std::optional<RecipientAddress> ZcashdUnifiedFullViewingKey::GetChangeAddress() const {
|
||||
if (saplingKey.has_value()) {
|
||||
return saplingKey.value().GetChangeAddress();
|
||||
}
|
||||
if (transparentKey.has_value()) {
|
||||
auto changeAddr = transparentKey.value().GetChangeAddress(diversifier_index_t(0));
|
||||
if (changeAddr.has_value()) {
|
||||
return changeAddr.value();
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
UnifiedFullViewingKey ZcashdUnifiedFullViewingKey::ToFullViewingKey() const {
|
||||
UnifiedFullViewingKeyBuilder builder;
|
||||
|
||||
if (transparentKey.has_value()) {
|
||||
builder.AddTransparentKey(transparentKey.value());
|
||||
}
|
||||
if (saplingKey.has_value()) {
|
||||
builder.AddSaplingKey(saplingKey.value());
|
||||
}
|
||||
|
||||
// This call to .value() is safe as ZcashdUnifiedFullViewingKey values are always
|
||||
// constructed to contain all required components.
|
||||
return builder.build().value();
|
||||
}
|
||||
|
|
|
@ -214,6 +214,18 @@ public:
|
|||
*/
|
||||
std::optional<RecipientAddress> GetChangeAddress(const ChangeRequest& req) const;
|
||||
|
||||
/**
|
||||
* Return the "best available" change address. This returns `std::nullopt`
|
||||
* only in the case of derivation failure for all key types. This will only
|
||||
* return a transparent change address under highly exceptional
|
||||
* circumstances, (i.e. it was not possible to derive a change address for
|
||||
* *any* shielded pool) in which case the change address returned will be
|
||||
* associated with diversifier index 0.
|
||||
*/
|
||||
std::optional<RecipientAddress> GetChangeAddress() const;
|
||||
|
||||
UnifiedFullViewingKey ToFullViewingKey() const;
|
||||
|
||||
friend bool operator==(const ZcashdUnifiedFullViewingKey& a, const ZcashdUnifiedFullViewingKey& b)
|
||||
{
|
||||
return a.transparentKey == b.transparentKey && a.saplingKey == b.saplingKey;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "uint256.h"
|
||||
#include "utiltime.h"
|
||||
#include "zcash/address/sapling.hpp"
|
||||
#include <librustzcash.h>
|
||||
#include <rust/zip339.h>
|
||||
|
||||
#include <optional>
|
||||
|
@ -171,6 +172,12 @@ public:
|
|||
*/
|
||||
std::pair<uint256, uint256> GetOVKs() const;
|
||||
|
||||
diversifier_index_t DecryptDiversifier(const diversifier_t& d) const {
|
||||
diversifier_index_t j;
|
||||
librustzcash_sapling_diversifier_index(dk.begin(), d.begin(), j.begin());
|
||||
return j;
|
||||
}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
|
|
Loading…
Reference in New Issue