Use legacy address for Sapling migration until UA functionality is available to the RPC tests.

This commit is contained in:
Kris Nuttycombe 2021-10-20 17:13:52 -06:00
parent ebab190fdf
commit d8d9cd129e
6 changed files with 86 additions and 90 deletions

View File

@ -191,8 +191,9 @@ CAmount AsyncRPCOperation_saplingmigration::chooseAmount(const CAmount& availabl
return amount;
}
// Unless otherwise specified, the migration destination address is the address for Sapling account 0
// at the smallest diversifier index that produces a valid diversified address.
// Unless otherwise specified, the migration destination address is the address
// the legacy Sapling account (0x7FFFFFFE) at the smallest diversifier index
// that produces a valid diversified address.
libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) {
KeyIO keyIO(Params());
if (mapArgs.count("-migrationdestaddress")) {
@ -203,17 +204,9 @@ libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigration
return *saplingAddress;
}
auto usk = pwalletMain->GetUnifiedSpendingKeyForAccount(0);
assert(usk.has_value()); // mnemonic seeds are currently always generated to have valid USKs at account 0
auto ua = usk.value().ToFullViewingKey().FindAddress(libzcash::diversifier_index_t(0));
auto addr = ua.first.GetSaplingPaymentAddress();
if (addr.has_value()) {
return addr.value();
} else {
// This error will only occur if Sapling address generation has been disbled for USKs from this
// wallet.
throw std::runtime_error(std::string(__func__) + ": No Sapling address generated for account 0.");
}
// TODO: move off of legacy addresses.
auto generatedKey = pwalletMain->GenerateLegacySaplingZKey(0);
return generatedKey.first.ToXFVK().DefaultAddress();
}
void AsyncRPCOperation_saplingmigration::cancel() {

View File

@ -29,8 +29,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) {
ASSERT_EQ(0, addrs.size());
// No HD seed in the wallet
auto legacyKey = wallet.GenerateNewLegacySaplingZKey();
ASSERT_FALSE(legacyKey.has_value());
EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey());
// Load the all-zeroes seed
std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art");
@ -41,9 +40,7 @@ TEST(WalletZkeysTest, StoreAndLoadSaplingZkeys) {
wallet.LoadMnemonicSeed(seed);
// Now this call succeeds
legacyKey = wallet.GenerateNewLegacySaplingZKey();
ASSERT_TRUE(legacyKey.has_value());
auto address = legacyKey.value();
auto address = wallet.GenerateNewLegacySaplingZKey();
// wallet should have one key
wallet.GetSaplingPaymentAddresses(addrs);
@ -433,12 +430,13 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) {
// No default CPubKey set
ASSERT_TRUE(fFirstRun);
ASSERT_FALSE(wallet.HaveLegacyHDSeed());
ASSERT_FALSE(wallet.HaveMnemonicSeed());
// Load the all-zeroes seed as the legacy seed
std::string mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art");
MnemonicSeed seed(English, mnemonic);
wallet.LoadMnemonicSeed(seed);
ASSERT_TRUE(wallet.HaveMnemonicSeed());
// wallet should be empty
std::set<libzcash::SaplingPaymentAddress> addrs;
@ -446,7 +444,7 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) {
ASSERT_EQ(0, addrs.size());
// Add random key to the wallet
auto address = wallet.GenerateNewLegacySaplingZKey().value();
auto address = wallet.GenerateNewLegacySaplingZKey();
// wallet should have one key
wallet.GetSaplingPaymentAddresses(addrs);
@ -470,13 +468,11 @@ TEST(WalletZkeysTest, WriteCryptedSaplingZkeyDirectToDb) {
ASSERT_TRUE(wallet.EncryptWallet(strWalletPass));
// adding a new key will fail as the wallet is locked
EXPECT_FALSE(wallet.GenerateNewLegacySaplingZKey().has_value());
EXPECT_ANY_THROW(wallet.GenerateNewLegacySaplingZKey());
// unlock wallet and then add
wallet.Unlock(strWalletPass);
auto address2Opt = wallet.GenerateNewLegacySaplingZKey();
EXPECT_TRUE(address2Opt.has_value());
auto address2 = address2Opt.value();
auto address2 = wallet.GenerateNewLegacySaplingZKey();
// flush the wallet to prevent race conditions
wallet.Flush();

View File

@ -2941,11 +2941,7 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp)
return keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey());
} else if (addrType == ADDR_TYPE_SAPLING) {
auto saplingAddress = pwalletMain->GenerateNewLegacySaplingZKey();
if (saplingAddress.has_value()) {
return keyIO.EncodePaymentAddress(saplingAddress.value());
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "No legacy HD seed available; please use z_getunifiedaddress instead.");
}
return keyIO.EncodePaymentAddress(saplingAddress);
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address type");
}

