Auto merge of #4220 - oxarbitrage:issue3748, r=str4d

Return address and type of imported key in z_importkey

Fixes #3748.
This commit is contained in:
Homu 2020-01-25 00:45:00 +00:00
commit 37435aa2c2
6 changed files with 75 additions and 24 deletions

View File

@ -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')

View File

@ -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()

View File

@ -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)

View File

@ -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
*/

View File

@ -3,6 +3,7 @@
#include "hash.h"
#include "prf.h"
#include "streams.h"
#include "zcash/zip32.h"
#include <librustzcash.h>
@ -109,6 +110,16 @@ SaplingPaymentAddress SaplingSpendingKey::default_address() const {
return addrOpt.value();
}
std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(const SproutSpendingKey &sk) const {
return std::make_pair("sprout", sk.address());
}
std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(const SaplingExtendedSpendingKey &sk) const {
return std::make_pair("sapling", sk.DefaultAddress());
}
std::pair<std::string, PaymentAddress> AddressInfoFromSpendingKey::operator()(const InvalidEncoding&) const {
throw std::invalid_argument("Cannot derive default address from invalid spending key");
}
}
bool IsValidPaymentAddress(const libzcash::PaymentAddress& zaddr) {

View File

@ -221,6 +221,13 @@ public:
typedef boost::variant<InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress> PaymentAddress;
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
class AddressInfoFromSpendingKey : public boost::static_visitor<std::pair<std::string, PaymentAddress>> {
public:
std::pair<std::string, PaymentAddress> operator()(const SproutSpendingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const struct SaplingExtendedSpendingKey&) const;
std::pair<std::string, PaymentAddress> operator()(const InvalidEncoding&) const;
};
}
/** Check whether a PaymentAddress is not an InvalidEncoding. */