wallet: Show UAs instead of Sapling receivers in `z_listunspent`

We also add a `type` field to the output objects (matching the field in
`z_viewtransaction`), now that the output type can't be distinguished
solely from the address encoding.
This commit is contained in:
Jack Grigg 2022-02-10 02:06:53 +00:00
parent 0a9c27e8f2
commit 88dde127f4
5 changed files with 58 additions and 20 deletions

View File

@ -46,6 +46,7 @@ class WalletListNotes(BitcoinTestFramework):
assert_equal(1, len(unspent_cb))
assert_equal(False, unspent_cb[0]['change'])
assert_equal(txid_1, unspent_cb[0]['txid'])
assert_equal('sprout', unspent_cb[0]['type'])
assert_equal(True, unspent_cb[0]['spendable'])
assert_equal(sproutzaddr, unspent_cb[0]['address'])
assert_equal(receive_amount_1, unspent_cb[0]['amount'])
@ -78,12 +79,14 @@ class WalletListNotes(BitcoinTestFramework):
unspent_tx = sorted(unspent_tx, key=lambda k: k['amount'])
assert_equal(False, unspent_tx[0]['change'])
assert_equal(txid_2, unspent_tx[0]['txid'])
assert_equal('sapling', unspent_tx[0]['type'])
assert_equal(True, unspent_tx[0]['spendable'])
assert_equal(saplingzaddr, unspent_tx[0]['address'])
assert_equal(receive_amount_2, unspent_tx[0]['amount'])
assert_equal(True, unspent_tx[1]['change'])
assert_equal(txid_2, unspent_tx[1]['txid'])
assert_equal('sprout', unspent_tx[1]['type'])
assert_equal(True, unspent_tx[1]['spendable'])
assert_equal(sproutzaddr, unspent_tx[1]['address'])
assert_equal(change_amount_2, unspent_tx[1]['amount'])
@ -115,18 +118,21 @@ class WalletListNotes(BitcoinTestFramework):
assert_equal(False, unspent_tx[0]['change'])
assert_equal(txid_2, unspent_tx[0]['txid'])
assert_equal('sapling', unspent_tx[0]['type'])
assert_equal(True, unspent_tx[0]['spendable'])
assert_equal(saplingzaddr, unspent_tx[0]['address'])
assert_equal(receive_amount_2, unspent_tx[0]['amount'])
assert_equal(False, unspent_tx[1]['change'])
assert_equal(txid_3, unspent_tx[1]['txid'])
assert_equal('sapling', unspent_tx[1]['type'])
assert_equal(True, unspent_tx[1]['spendable'])
assert_equal(saplingzaddr2, unspent_tx[1]['address'])
assert_equal(receive_amount_3, unspent_tx[1]['amount'])
assert_equal(True, unspent_tx[2]['change'])
assert_equal(txid_3, unspent_tx[2]['txid'])
assert_equal('sprout', unspent_tx[2]['type'])
assert_equal(True, unspent_tx[2]['spendable'])
assert_equal(sproutzaddr, unspent_tx[2]['address'])
assert_equal(change_amount_3, unspent_tx[2]['amount'])

View File

@ -26,6 +26,15 @@ class WalletShieldCoinbaseUANU5(WalletShieldCoinbaseTest):
assert_equal(balances['pools']['sapling']['valueZat'], expected * COIN)
# assert_equal(balances['pools']['orchard']['valueZat'], expected * COIN)
# While we're at it, check that z_listunspent only shows outputs with
# the Unified Address (not the Orchard receiver), and of the expected
# type.
unspent = node.z_listunspent(1, 999999, False, [self.addr])
assert_equal(
[{'type': 'sapling', 'address': self.addr} for _ in unspent],
[{'type': x['type'], 'address': x['address']} for x in unspent],
)
if __name__ == '__main__':
print("Test shielding to a unified address with NU5 activated")

View File

@ -24,6 +24,15 @@ class WalletShieldCoinbaseUASapling(WalletShieldCoinbaseTest):
assert_equal(balances['pools']['sapling']['valueZat'], expected * COIN)
assert('orchard' not in balances['pools'])
# While we're at it, check that z_listunspent only shows outputs with
# the Unified Address (not the Sapling receiver), and of the expected
# type.
unspent = node.z_listunspent(1, 999999, False, [self.addr])
assert_equal(
[{'type': 'sapling', 'address': self.addr} for _ in unspent],
[{'type': x['type'], 'address': x['address']} for x in unspent],
)
if __name__ == '__main__':
print("Test shielding to a unified address with sapling activated (but not NU5)")

View File

