wallet: consolidate unified key/address/account map reconstruction
This commit is contained in:
parent
68d6c3ddc3
commit
49ce03ed79
|
@ -822,51 +822,11 @@ WalletUAGenerationResult CWallet::GenerateUnifiedAddress(
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: There is an important relationship between `LoadUnifiedFullViewingKey`
|
||||
// and `LoadUnifiedAddressMetadata`. In all cases, by the end of the
|
||||
// wallet-loading process, `mapUfvkAddressMetadata` needs to be fully populated
|
||||
// for each unified full viewing key. However, we cannot guarantee in what
|
||||
// order UFVKs and unified address and account metadata are loaded from disk.
|
||||
// Therefore:
|
||||
// * When we load a unified full viewing key from disk, we repopulate
|
||||
// the cache of generated addresses by regenerating the address for each
|
||||
// (diversifierIndex, {receiverType...}) pair in `mapUfvkAddressMetadata`
|
||||
// that we have loaded so far.
|
||||
// * When we load a unified address metadata record from disk:
|
||||
// - if we have not yet loaded the unified full viewing key, simply add
|
||||
// an entry `mapUfvkAddressMetadata` so that we can restore the address
|
||||
// once the UFVK has been loaded.
|
||||
// - if we have already loaded the unified full viewing key from disk,
|
||||
// regenerate the address from its metadata and add it to the keystore
|
||||
//
|
||||
// While this is somewhat complicated, it has the benefit of making each
|
||||
// piece of unified full viewing key and address metadata write-once in
|
||||
// the wallet database; we never need to update ufvk or address metadata
|
||||
// once written.
|
||||
bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key)
|
||||
{
|
||||
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), key);
|
||||
auto metadata = mapUfvkAddressMetadata.find(zufvk.GetKeyID());
|
||||
if (metadata != mapUfvkAddressMetadata.end()) {
|
||||
// restore unified addresses that have been previously generated to the
|
||||
// keystore
|
||||
for (const auto &[j, receiverTypes] : metadata->second.GetKnownReceiverSetsByDiversifierIndex()) {
|
||||
bool restored = std::visit(match {
|
||||
[&](const UnifiedAddressGenerationError& err) {
|
||||
return false;
|
||||
},
|
||||
[&](const std::pair<UnifiedAddress, diversifier_index_t>& addr) {
|
||||
return CCryptoKeyStore::AddTransparentReceiverForUnifiedAddress(
|
||||
zufvk.GetKeyID(), addr.second, addr.first);
|
||||
}
|
||||
}, zufvk.Address(j, receiverTypes));
|
||||
|
||||
// failure to restore the generated address is an error
|
||||
if (!restored) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return CCryptoKeyStore::AddUnifiedFullViewingKey(zufvk);
|
||||
return CCryptoKeyStore::AddUnifiedFullViewingKey(
|
||||
ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), key)
|
||||
);
|
||||
}
|
||||
|
||||
bool CWallet::LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta)
|
||||
|
@ -880,23 +840,47 @@ bool CWallet::LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skm
|
|||
bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta)
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
if (!mapUfvkAddressMetadata[addrmeta.GetKeyID()].SetReceivers(
|
||||
|
||||
return mapUfvkAddressMetadata[addrmeta.GetKeyID()].SetReceivers(
|
||||
addrmeta.GetDiversifierIndex(),
|
||||
addrmeta.GetReceiverTypes())) {
|
||||
return false;
|
||||
}
|
||||
addrmeta.GetReceiverTypes());
|
||||
}
|
||||
|
||||
auto ufvk = GetUnifiedFullViewingKey(addrmeta.GetKeyID());
|
||||
if (ufvk.has_value()) {
|
||||
// Regenerate the unified address and add it to the keystore.
|
||||
auto j = addrmeta.GetDiversifierIndex();
|
||||
auto addr = ufvk.value().Address(j, addrmeta.GetReceiverTypes());
|
||||
auto addrPtr = std::get_if<std::pair<UnifiedAddress, diversifier_index_t>>(&addr);
|
||||
bool CWallet::LoadUnifiedCaches()
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
|
||||
if (addrPtr == nullptr) {
|
||||
return false;
|
||||
for (auto account = mapUnifiedAccountKeys.begin(); account != mapUnifiedAccountKeys.end(); ++account) {
|
||||
auto ufvkId = account->second;
|
||||
auto ufvk = GetUnifiedFullViewingKey(ufvkId);
|
||||
if (ufvk.has_value()) {
|
||||
auto metadata = mapUfvkAddressMetadata.find(ufvkId);
|
||||
if (metadata != mapUfvkAddressMetadata.end()) {
|
||||
// restore unified addresses that have been previously generated to the
|
||||
// keystore
|
||||
for (const auto &[j, receiverTypes] : metadata->second.GetKnownReceiverSetsByDiversifierIndex()) {
|
||||
bool restored = std::visit(match {
|
||||
[&](const UnifiedAddressGenerationError& err) {
|
||||
return false;
|
||||
},
|
||||
[&](const std::pair<UnifiedAddress, diversifier_index_t>& addr) {
|
||||
return CCryptoKeyStore::AddTransparentReceiverForUnifiedAddress(
|
||||
ufvkId, addr.second, addr.first);
|
||||
}
|
||||
}, ufvk.value().Address(j, receiverTypes));
|
||||
|
||||
// failure to restore the generated address is an error
|
||||
if (!restored) return false;
|
||||
}
|
||||
} else {
|
||||
// Loaded an account, but didn't initialize
|
||||
// `mapUfvkAddressMetadata` for the corresponding viewing key.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return CCryptoKeyStore::AddTransparentReceiverForUnifiedAddress(addrmeta.GetKeyID(), j, addrPtr->first);
|
||||
// Loaded an account, but the corresponding viewing key could not be
|
||||
// found.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1534,6 +1534,14 @@ public:
|
|||
bool LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta);
|
||||
bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta);
|
||||
|
||||
//! Reconstructs (in memory) caches and mappings for unified accounts,
|
||||
//! addresses and keying material. This should be called once, after the
|
||||
//! remainder of the on-disk wallet data has been loaded.
|
||||
//!
|
||||
//! Returns true if and only if there were no detected inconsistencies or
|
||||
//! failures in reconstructing the cache.
|
||||
bool LoadUnifiedCaches();
|
||||
|
||||
std::optional<libzcash::UFVKId> FindUnifiedFullViewingKey(const libzcash::UnifiedAddress& addr) const;
|
||||
std::optional<libzcash::AccountId> GetUnifiedAccountId(const libzcash::UFVKId& ufvkId) const;
|
||||
|
||||
|
|
|
@ -963,6 +963,14 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
|
|||
}
|
||||
pcursor->close();
|
||||
|
||||
// Load unified address/account/key caches based on what was loaded
|
||||
if (!pwallet->LoadUnifiedCaches()) {
|
||||
// We can be more permissive of certain kinds of failures during
|
||||
// loading; for now we'll interpret failure to reconstruct the
|
||||
// caches to be "as bad" as losing keys.
|
||||
result = DB_CORRUPT;
|
||||
}
|
||||
|
||||
// Run the Orchard batch validator; if it fails, treat it like a bad transaction record.
|
||||
if (!wss.orchardAuth.Validate()) {
|
||||
fNoncriticalErrors = true;
|
||||
|
@ -1283,6 +1291,13 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKe
|
|||
ptxn->commit(0);
|
||||
pdbCopy->close(0);
|
||||
|
||||
// Try to load the unified caches, uncovering inconsistencies in wallet
|
||||
// records like missing viewing key records despite existing account
|
||||
// records.
|
||||
if (!dummyWallet.LoadUnifiedCaches()) {
|
||||
LogPrintf("WARNING: unified caches could not be reconstructed; salvaged wallet file may have omissions");
|
||||
}
|
||||
|
||||
return fSuccess;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue