diff --git a/qa/rpc-tests/wallet_listnotes.py b/qa/rpc-tests/wallet_listnotes.py index 127be8653..c50e7bb7b 100755 --- a/qa/rpc-tests/wallet_listnotes.py +++ b/qa/rpc-tests/wallet_listnotes.py @@ -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']) diff --git a/qa/rpc-tests/wallet_shieldcoinbase_ua_nu5.py b/qa/rpc-tests/wallet_shieldcoinbase_ua_nu5.py index 0767f12b5..81543ef62 100755 --- a/qa/rpc-tests/wallet_shieldcoinbase_ua_nu5.py +++ b/qa/rpc-tests/wallet_shieldcoinbase_ua_nu5.py @@ -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") diff --git a/qa/rpc-tests/wallet_shieldcoinbase_ua_sapling.py b/qa/rpc-tests/wallet_shieldcoinbase_ua_sapling.py index abc182eab..6ef2baea6 100755 --- a/qa/rpc-tests/wallet_shieldcoinbase_ua_sapling.py +++ b/qa/rpc-tests/wallet_shieldcoinbase_ua_sapling.py @@ -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)") diff --git a/qa/rpc-tests/wallet_shieldingcoinbase.py b/qa/rpc-tests/wallet_shieldingcoinbase.py index e61e6644b..f8a3b9ed7 100755 --- a/qa/rpc-tests/wallet_shieldingcoinbase.py +++ b/qa/rpc-tests/wallet_shieldingcoinbase.py @@ -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) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 7834eb89a..3f622dc96 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -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);