diff --git a/qa/rpc-tests/wallet_sapling.py b/qa/rpc-tests/wallet_sapling.py index b9bfa1f57..05943c1fb 100755 --- a/qa/rpc-tests/wallet_sapling.py +++ b/qa/rpc-tests/wallet_sapling.py @@ -136,11 +136,13 @@ class WalletSaplingTest(BitcoinTestFramework): # Verify importing a spending key will update the nullifiers and witnesses correctly sk0 = self.nodes[0].z_exportkey(saplingAddr0) - self.nodes[2].z_importkey(sk0, "yes") - assert_equal(self.nodes[2].z_getbalance(saplingAddr0), Decimal('10')) + saplingAddrInfo0 = self.nodes[2].z_importkey(sk0, "yes") + assert_equal(saplingAddrInfo0["type"], "sapling") + assert_equal(self.nodes[2].z_getbalance(saplingAddrInfo0["address"]), Decimal('10')) sk1 = self.nodes[1].z_exportkey(saplingAddr1) - self.nodes[2].z_importkey(sk1, "yes") - assert_equal(self.nodes[2].z_getbalance(saplingAddr1), Decimal('5')) + saplingAddrInfo1 = self.nodes[2].z_importkey(sk1, "yes") + assert_equal(saplingAddrInfo1["type"], "sapling") + assert_equal(self.nodes[2].z_getbalance(saplingAddrInfo1["address"]), Decimal('5')) # Make sure we get a useful error when trying to send to both sprout and sapling node4_sproutaddr = self.nodes[3].z_getnewaddress('sprout') diff --git a/qa/rpc-tests/zkey_import_export.py b/qa/rpc-tests/zkey_import_export.py index b0311e16b..7d8646be2 100755 --- a/qa/rpc-tests/zkey_import_export.py +++ b/qa/rpc-tests/zkey_import_export.py @@ -74,11 +74,6 @@ class ZkeyImportExportTest (BitcoinTestFramework): balance = node.z_gettotalbalance() return balance['private'] - def find_imported_key(node, import_zaddr): - zaddrs = node.z_listaddresses() - assert(import_zaddr in zaddrs) - return import_zaddr - # Seed Alice with some funds alice.generate(10) self.sync_all() @@ -122,27 +117,28 @@ class ZkeyImportExportTest (BitcoinTestFramework): logging.info("Importing bob_privkey into charlie...") # z_importkey rescan defaults to "whenkeyisnew", so should rescan here - charlie.z_importkey(bob_privkey) - ipk_zaddr = find_imported_key(charlie, bob_zaddr) + ipk_zaddr = charlie.z_importkey(bob_privkey) # z_importkey should have rescanned for new key, so this should pass: - verify_utxos(charlie, amounts[:4], ipk_zaddr) + verify_utxos(charlie, amounts[:4], ipk_zaddr["address"]) + + # address is sprout + assert_equal(ipk_zaddr["type"], "sprout") # Verify idempotent behavior: - charlie.z_importkey(bob_privkey) - ipk_zaddr2 = find_imported_key(charlie, bob_zaddr) - assert_equal(ipk_zaddr, ipk_zaddr2) + ipk_zaddr2 = charlie.z_importkey(bob_privkey) + assert_equal(ipk_zaddr["address"], ipk_zaddr2["address"]) # amounts should be unchanged - verify_utxos(charlie, amounts[:4], ipk_zaddr2) + verify_utxos(charlie, amounts[:4], ipk_zaddr2["address"]) logging.info("Sending post-import txns...") for amount in amounts[4:]: z_send(alice, alice_zaddr, bob_zaddr, amount) verify_utxos(bob, amounts, bob_zaddr) - verify_utxos(charlie, amounts, ipk_zaddr) - verify_utxos(charlie, amounts, ipk_zaddr2) + verify_utxos(charlie, amounts, ipk_zaddr["address"]) + verify_utxos(charlie, amounts, ipk_zaddr2["address"]) # keep track of the fees incurred by bob (his sends) bob_fee = Decimal(0) @@ -158,12 +154,11 @@ class ZkeyImportExportTest (BitcoinTestFramework): assert_equal(bob.z_getbalance(bob_zaddr), bob_balance) # z_import onto new node "david" (blockchain rescan, default or True?) - david.z_importkey(bob_privkey) - d_ipk_zaddr = find_imported_key(david, bob_zaddr) + d_ipk_zaddr = david.z_importkey(bob_privkey) # Check if amt bob spent is deducted for charlie and david - assert_equal(charlie.z_getbalance(ipk_zaddr), bob_balance) - assert_equal(david.z_getbalance(d_ipk_zaddr), bob_balance) + assert_equal(charlie.z_getbalance(ipk_zaddr["address"]), bob_balance) + assert_equal(david.z_getbalance(d_ipk_zaddr["address"]), bob_balance) if __name__ == '__main__': ZkeyImportExportTest().main() diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index cad0cb0f5..810c416ca 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -678,6 +678,11 @@ UniValue z_importkey(const UniValue& params, bool fHelp) "2. rescan (string, optional, default=\"whenkeyisnew\") Rescan the wallet for transactions - can be \"yes\", \"no\" or \"whenkeyisnew\"\n" "3. startHeight (numeric, optional, default=0) Block height to start rescan from\n" "\nNote: This call can take minutes to complete if rescan is true.\n" + "\nResult:\n" + "{\n" + " \"type\" : \"xxxx\", (string) \"sprout\" or \"sapling\"\n" + " \"address\" : \"address|DefaultAddress\", (string) The address(sprout) or the DefaultAddress(sapling)\n" + "}\n" "\nExamples:\n" "\nExport a zkey\n" + HelpExampleCli("z_exportkey", "\"myaddress\"") + @@ -737,10 +742,15 @@ UniValue z_importkey(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } + auto addrInfo = boost::apply_visitor(libzcash::AddressInfoFromSpendingKey{}, spendingkey); + UniValue result(UniValue::VOBJ); + result.pushKV("type", addrInfo.first); + result.pushKV("address", EncodePaymentAddress(addrInfo.second)); + // Sapling support auto addResult = boost::apply_visitor(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey); if (addResult == KeyAlreadyExists && fIgnoreExistingKey) { - return NullUniValue; + return result; } pwalletMain->MarkDirty(); if (addResult == KeyNotAdded) { @@ -755,7 +765,7 @@ UniValue z_importkey(const UniValue& params, bool fHelp) pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true); } - return NullUniValue; + return result; } UniValue z_importviewingkey(const UniValue& params, bool fHelp) diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 28a6c969b..a0221b241 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -425,6 +425,32 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_validateaddress) BOOST_CHECK_EQUAL(find_value(resultObj, "diversifiedtransmissionkey").get_str(), "34ed1f60f5db5763beee1ddbb37dd5f7e541d4d4fbdcc09fbfcc6b8e949bbe9d"); } +BOOST_AUTO_TEST_CASE(rpc_wallet_z_importkey_paymentaddress) { + SelectParams(CBaseChainParams::MAIN); + LOCK2(cs_main, pwalletMain->cs_wallet); + + auto testAddress = [](const std::string& type, const std::string& key) { + UniValue ret; + BOOST_CHECK_NO_THROW(ret = CallRPC("z_importkey " + key)); + auto defaultAddr = find_value(ret, "address").get_str(); + BOOST_CHECK_EQUAL(type, find_value(ret, "type").get_str()); + BOOST_CHECK_NO_THROW(ret = CallRPC("z_validateaddress " + defaultAddr)); + ret = ret.get_obj(); + BOOST_CHECK_EQUAL(true, find_value(ret, "isvalid").get_bool()); + BOOST_CHECK_EQUAL(true, find_value(ret, "ismine").get_bool()); + BOOST_CHECK_EQUAL(type, find_value(ret, "type").get_str()); + }; + + testAddress("sapling", "secret-extended-key-main1qya4wae0qqqqqqpxfq3ukywunn" + "dtr8xf39hktp3w4z94smuu3l8wr6h4cwxklzzemtg9sk5c7tamfqs48ml6rvuvyup8" + "ne6jz9g7l0asew0htdpjgfss29et84uvqhynjayl3laphks2wxy3c8vhqr4wrca3wl" + "ft2fhcacqtvfwsht4t33l8ckpyr8djmzj7swlvhdhepvc3ehycf9cja335ex6rlpka" + "8z2gzkul3mztga2ups55c3xvn9j6vpdfm5a5v60g9v3sztcpvxqhm"); + + testAddress("sprout", + "SKxoWv77WGwFnUJitQKNEcD636bL4X5Gd6wWmgaA4Q9x8jZBPJXT"); +} + /* * This test covers RPC command z_exportwallet */ diff --git a/src/zcash/Address.cpp b/src/zcash/Address.cpp index dd436ad6d..f0e421501 100644 --- a/src/zcash/Address.cpp +++ b/src/zcash/Address.cpp @@ -3,6 +3,7 @@ #include "hash.h" #include "prf.h" #include "streams.h" +#include "zcash/zip32.h" #include @@ -109,6 +110,16 @@ SaplingPaymentAddress SaplingSpendingKey::default_address() const { return addrOpt.value(); } +std::pair AddressInfoFromSpendingKey::operator()(const SproutSpendingKey &sk) const { + return std::make_pair("sprout", sk.address()); +} +std::pair AddressInfoFromSpendingKey::operator()(const SaplingExtendedSpendingKey &sk) const { + return std::make_pair("sapling", sk.DefaultAddress()); +} +std::pair AddressInfoFromSpendingKey::operator()(const InvalidEncoding&) const { + throw std::invalid_argument("Cannot derive default address from invalid spending key"); +} + } bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr) { diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index dd2a75cff..c7e446755 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -221,6 +221,13 @@ public: typedef boost::variant PaymentAddress; typedef boost::variant ViewingKey; +class AddressInfoFromSpendingKey : public boost::static_visitor> { +public: + std::pair operator()(const SproutSpendingKey&) const; + std::pair operator()(const struct SaplingExtendedSpendingKey&) const; + std::pair operator()(const InvalidEncoding&) const; +}; + } /** Check whether a PaymentAddress is not an InvalidEncoding. */