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(1, len(unspent_cb))
assert_equal(False, unspent_cb[0]['change']) assert_equal(False, unspent_cb[0]['change'])
assert_equal(txid_1, unspent_cb[0]['txid']) assert_equal(txid_1, unspent_cb[0]['txid'])
assert_equal('sprout', unspent_cb[0]['type'])
assert_equal(True, unspent_cb[0]['spendable']) assert_equal(True, unspent_cb[0]['spendable'])
assert_equal(sproutzaddr, unspent_cb[0]['address']) assert_equal(sproutzaddr, unspent_cb[0]['address'])
assert_equal(receive_amount_1, unspent_cb[0]['amount']) 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']) unspent_tx = sorted(unspent_tx, key=lambda k: k['amount'])
assert_equal(False, unspent_tx[0]['change']) assert_equal(False, unspent_tx[0]['change'])
assert_equal(txid_2, unspent_tx[0]['txid']) assert_equal(txid_2, unspent_tx[0]['txid'])
assert_equal('sapling', unspent_tx[0]['type'])
assert_equal(True, unspent_tx[0]['spendable']) assert_equal(True, unspent_tx[0]['spendable'])
assert_equal(saplingzaddr, unspent_tx[0]['address']) assert_equal(saplingzaddr, unspent_tx[0]['address'])
assert_equal(receive_amount_2, unspent_tx[0]['amount']) assert_equal(receive_amount_2, unspent_tx[0]['amount'])
assert_equal(True, unspent_tx[1]['change']) assert_equal(True, unspent_tx[1]['change'])
assert_equal(txid_2, unspent_tx[1]['txid']) assert_equal(txid_2, unspent_tx[1]['txid'])
assert_equal('sprout', unspent_tx[1]['type'])
assert_equal(True, unspent_tx[1]['spendable']) assert_equal(True, unspent_tx[1]['spendable'])
assert_equal(sproutzaddr, unspent_tx[1]['address']) assert_equal(sproutzaddr, unspent_tx[1]['address'])
assert_equal(change_amount_2, unspent_tx[1]['amount']) 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(False, unspent_tx[0]['change'])
assert_equal(txid_2, unspent_tx[0]['txid']) assert_equal(txid_2, unspent_tx[0]['txid'])
assert_equal('sapling', unspent_tx[0]['type'])
assert_equal(True, unspent_tx[0]['spendable']) assert_equal(True, unspent_tx[0]['spendable'])
assert_equal(saplingzaddr, unspent_tx[0]['address']) assert_equal(saplingzaddr, unspent_tx[0]['address'])
assert_equal(receive_amount_2, unspent_tx[0]['amount']) assert_equal(receive_amount_2, unspent_tx[0]['amount'])
assert_equal(False, unspent_tx[1]['change']) assert_equal(False, unspent_tx[1]['change'])
assert_equal(txid_3, unspent_tx[1]['txid']) assert_equal(txid_3, unspent_tx[1]['txid'])
assert_equal('sapling', unspent_tx[1]['type'])
assert_equal(True, unspent_tx[1]['spendable']) assert_equal(True, unspent_tx[1]['spendable'])
assert_equal(saplingzaddr2, unspent_tx[1]['address']) assert_equal(saplingzaddr2, unspent_tx[1]['address'])
assert_equal(receive_amount_3, unspent_tx[1]['amount']) assert_equal(receive_amount_3, unspent_tx[1]['amount'])
assert_equal(True, unspent_tx[2]['change']) assert_equal(True, unspent_tx[2]['change'])
assert_equal(txid_3, unspent_tx[2]['txid']) assert_equal(txid_3, unspent_tx[2]['txid'])
assert_equal('sprout', unspent_tx[2]['type'])
assert_equal(True, unspent_tx[2]['spendable']) assert_equal(True, unspent_tx[2]['spendable'])
assert_equal(sproutzaddr, unspent_tx[2]['address']) assert_equal(sproutzaddr, unspent_tx[2]['address'])
assert_equal(change_amount_3, unspent_tx[2]['amount']) 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']['sapling']['valueZat'], expected * COIN)
# assert_equal(balances['pools']['orchard']['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__': if __name__ == '__main__':
print("Test shielding to a unified address with NU5 activated") 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_equal(balances['pools']['sapling']['valueZat'], expected * COIN)
assert('orchard' not in balances['pools']) 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__': if __name__ == '__main__':
print("Test shielding to a unified address with sapling activated (but not NU5)") 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) assert(len(results) == 0)
results = self.nodes[0].z_listunspent(0) # set minconf to zero results = self.nodes[0].z_listunspent(0) # set minconf to zero
assert(len(results) == 1) assert(len(results) == 1)
assert_equal(results[0]["type"], "sapling")
assert_equal(results[0]["address"], myzaddr) assert_equal(results[0]["address"], myzaddr)
assert_equal(results[0]["amount"], shieldvalue) assert_equal(results[0]["amount"], shieldvalue)
assert_equal(results[0]["confirmations"], 0) assert_equal(results[0]["confirmations"], 0)
@ -140,6 +141,7 @@ class WalletShieldingCoinbaseTest (BitcoinTestFramework):
# Verify that z_listunspent returns one note which has been confirmed # Verify that z_listunspent returns one note which has been confirmed
results = self.nodes[0].z_listunspent() results = self.nodes[0].z_listunspent()
assert(len(results) == 1) assert(len(results) == 1)
assert_equal(results[0]["type"], "sapling")
assert_equal(results[0]["address"], myzaddr) assert_equal(results[0]["address"], myzaddr)
assert_equal(results[0]["amount"], shieldvalue) assert_equal(results[0]["amount"], shieldvalue)
assert_equal(results[0]["confirmations"], 1) 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. # Verify that z_listunspent returns note for watchonly address on node 3.
results = self.nodes[3].z_listunspent(1, 999, True) results = self.nodes[3].z_listunspent(1, 999, True)
assert(len(results) == 1) assert(len(results) == 1)
assert_equal(results[0]["type"], "sapling")
assert_equal(results[0]["address"], myzaddr) assert_equal(results[0]["address"], myzaddr)
assert_equal(results[0]["amount"], shieldvalue) assert_equal(results[0]["amount"], shieldvalue)
assert_equal(results[0]["confirmations"], 1) 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" "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" "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" "Results are an array of Objects, each of which has:\n"
"{txid, jsindex, jsoutindex, confirmations, address, amount, memo} (Sprout)\n" "{txid, type, jsindex, jsoutindex, confirmations, address, amount, memo} (Sprout)\n"
"{txid, outindex, confirmations, address, amount, memo} (Sapling)\n" "{txid, type, outindex, confirmations, address, amount, memo} (Sapling)\n"
"\nArguments:\n" "\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum confirmations to filter\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" "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" "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" " [\n"
" \"address\" (string) zaddr\n" " \"address\" (string) Sprout, Sapling, or Unified address\n"
" ,...\n" " ,...\n"
" ]\n" " ]\n"
"\nResult (output indices for only one pool will be present):\n" "\nResult (output indices for only one pool will be present):\n"
"[ (array of json object)\n" "[ (array of json object)\n"
" {\n" " {\n"
" \"txid\" : \"txid\", (string) the transaction id \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" " \"jsindex\" (sprout) : n, (numeric) the joinsplit index\n"
" \"jsoutindex\" (sprout) : n, (numeric) the output index of the joinsplit\n" " \"jsoutindex\" (sprout) : n, (numeric) the output index of the joinsplit\n"
" \"outindex\" (sapling) : n, (numeric) the output index\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) { for (auto & entry : sproutEntries) {
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
obj.pushKV("txid", entry.jsop.hash.ToString()); obj.pushKV("txid", entry.jsop.hash.ToString());
obj.pushKV("type", ADDR_TYPE_SPROUT);
obj.pushKV("jsindex", (int)entry.jsop.js ); obj.pushKV("jsindex", (int)entry.jsop.js );
obj.pushKV("jsoutindex", (int)entry.jsop.n); obj.pushKV("jsoutindex", (int)entry.jsop.n);
obj.pushKV("confirmations", entry.confirmations); obj.pushKV("confirmations", entry.confirmations);
@ -2391,12 +2393,19 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
for (auto & entry : saplingEntries) { for (auto & entry : saplingEntries) {
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
obj.pushKV("txid", entry.op.hash.ToString()); obj.pushKV("txid", entry.op.hash.ToString());
obj.pushKV("type", ADDR_TYPE_SAPLING);
obj.pushKV("outindex", (int)entry.op.n); obj.pushKV("outindex", (int)entry.op.n);
obj.pushKV("confirmations", entry.confirmations); obj.pushKV("confirmations", entry.confirmations);
bool hasSaplingSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(entry.address); bool hasSaplingSpendingKey = pwalletMain->HaveSaplingSpendingKeyForAddress(entry.address);
obj.pushKV("spendable", hasSaplingSpendingKey); obj.pushKV("spendable", hasSaplingSpendingKey);
// TODO: If we found this entry via a UA, show that instead. obj.pushKV("address", keyIO.EncodePaymentAddress([&]() {
obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address)); 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("amount", ValueFromAmount(CAmount(entry.note.value()))); // note.value() is equivalent to plaintext.value()
obj.pushKV("memo", HexStr(entry.memo)); obj.pushKV("memo", HexStr(entry.memo));
if (hasSaplingSpendingKey) { 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 // If the note belongs to a Sapling address that is part of an account in the
// wallet, show the corresponding Unified Address. // wallet, show the corresponding Unified Address.
std::string address; std::string address = keyIO.EncodePaymentAddress([&]() {
const auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa); auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa);
if (ua.has_value()) { if (ua.has_value()) {
address = keyIO.EncodePaymentAddress(ua.value()); return libzcash::PaymentAddress{ua.value()};
} else { } else {
address = keyIO.EncodePaymentAddress(pa); return libzcash::PaymentAddress{pa};
} }
}());
UniValue entry(UniValue::VOBJ); UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_SAPLING); 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 // If the note belongs to a Sapling address that is part of an account in the
// wallet, show the corresponding Unified Address. // wallet, show the corresponding Unified Address.
std::string address; std::string address = keyIO.EncodePaymentAddress([&]() {
const auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa); auto ua = pwalletMain->FindUnifiedAddressByReceiver(pa);
if (ua.has_value()) { if (ua.has_value()) {
address = keyIO.EncodePaymentAddress(ua.value()); return libzcash::PaymentAddress{ua.value()};
} else { } else {
address = keyIO.EncodePaymentAddress(pa); return libzcash::PaymentAddress{pa};
} }
}());
UniValue entry(UniValue::VOBJ); UniValue entry(UniValue::VOBJ);
entry.pushKV("type", ADDR_TYPE_SAPLING); entry.pushKV("type", ADDR_TYPE_SAPLING);