From eec85c4388691da0fa5950217c158bb87bf59cd4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 9 Jul 2018 23:46:36 +0100 Subject: [PATCH] Add Sapling support to z_getnewaddress and z_listaddresses --- qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/wallet_addresses.py | 85 ++++++++++++++++++++++++++++++++ src/wallet/rpcwallet.cpp | 61 ++++++++++++++++++----- 3 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 qa/rpc-tests/wallet_addresses.py diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 6a2348012..73d9ddb76 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -23,6 +23,7 @@ testScripts=( 'wallet_overwintertx.py' 'wallet_nullifiers.py' 'wallet_1941.py' + 'wallet_addresses.py' 'listtransactions.py' 'mempool_resurrect_test.py' 'txn_doublespend.py' diff --git a/qa/rpc-tests/wallet_addresses.py b/qa/rpc-tests/wallet_addresses.py new file mode 100644 index 000000000..874088719 --- /dev/null +++ b/qa/rpc-tests/wallet_addresses.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, start_nodes + +# Test wallet address behaviour across network upgradesa\ +class WalletAddressesTest(BitcoinTestFramework): + + def setup_nodes(self): + return start_nodes(4, self.options.tmpdir, [[ + '-nuparams=5ba81b19:202', # Overwinter + '-nuparams=76b809bb:204', # Sapling + ]] * 4) + + def run_test(self): + def addr_checks(default_type): + # Check default type + addr = self.nodes[0].z_getnewaddress() + res = self.nodes[0].z_validateaddress(addr) + assert(res['isvalid']) + assert(res['ismine']) + assert_equal(res['type'], default_type) + assert(addr in self.nodes[0].z_listaddresses()) + + # Check explicit Sprout type + addr = self.nodes[0].z_getnewaddress('sprout') + res = self.nodes[0].z_validateaddress(addr) + assert(res['isvalid']) + assert(res['ismine']) + assert_equal(res['type'], 'sprout') + assert(addr in self.nodes[0].z_listaddresses()) + + # Check explicit Sapling type + addr = self.nodes[0].z_getnewaddress('sapling') + res = self.nodes[0].z_validateaddress(addr) + assert(res['isvalid']) + assert(res['ismine']) + assert_equal(res['type'], 'sapling') + assert(addr in self.nodes[0].z_listaddresses()) + + # Sanity-check the test harness + assert_equal(self.nodes[0].getblockcount(), 200) + + # Current height = 200 -> Sprout + # Default address type is Sprout + print "Testing height 200 (Sprout)" + addr_checks('sprout') + + self.nodes[0].generate(1) + self.sync_all() + + # Current height = 201 -> Sprout + # Default address type is Sprout + print "Testing height 201 (Sprout)" + addr_checks('sprout') + + self.nodes[0].generate(1) + self.sync_all() + + # Current height = 202 -> Overwinter + # Default address type is Sprout + print "Testing height 202 (Overwinter)" + addr_checks('sprout') + + self.nodes[0].generate(1) + self.sync_all() + + # Current height = 203 -> Overwinter + # Default address type is Sprout + print "Testing height 203 (Overwinter)" + addr_checks('sprout') + + self.nodes[0].generate(1) + self.sync_all() + + # Current height = 204 -> Sapling + # Default address type is Sprout + print "Testing height 204 (Sapling)" + addr_checks('sprout') + +if __name__ == '__main__': + WalletAddressesTest().main() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 696e2b288..04f3e9ebf 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -42,6 +42,9 @@ using namespace std; using namespace libzcash; +const std::string ADDR_TYPE_SPROUT = "sprout"; +const std::string ADDR_TYPE_SAPLING = "sapling"; + extern UniValue TxJoinSplitToJSON(const CTransaction& tx); int64_t nWalletUnlockTime; @@ -3100,15 +3103,21 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() > 0) + std::string defaultType = ADDR_TYPE_SPROUT; + + if (fHelp || params.size() > 1) throw runtime_error( - "z_getnewaddress\n" - "\nReturns a new zaddr for receiving payments.\n" + "z_getnewaddress ( type )\n" + "\nReturns a new shielded address for receiving payments.\n" + "\nWith no arguments, returns a Sprout address.\n" "\nArguments:\n" + "1. \"type\" (string, optional, default=\"" + defaultType + "\") The type of address. One of [\"" + + ADDR_TYPE_SPROUT + "\", \"" + ADDR_TYPE_SAPLING + "\"].\n" "\nResult:\n" - "\"zcashaddress\" (string) The new zaddr\n" + "\"zcashaddress\" (string) The new shielded address.\n" "\nExamples:\n" + HelpExampleCli("z_getnewaddress", "") + + HelpExampleCli("z_getnewaddress", ADDR_TYPE_SAPLING) + HelpExampleRpc("z_getnewaddress", "") ); @@ -3116,8 +3125,18 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); - auto zaddr = pwalletMain->GenerateNewZKey(); - return EncodePaymentAddress(zaddr); + auto addrType = defaultType; + if (params.size() > 0) { + addrType = params[0].get_str(); + } + + if (addrType == ADDR_TYPE_SPROUT) { + return EncodePaymentAddress(pwalletMain->GenerateNewZKey()); + } else if (addrType == ADDR_TYPE_SAPLING) { + return EncodePaymentAddress(pwalletMain->GenerateNewSaplingZKey()); + } else { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address type"); + } } @@ -3129,7 +3148,7 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( "z_listaddresses ( includeWatchonly )\n" - "\nReturns the list of zaddr belonging to the wallet.\n" + "\nReturns the list of Sprout and Sapling shielded addresses belonging to the wallet.\n" "\nArguments:\n" "1. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n" "\nResult:\n" @@ -3150,12 +3169,28 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp) } UniValue ret(UniValue::VARR); - // TODO: Add Sapling support - std::set addresses; - pwalletMain->GetPaymentAddresses(addresses); - for (auto addr : addresses ) { - if (fIncludeWatchonly || pwalletMain->HaveSpendingKey(addr)) { - ret.push_back(EncodePaymentAddress(addr)); + { + std::set addresses; + pwalletMain->GetPaymentAddresses(addresses); + for (auto addr : addresses ) { + if (fIncludeWatchonly || pwalletMain->HaveSpendingKey(addr)) { + ret.push_back(EncodePaymentAddress(addr)); + } + } + } + { + std::set addresses; + pwalletMain->GetSaplingPaymentAddresses(addresses); + libzcash::SaplingIncomingViewingKey ivk; + libzcash::SaplingFullViewingKey fvk; + for (auto addr : addresses ) { + if (fIncludeWatchonly || ( + pwalletMain->GetSaplingIncomingViewingKey(addr, ivk) && + pwalletMain->GetSaplingFullViewingKey(ivk, fvk) && + pwalletMain->HaveSaplingSpendingKey(fvk) + )) { + ret.push_back(EncodePaymentAddress(addr)); + } } } return ret;