Categorize listaddresses result by source type.

Addresses managed by the zcashd wallet may be generated using
multiple different sources of randomness and/or the wallet's
HD seeds. Depending upon how addresses are generated, they may
divided into multiple sets, each associated with a separate
spend authority and treated as an independent pool of funds.
In the future, the root spend authority for a wallet will
be a mnemonic seed phrase; the API represented by this PR
anticipates that future and attempts to provide functionality
that will serve both current and future needs, as well as the
transition between them.
This commit is contained in:
Kris Nuttycombe 2021-10-06 13:48:15 -06:00
parent 64ecf700fc
commit 9a11ac73a4
1 changed files with 157 additions and 58 deletions

View File

@ -313,22 +313,37 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
if (fHelp) if (fHelp)
throw runtime_error( throw runtime_error(
"listaddresses\n" "listaddresses\n"
"\nLists the addresses tracked by this wallet.\n" "\nLists the addresses managed by this wallet by source, including \n"
"those generated from randomness by this wallet, Sapling addresses \n"
"generated from the legacy HD seed, imported watchonly transparent \n"
"addresses, shielded addresses tracked using imported viewing keys, \n"
"and addresses derived from the wallet's mnemonic seed for releases \n"
"version 5.0.0 and above. \n"
"\nREMINDER: It is recommended that you back up your wallet.dat file \n"
"regularly!\n"
"\nResult:\n" "\nResult:\n"
"[\n" "[\n"
" [\n"
" {\n" " {\n"
" \"transparent\": { \"address\": \"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"watchonly\": false },\n" " \"source\": \"imported|imported_watchonly|keypool|legacy_seed|mnemnoic_seed\"\n"
" }\n" " \"transparent\": {\n"
" \"addresses\": [\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", ...],\n"
" \"change_addresses\": [\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", ...]\n"
" },\n"
" \"sprout\": {\n"
" \"addresses\": [\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\", ...]\n"
" },\n"
" \"sapling\": [\n"
" {\n" " {\n"
" \"sprout\": { \"address\": \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\"},\n" " \"zip32_account_id\": 0, -- optional field, not present for imported/watchonly sources,\n"
" }\n" " \"addresses\": [\n"
" {\n" " \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\",\n"
" \"sapling\": { \"address\": \"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\"},\n" " ...\n"
" ]\n"
" }\n" " }\n"
" ,...\n" " ,...\n"
" ]\n" " ]\n"
" ,...\n" " },\n"
" ...\n"
"]\n" "]\n"
"\nExamples:\n" "\nExamples:\n"
+ HelpExampleCli("listaddresses", "") + HelpExampleCli("listaddresses", "")
@ -341,12 +356,14 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
{ // keypool-derived and imported/watchonly transparent addresses
std::set<CTxDestination> transparentDests; std::set<CTxDestination> t_generated_dests;
std::set<CTxDestination> t_change_dests;
std::set<CTxDestination> t_watchonly_dests;
// Get the CTxDestination values for all the entries in the transparent address book. // Get the CTxDestination values for all the entries in the transparent address book.
// This will include any address that has been generated by this wallet. // This will include any address that has been generated by this wallet.
for (const std::pair<CTxDestination, CAddressBookData>& item : pwalletMain->mapAddressBook) { for (const std::pair<CTxDestination, CAddressBookData>& item : pwalletMain->mapAddressBook) {
transparentDests.insert(item.first); t_generated_dests.insert(item.first);
} }
// Ensure we have every address that holds a balance. While this is likely to be redundant // Ensure we have every address that holds a balance. While this is likely to be redundant
@ -354,46 +371,128 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
// there is not a guarantee that an externally generated address (such as one associated with // there is not a guarantee that an externally generated address (such as one associated with
// a future unified incoming viewing key) will have been added to the address book. // a future unified incoming viewing key) will have been added to the address book.
for (const std::pair<CTxDestination, CAmount>& item : pwalletMain->GetAddressBalances()) { for (const std::pair<CTxDestination, CAmount>& item : pwalletMain->GetAddressBalances()) {
transparentDests.insert(item.first); auto script = GetScriptForDestination(item.first);
} if (pwalletMain->HaveWatchOnly(script)) {
t_watchonly_dests.insert(item.first);
for (const CTxDestination& dest : transparentDests) { } else if (t_generated_dests.count(item.first) == 0) {
UniValue payload(UniValue::VOBJ); // assume that if we didn't add the address to the addrbook
payload.pushKV("address", keyIO.EncodeDestination(dest)); // that it's a change address. Ideally we'd have a better way
// of checking this by exploring the transaction graph;
auto script = GetScriptForDestination(dest); t_change_dests.insert(item.first);
payload.pushKV("watchonly", pwalletMain->HaveWatchOnly(script)); } else {
// already accounted for in the address book
UniValue entry(UniValue::VOBJ);
entry.pushKV("transparent", payload);
ret.push_back(entry);
} }
} }
{
/// sprout addresses
std::set<libzcash::SproutPaymentAddress> sproutAddresses; std::set<libzcash::SproutPaymentAddress> sproutAddresses;
pwalletMain->GetSproutPaymentAddresses(sproutAddresses); pwalletMain->GetSproutPaymentAddresses(sproutAddresses);
for (const SproutPaymentAddress& addr : sproutAddresses) {
UniValue payload(UniValue::VOBJ);
payload.pushKV("address", keyIO.EncodePaymentAddress(addr));
payload.pushKV("watchonly", HaveSpendingKeyForPaymentAddress(pwalletMain)(addr));
UniValue entry(UniValue::VOBJ); /// sapling addresses
entry.pushKV("sprout", payload);
ret.push_back(entry);
}
}
{
std::set<libzcash::SaplingPaymentAddress> saplingAddresses; std::set<libzcash::SaplingPaymentAddress> saplingAddresses;
pwalletMain->GetSaplingPaymentAddresses(saplingAddresses); pwalletMain->GetSaplingPaymentAddresses(saplingAddresses);
for (const SaplingPaymentAddress& addr : saplingAddresses) {
UniValue payload(UniValue::VOBJ); // legacy_random source
payload.pushKV("address", keyIO.EncodePaymentAddress(addr)); {
payload.pushKV("watchonly", HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)); // Add legacy randomly generated address records to the result.
// This includes transparent addresses generated by the wallet via
// the keypool and Sprout addresses for which we have the
// spending key.
UniValue random_t_addrs(UniValue::VARR);
for (const CTxDestination& dest : t_generated_dests) {
random_t_addrs.push_back(keyIO.EncodeDestination(dest));
}
UniValue random_t_change_addrs(UniValue::VARR);
for (const CTxDestination& dest : t_change_dests) {
random_t_change_addrs.push_back(keyIO.EncodeDestination(dest));
}
UniValue random_sprout_addrs(UniValue::VARR);
for (const SproutPaymentAddress& addr : sproutAddresses) {
if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
random_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr));
}
}
/// keypool source only applies to transparent transactions
UniValue random_t(UniValue::VOBJ);
random_t.pushKV("addresses", random_t_addrs);
random_t.pushKV("change_addresses", random_t_change_addrs);
UniValue random_sprout(UniValue::VOBJ);
random_sprout.pushKV("addresses", random_sprout_addrs);
UniValue entry(UniValue::VOBJ); UniValue entry(UniValue::VOBJ);
entry.pushKV("sapling", payload); entry.pushKV("source", "legacy_random");
entry.pushKV("transparent", random_t);
entry.pushKV("sprout", random_sprout);
ret.push_back(entry); ret.push_back(entry);
} }
/// imported_watchonly source
{
UniValue watchonly_t_addrs(UniValue::VARR);
for (const CTxDestination& dest: t_watchonly_dests) {
watchonly_t_addrs.push_back(keyIO.EncodeDestination(dest));
}
UniValue watchonly_sprout_addrs(UniValue::VARR);
for (const SproutPaymentAddress& addr : sproutAddresses) {
if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
watchonly_sprout_addrs.push_back(keyIO.EncodePaymentAddress(addr));
}
}
UniValue watchonly_sapling_addrs(UniValue::VARR);
for (const SaplingPaymentAddress& addr : saplingAddresses) {
if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
watchonly_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr));
}
}
UniValue watchonly_t(UniValue::VOBJ);
watchonly_t.pushKV("addresses", watchonly_t_addrs);
UniValue watchonly_sprout(UniValue::VOBJ);
watchonly_sprout.pushKV("addresses", watchonly_sprout_addrs);
UniValue watchonly_sapling_obj(UniValue::VOBJ);
watchonly_sapling_obj.pushKV("addresses", watchonly_sapling_addrs);
UniValue watchonly_sapling(UniValue::VARR);
watchonly_sapling.push_back(watchonly_sapling_obj);
UniValue entry(UniValue::VOBJ);
entry.pushKV("source", "imported_watchonly");
entry.pushKV("transparent", watchonly_t);
entry.pushKV("sprout", watchonly_sprout);
entry.pushKV("sapling", watchonly_sapling);
ret.push_back(entry);
}
/// legacy_hdseed source
{
// TODO: split up by zip32 account id
UniValue legacy_sapling_addrs(UniValue::VARR);
for (const SaplingPaymentAddress& addr : saplingAddresses) {
if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
legacy_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr));
}
}
UniValue legacy_sapling_obj(UniValue::VOBJ);
legacy_sapling_obj.pushKV("addresses", legacy_sapling_addrs);
UniValue legacy_sapling(UniValue::VARR);
legacy_sapling.push_back(legacy_sapling_obj);
UniValue entry(UniValue::VOBJ);
entry.pushKV("source", "legacy_hdseed");
entry.pushKV("sapling", legacy_sapling);
ret.push_back(entry);
} }
return ret; return ret;