Correctly handle imported Sapling addresses
Update the tests to reflect the new listaddresses result structure.
This commit is contained in:
parent
9a11ac73a4
commit
6be880fe34
|
@ -398,16 +398,35 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
|
|||
// This includes transparent addresses generated by the wallet via
|
||||
// the keypool and Sprout addresses for which we have the
|
||||
// spending key.
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("source", "legacy_random");
|
||||
bool hasData = false;
|
||||
|
||||
UniValue random_t(UniValue::VOBJ);
|
||||
|
||||
if (!t_generated_dests.empty()) {
|
||||
UniValue random_t_addrs(UniValue::VARR);
|
||||
for (const CTxDestination& dest : t_generated_dests) {
|
||||
random_t_addrs.push_back(keyIO.EncodeDestination(dest));
|
||||
}
|
||||
random_t.pushKV("addresses", random_t_addrs);
|
||||
hasData = true;
|
||||
}
|
||||
|
||||
if (!t_change_dests.empty()) {
|
||||
UniValue random_t_change_addrs(UniValue::VARR);
|
||||
for (const CTxDestination& dest : t_change_dests) {
|
||||
random_t_change_addrs.push_back(keyIO.EncodeDestination(dest));
|
||||
}
|
||||
random_t.pushKV("change_addresses", random_t_change_addrs);
|
||||
hasData = true;
|
||||
}
|
||||
|
||||
if (hasData) {
|
||||
entry.pushKV("transparent", random_t);
|
||||
}
|
||||
|
||||
if (!sproutAddresses.empty()) {
|
||||
UniValue random_sprout_addrs(UniValue::VARR);
|
||||
for (const SproutPaymentAddress& addr : sproutAddresses) {
|
||||
if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
|
||||
|
@ -415,29 +434,68 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
|
|||
}
|
||||
}
|
||||
|
||||
/// keypool source only applies to transparent transactions
|
||||
UniValue random_t(UniValue::VOBJ);
|
||||
random_t.pushKV("addresses", random_t_addrs);
|
||||
random_t.pushKV("change_addresses", random_t_change_addrs);
|
||||
|
||||
UniValue random_sprout(UniValue::VOBJ);
|
||||
random_sprout.pushKV("addresses", random_sprout_addrs);
|
||||
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("source", "legacy_random");
|
||||
entry.pushKV("transparent", random_t);
|
||||
entry.pushKV("sprout", random_sprout);
|
||||
hasData = true;
|
||||
}
|
||||
|
||||
if (hasData) {
|
||||
ret.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// imported source
|
||||
{
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("source", "imported");
|
||||
|
||||
bool hasData = false;
|
||||
{
|
||||
UniValue imported_sapling_addrs(UniValue::VARR);
|
||||
for (const SaplingPaymentAddress& addr : saplingAddresses) {
|
||||
if (GetSourceForPaymentAddress(pwalletMain)(addr) == Imported) {
|
||||
imported_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr));
|
||||
}
|
||||
}
|
||||
|
||||
if (!imported_sapling_addrs.empty()) {
|
||||
UniValue imported_sapling_obj(UniValue::VOBJ);
|
||||
imported_sapling_obj.pushKV("addresses", imported_sapling_addrs);
|
||||
UniValue imported_sapling(UniValue::VARR);
|
||||
imported_sapling.push_back(imported_sapling_obj);
|
||||
|
||||
entry.pushKV("sapling", imported_sapling);
|
||||
hasData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasData) {
|
||||
ret.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// imported_watchonly source
|
||||
{
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("source", "imported_watchonly");
|
||||
bool hasData = false;
|
||||
|
||||
if (!t_watchonly_dests.empty()) {
|
||||
UniValue watchonly_t_addrs(UniValue::VARR);
|
||||
for (const CTxDestination& dest: t_watchonly_dests) {
|
||||
watchonly_t_addrs.push_back(keyIO.EncodeDestination(dest));
|
||||
}
|
||||
|
||||
UniValue watchonly_t(UniValue::VOBJ);
|
||||
watchonly_t.pushKV("addresses", watchonly_t_addrs);
|
||||
|
||||
entry.pushKV("transparent", watchonly_t);
|
||||
hasData = true;
|
||||
}
|
||||
|
||||
{
|
||||
UniValue watchonly_sprout_addrs(UniValue::VARR);
|
||||
for (const SproutPaymentAddress& addr : sproutAddresses) {
|
||||
if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
|
||||
|
@ -445,6 +503,15 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
|
|||
}
|
||||
}
|
||||
|
||||
if (!watchonly_sprout_addrs.empty()) {
|
||||
UniValue watchonly_sprout(UniValue::VOBJ);
|
||||
watchonly_sprout.pushKV("addresses", watchonly_sprout_addrs);
|
||||
entry.pushKV("sprout", watchonly_sprout);
|
||||
hasData = true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
UniValue watchonly_sapling_addrs(UniValue::VARR);
|
||||
for (const SaplingPaymentAddress& addr : saplingAddresses) {
|
||||
if (!HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
|
||||
|
@ -452,48 +519,47 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
|
|||
}
|
||||
}
|
||||
|
||||
UniValue watchonly_t(UniValue::VOBJ);
|
||||
watchonly_t.pushKV("addresses", watchonly_t_addrs);
|
||||
|
||||
UniValue watchonly_sprout(UniValue::VOBJ);
|
||||
watchonly_sprout.pushKV("addresses", watchonly_sprout_addrs);
|
||||
|
||||
if (!watchonly_sapling_addrs.empty()) {
|
||||
UniValue watchonly_sapling_obj(UniValue::VOBJ);
|
||||
watchonly_sapling_obj.pushKV("addresses", watchonly_sapling_addrs);
|
||||
UniValue watchonly_sapling(UniValue::VARR);
|
||||
watchonly_sapling.push_back(watchonly_sapling_obj);
|
||||
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("source", "imported_watchonly");
|
||||
entry.pushKV("transparent", watchonly_t);
|
||||
entry.pushKV("sprout", watchonly_sprout);
|
||||
entry.pushKV("sapling", watchonly_sapling);
|
||||
hasData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasData) {
|
||||
ret.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// legacy_hdseed source
|
||||
{
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("source", "legacy_hdseed");
|
||||
|
||||
// TODO: split up by zip32 account id
|
||||
UniValue legacy_sapling_addrs(UniValue::VARR);
|
||||
for (const SaplingPaymentAddress& addr : saplingAddresses) {
|
||||
if (HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
|
||||
if (GetSourceForPaymentAddress(pwalletMain)(addr) == LegacyHDSeed) {
|
||||
legacy_sapling_addrs.push_back(keyIO.EncodePaymentAddress(addr));
|
||||
}
|
||||
}
|
||||
|
||||
if (!legacy_sapling_addrs.empty()) {
|
||||
UniValue legacy_sapling_obj(UniValue::VOBJ);
|
||||
legacy_sapling_obj.pushKV("addresses", legacy_sapling_addrs);
|
||||
|
||||
UniValue legacy_sapling(UniValue::VARR);
|
||||
legacy_sapling.push_back(legacy_sapling_obj);
|
||||
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("source", "legacy_hdseed");
|
||||
entry.pushKV("sapling", legacy_sapling);
|
||||
|
||||
ret.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -223,14 +223,22 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
|
|||
*********************************/
|
||||
BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses"));
|
||||
UniValue arr = retValue.get_array();
|
||||
BOOST_CHECK_EQUAL(2, arr.size());
|
||||
{
|
||||
BOOST_CHECK_EQUAL(1, arr.size());
|
||||
bool notFound = true;
|
||||
for (auto a : arr.getValues()) {
|
||||
auto source = find_value(a.get_obj(), "source");
|
||||
if (source.get_str() == "legacy_random") {
|
||||
auto t_obj = find_value(a.get_obj(), "transparent");
|
||||
auto addr = find_value(t_obj, "address").get_str();
|
||||
notFound &= keyIO.DecodeDestination(addr) != demoAddress;
|
||||
auto addrs = find_value(t_obj, "addresses").get_array();
|
||||
BOOST_CHECK_EQUAL(2, addrs.size());
|
||||
for (auto addr : addrs.getValues()) {
|
||||
notFound &= keyIO.DecodeDestination(addr.get_str()) != demoAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_CHECK(!notFound);
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* fundrawtransaction
|
||||
|
@ -670,17 +678,35 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
|
|||
UniValue arr = retValue.get_array();
|
||||
BOOST_CHECK(arr.size() == (2 * n1));
|
||||
|
||||
// Verify that the keys imported are also available from listaddresses
|
||||
{
|
||||
BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses"));
|
||||
auto listarr = retValue.get_array();
|
||||
bool sproutCountMatch = false;
|
||||
bool saplingCountMatch = false;
|
||||
for (auto a : listarr.getValues()) {
|
||||
auto source = find_value(a.get_obj(), "source");
|
||||
if (source.get_str() == "legacy_random") {
|
||||
auto sprout_obj = find_value(a.get_obj(), "sprout").get_obj();
|
||||
auto sprout_addrs = find_value(sprout_obj, "addresses").get_array();
|
||||
sproutCountMatch = (sprout_addrs.size() == n1);
|
||||
}
|
||||
if (source.get_str() == "imported") {
|
||||
auto sapling_obj = find_value(a.get_obj(), "sapling").get_array()[0];
|
||||
auto sapling_addrs = find_value(sapling_obj, "addresses").get_array();
|
||||
saplingCountMatch = (sapling_addrs.size() == n1);
|
||||
}
|
||||
}
|
||||
BOOST_CHECK(sproutCountMatch);
|
||||
BOOST_CHECK(saplingCountMatch);
|
||||
}
|
||||
|
||||
// Put addresses into a set
|
||||
std::unordered_set<std::string> myaddrs;
|
||||
for (UniValue element : arr.getValues()) {
|
||||
myaddrs.insert(element.get_str());
|
||||
}
|
||||
|
||||
// Verify that the keys imported are also available from listaddresses
|
||||
BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses"));
|
||||
arr = retValue.get_array();
|
||||
BOOST_CHECK(arr.size() == (2 * n1));
|
||||
|
||||
// Make new addresses for the set
|
||||
for (int i=0; i<n2; i++) {
|
||||
myaddrs.insert(keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey()));
|
||||
|
@ -704,10 +730,21 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
|
|||
listaddrs.insert(element.get_str());
|
||||
}
|
||||
|
||||
// Verify that the keys imported are also available from listaddresses
|
||||
// Verify that the newly added sprout keys imported are also available from listaddresses
|
||||
{
|
||||
BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses"));
|
||||
arr = retValue.get_array();
|
||||
BOOST_CHECK(arr.size() == numAddrs);
|
||||
auto listarr = retValue.get_array();
|
||||
bool sproutCountMatch = false;
|
||||
for (auto a : listarr.getValues()) {
|
||||
auto source = find_value(a.get_obj(), "source");
|
||||
if (source.get_str() == "legacy_random") {
|
||||
auto sprout_obj = find_value(a.get_obj(), "sprout").get_obj();
|
||||
auto sprout_addrs = find_value(sprout_obj, "addresses").get_array();
|
||||
sproutCountMatch = (sprout_addrs.size() == n1 + n2);
|
||||
}
|
||||
}
|
||||
BOOST_CHECK(sproutCountMatch);
|
||||
}
|
||||
|
||||
// Verify the two sets of addresses are the same
|
||||
BOOST_CHECK(listaddrs.size() == numAddrs);
|
||||
|
|
|
@ -5211,6 +5211,50 @@ bool PaymentAddressBelongsToWallet::operator()(const libzcash::InvalidEncoding&
|
|||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const
|
||||
{
|
||||
return Random;
|
||||
}
|
||||
|
||||
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const
|
||||
{
|
||||
libzcash::SaplingIncomingViewingKey ivk;
|
||||
|
||||
// If we have a SaplingExtendedSpendingKey in the wallet, then we will
|
||||
// also have the corresponding SaplingExtendedFullViewingKey.
|
||||
if (m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk)) {
|
||||
if (m_wallet->HaveSaplingFullViewingKey(ivk)) {
|
||||
// If we have the HD keypath, it's related to the legacy seed
|
||||
if (m_wallet->mapSaplingZKeyMetadata.count(ivk) > 0 &&
|
||||
m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath != "") {
|
||||
return LegacyHDSeed;
|
||||
} else {
|
||||
return Imported;
|
||||
}
|
||||
} else {
|
||||
return Imported;
|
||||
}
|
||||
} else {
|
||||
return AddressNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::UnifiedAddress &uaddr) const
|
||||
{
|
||||
// TODO
|
||||
return AddressNotFound;
|
||||
}
|
||||
|
||||
PaymentAddressSource GetSourceForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const
|
||||
{
|
||||
return AddressNotFound;
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
|
||||
std::optional<libzcash::ViewingKey> GetViewingKeyForPaymentAddress::operator()(
|
||||
const libzcash::SproutPaymentAddress &zaddr) const
|
||||
{
|
||||
|
@ -5385,7 +5429,7 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS
|
|||
// 154051200 seconds from epoch is Friday, 26 October 2018 00:00:00 GMT - definitely before Sapling activates
|
||||
m_wallet->mapSaplingZKeyMetadata[ivk].nCreateTime = std::max((int64_t) 154051200, nTime);
|
||||
}
|
||||
if (hdKeypath) {
|
||||
if (hdKeypath.has_value()) {
|
||||
m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath = hdKeypath.value();
|
||||
}
|
||||
if (seedFpStr) {
|
||||
|
|
|
@ -1411,6 +1411,27 @@ public:
|
|||
std::optional<libzcash::SpendingKey> operator()(const libzcash::InvalidEncoding& no) const;
|
||||
};
|
||||
|
||||
enum PaymentAddressSource {
|
||||
Random,
|
||||
LegacyHDSeed,
|
||||
MnemonicHDSeed,
|
||||
Imported,
|
||||
AddressNotFound,
|
||||
};
|
||||
|
||||
class GetSourceForPaymentAddress
|
||||
{
|
||||
private:
|
||||
CWallet *m_wallet;
|
||||
public:
|
||||
GetSourceForPaymentAddress(CWallet *wallet) : m_wallet(wallet) {}
|
||||
|
||||
PaymentAddressSource operator()(const libzcash::SproutPaymentAddress &zaddr) const;
|
||||
PaymentAddressSource operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
|
||||
PaymentAddressSource operator()(const libzcash::UnifiedAddress &uaddr) const;
|
||||
PaymentAddressSource operator()(const libzcash::InvalidEncoding& no) const;
|
||||
};
|
||||
|
||||
enum KeyAddResult {
|
||||
SpendingKeyExists,
|
||||
KeyAlreadyExists,
|
||||
|
|
Loading…
Reference in New Issue