@ -129,6 +129,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
assert(len(results) == 0)
results = self.nodes[0].z_listunspent(0) # set minconf to zero
assert(len(results) == 1)
assert_equal(results[0]["type"], "sapling")
assert_equal(results[0]["address"], myzaddr)
assert_equal(results[0]["amount"], shieldvalue)
assert_equal(results[0]["confirmations"], 0)
@ -140,6 +141,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
# Verify that z_listunspent returns one note which has been confirmed
results = self.nodes[0].z_listunspent()
assert(len(results) == 1)
assert_equal(results[0]["type"], "sapling")
assert_equal(results[0]["address"], myzaddr)
assert_equal(results[0]["amount"], shieldvalue)
assert_equal(results[0]["confirmations"], 1)
@ -148,6 +150,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
# Verify that z_listunspent returns note for watchonly address on node 3.
results = self.nodes[3].z_listunspent(1, 999, True)
assert(len(results) == 1)
assert_equal(results[0]["type"], "sapling")
assert_equal(results[0]["address"], myzaddr)
assert_equal(results[0]["amount"], shieldvalue)
assert_equal(results[0]["confirmations"], 1)

View File

@ -2256,21 +2256,22 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
"Optionally filter to only include notes sent to specified addresses.\n"
"When minconf is 0, unspent notes with zero confirmations are returned, even though they are not immediately spendable.\n"
"Results are an array of Objects, each of which has:\n"
"{txid, jsindex, jsoutindex, confirmations, address, amount, memo} (Sprout)\n"
"{txid, outindex, confirmations, address, amount, memo} (Sapling)\n"
"{txid, type, jsindex, jsoutindex, confirmations, address, amount, memo} (Sprout)\n"
"{txid, type, outindex, confirmations, address, amount, memo} (Sapling)\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n"
"2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n"
"3. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n"
"4. \"addresses\" (string) A json array of zaddrs (both Sprout and Sapling) to filter on. Duplicate addresses not allowed.\n"
"4. \"addresses\" (string) A json array of shielded addresses to filter on. Duplicate addresses not allowed.\n"
" [\n"
" \"address\" (string) zaddr\n"
" \"address\" (string) Sprout, Sapling, or Unified address\n"
" ,...\n"
" ]\n"
"\nResult (output indices for only one pool will be present):\n"
"[ (array of json object)\n"
" {\n"
" \"txid\" : \"txid\", (string) the transaction id \n"
" \"type\" : \"sprout|sapling|orchard\", (string) The shielded pool\n"
" \"jsindex\" (sprout) : n, (numeric) the joinsplit index\n"
" \"jsoutindex\" (sprout) : n, (numeric) the output index of the joinsplit\n"
" \"outindex\" (sapling) : n, (numeric) the output index\n"
@ -2373,6 +2374,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
for (auto & entry : sproutEntries) {
UniValue obj(UniValue::VOBJ);
obj.pushKV("txid", entry.jsop.hash.ToString());
obj.pushKV("type", ADDR_TYPE_SPROUT);
obj.pushKV("jsindex", (int)entry.jsop.js );
obj.pushKV("jsoutindex", (int)entry.jsop.n);
obj.pushKV("confirmations", entry.confirmations);
@ -2391,12 +2393,19 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
for (auto & entry : saplingEntries) {
UniValue obj(UniValue::VOBJ);
obj.pushKV("txid", entry.op.hash.ToString());
obj.pushKV("type", ADDR_TYPE_SAPLING);
obj.pushKV("outindex", (int)entry.op.n);
obj.pushKV("confirmations", entry.confirmations);
bool hasSaplingSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(entry.address);
obj.pushKV("spendable", hasSaplingSpendingKey);
// TODO: If we found this entry via a UA, show that instead.
obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address));
obj.pushKV("address", keyIO.EncodePaymentAddress([&]() {
auto ua = pwalletMain->FindUnifiedAddressByReceiver(entry.address);
if (ua.has_value()) {
return libzcash::PaymentAddress{ua.value()};
} else {
return libzcash::PaymentAddress{entry.address};
}
}()));
obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); // note.value() is equivalent to plaintext.value()
obj.pushKV("memo", HexStr(entry.memo));
if (hasSaplingSpendingKey) {
@ -4067,13 +4076,14 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
// If the note belongs to a Sapling address that is part of an account in the
// wallet, show the corresponding Unified Address.
std::string address;
const auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa);
if (ua.has_value()) {
address = keyIO.EncodePaymentAddress(ua.value());
} else {
address = keyIO.EncodePaymentAddress(pa);
}
std::string address = keyIO.EncodePaymentAddress([&]() {
auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa);
if (ua.has_value()) {
return libzcash::PaymentAddress{ua.value()};
} else {
return libzcash::PaymentAddress{pa};
}
}());
UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_SAPLING);
@ -4120,13 +4130,14 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
// If the note belongs to a Sapling address that is part of an account in the
// wallet, show the corresponding Unified Address.
std::string address;
const auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa);
if (ua.has_value()) {
address = keyIO.EncodePaymentAddress(ua.value());
} else {
address = keyIO.EncodePaymentAddress(pa);
}
std::string address = keyIO.EncodePaymentAddress([&]() {
auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa);
if (ua.has_value()) {
return libzcash::PaymentAddress{ua.value()};
} else {
return libzcash::PaymentAddress{pa};
}
}());
UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_SAPLING);