View File

@ -76,7 +76,7 @@ static UniValue ValueFromString(const std::string &str)
}
static SaplingPaymentAddress DefaultSaplingAddress(CWallet* pwallet) {
auto usk = pwallet->GetUnifiedSpendingKeyForAccount(0);
auto usk = pwallet->GenerateUnifiedSpendingKeyForAccount(0);
return usk.value()
.ToFullViewingKey()

View File

@ -118,48 +118,59 @@ libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey()
// is not present. When using legacy HD seeds, the account index is determined
// by trial of legacyHDChain.GetAccountCounter(); for unified addresses this must use
// valued derived from legacyHDChain.unifiedAccountCounter
std::optional<SaplingPaymentAddress> CWallet::GenerateNewLegacySaplingZKey() {
SaplingPaymentAddress CWallet::GenerateNewLegacySaplingZKey() {
AssertLockHeld(cs_wallet);
auto seedOpt = GetMnemonicSeed();
if (seedOpt.has_value()) {
auto seed = seedOpt.value();
if (!mnemonicHDChain.has_value()) {
mnemonicHDChain = CHDChain(seed.Fingerprint(), GetTime());
}
CHDChain& hdChain = mnemonicHDChain.value();
// loop until we find an unused address index
while (true) {
auto xsk = libzcash::SaplingExtendedSpendingKey::Legacy(
seed,
BIP44CoinType(),
hdChain.GetLegacySaplingKeyCounter());
// advance the address index counter so that the next time we need to generate
// a key we're pointing at a free index.
hdChain.IncrementLegacySaplingKeyCounter();
if (HaveSaplingSpendingKey(xsk.first.ToXFVK())) {
// try the next index
continue;
} else {
// Update the persisted chain information
if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) {
throw std::runtime_error("CWallet::GenerateNewLegacySaplingZKey(): Writing HD chain model failed");
}
auto ivk = xsk.first.expsk.full_viewing_key().in_viewing_key();
mapSaplingZKeyMetadata[ivk] = xsk.second;
if (!AddSaplingZKey(xsk.first)) {
throw std::runtime_error("CWallet::GenerateNewLegacySaplingZKey(): AddSaplingZKey failed");
}
return xsk.first.ToXFVK().DefaultAddress();
}
}
} else {
return std::nullopt;
if (!mnemonicHDChain.has_value()) {
mnemonicHDChain = CHDChain(GetMnemonicSeed().value().Fingerprint(), GetTime());
}
CHDChain& hdChain = mnemonicHDChain.value();
// loop until we find an unused address index
while (true) {
auto generatedKey = GenerateLegacySaplingZKey(hdChain.GetLegacySaplingKeyCounter());
auto xfvk = generatedKey.first.ToXFVK();
// advance the address index counter so that the next time we need to generate
// a key we're pointing at a free index.
hdChain.IncrementLegacySaplingKeyCounter();
if (!generatedKey.second) {
// the key already existed, so try the next one
continue;
} else {
// Update the persisted chain information
if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) {
throw std::runtime_error(
"CWallet::GenerateLegacySaplingZKey(): Writing HD chain model failed");
}
return xfvk.DefaultAddress();
}
}
}
std::pair<SaplingExtendedSpendingKey, bool> CWallet::GenerateLegacySaplingZKey(uint32_t addrIndex) {
auto seedOpt = GetMnemonicSeed();
if (!seedOpt.has_value()) {
throw std::runtime_error(
"CWallet::GenerateLegacySaplingZKey(): Wallet does not have a mnemonic seed.");
}
auto seed = seedOpt.value();
auto xsk = libzcash::SaplingExtendedSpendingKey::Legacy(seed, BIP44CoinType(), addrIndex);
if (!HaveSaplingSpendingKey(xsk.first.ToXFVK())) {
auto ivk = xsk.first.expsk.full_viewing_key().in_viewing_key();
mapSaplingZKeyMetadata[ivk] = xsk.second;
if (!AddSaplingZKey(xsk.first)) {
throw std::runtime_error("CWallet::GenerateLegacySaplingZKey(): AddSaplingZKey failed.");
}
return std::make_pair(xsk.first, true) ;
} else {
return std::make_pair(xsk.first, false);
}
}
// Add spending key to keystore
@ -238,9 +249,7 @@ bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key)
return true;
if (!IsCrypted()) {
return CWalletDB(strWalletFile).WriteZKey(addr,
key,
mapSproutZKeyMetadata[addr]);
return CWalletDB(strWalletFile).WriteZKey(addr, key, mapSproutZKeyMetadata[addr]);
}
return true;
}
@ -394,35 +403,33 @@ bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullVi
UnifiedSpendingKey CWallet::GenerateNewUnifiedSpendingKey() {
AssertLockHeld(cs_wallet);
auto seed = GetMnemonicSeed();
if (!seed.has_value()) {
throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed. Please upgrade this wallet.");
}
CHDChain& hdChain = mnemonicHDChain.value();
while (true) {
auto usk = UnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), hdChain.GetAccountCounter());
auto usk = GenerateUnifiedSpendingKeyForAccount(hdChain.GetAccountCounter());
hdChain.IncrementAccountCounter();
if (usk.has_value()) {
// Update the persisted chain information
if (fFileBacked && !CWalletDB(strWalletFile).WriteMnemonicHDChain(hdChain)) {
throw std::runtime_error("CWallet::GenerateNewUnifiedSpendingKey(): Writing HD chain model failed");
throw std::runtime_error(
"CWallet::GenerateNewUnifiedSpendingKey(): Writing HD chain model failed");
}
// TODO: Save the unified full viewing key to the wallet metadata
return usk.value().first;
return usk.value();
}
}
}
std::optional<libzcash::UnifiedSpendingKey> CWallet::GetUnifiedSpendingKeyForAccount(uint32_t accountId) {
std::optional<libzcash::UnifiedSpendingKey> CWallet::GenerateUnifiedSpendingKeyForAccount(uint32_t accountId) {
auto seed = GetMnemonicSeed();
assert(seed.has_value());
// TODO: is there any reason to cache and not re-derive this every time?
if (!seed.has_value()) {
throw std::runtime_error(std::string(__func__) + ": Wallet has no mnemonic HD seed. Please upgrade this wallet.");
}
auto usk = UnifiedSpendingKey::ForAccount(seed.value(), BIP44CoinType(), accountId);
if (usk.has_value()) {
// TODO: Save the unified full viewing key & metadata to the wallet
return usk.value().first;
} else {
return std::nullopt;

View File

@ -1058,10 +1058,14 @@ public:
* Sapling ZKeys
*/
//! Generates new Sapling key using the legacy HD seed (if one is available)
//! and legacy account counter, stores the newly generated spending key to
//! the wallet, and returns the default address for the newly generated key.
std::optional<libzcash::SaplingPaymentAddress> GenerateNewLegacySaplingZKey();
//! Generates new Sapling key, stores the newly generated spending
//! key to the wallet, and returns the default address for the newly generated key.
libzcash::SaplingPaymentAddress GenerateNewLegacySaplingZKey();
//! Generates Sapling key at the specified address index, and stores the newly generated
//! spending key to the wallet if it has not alreay been persisted.
//! Returns the newly created key, its metadata, and a flag distinguishing
//! whether or not the key was already known by the wallet.
std::pair<libzcash::SaplingExtendedSpendingKey, bool> GenerateLegacySaplingZKey(uint32_t addrIndex);
//! Adds Sapling spending key to the store, and saves it to disk
bool AddSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key);
//! Add Sapling full viewing key to the wallet.
@ -1096,7 +1100,7 @@ public:
* Unified keys & addresses
*/
libzcash::UnifiedSpendingKey GenerateNewUnifiedSpendingKey();
std::optional<libzcash::UnifiedSpendingKey> GetUnifiedSpendingKeyForAccount(uint32_t accountId);
std::optional<libzcash::UnifiedSpendingKey> GenerateUnifiedSpendingKeyForAccount(uint32_t accountId);
/**
* Increment the next transaction order id