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)
|
bool CWallet::LoadUnifiedFullViewingKey(const libzcash::UnifiedFullViewingKey &key)
|
||||||
{
|
{
|
||||||
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), key);
|
return CCryptoKeyStore::AddUnifiedFullViewingKey(
|
||||||
auto metadata = mapUfvkAddressMetadata.find(zufvk.GetKeyID());
|
ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), key)
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta)
|
bool CWallet::LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta)
|
||||||
|
@ -880,23 +840,47 @@ bool CWallet::LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skm
|
||||||
bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta)
|
bool CWallet::LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet);
|
AssertLockHeld(cs_wallet);
|
||||||
if (!mapUfvkAddressMetadata[addrmeta.GetKeyID()].SetReceivers(
|
|
||||||
|
return mapUfvkAddressMetadata[addrmeta.GetKeyID()].SetReceivers(
|
||||||
addrmeta.GetDiversifierIndex(),
|
addrmeta.GetDiversifierIndex(),
|
||||||
addrmeta.GetReceiverTypes())) {
|
addrmeta.GetReceiverTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CWallet::LoadUnifiedCaches()
|
||||||
|
{
|
||||||
|
AssertLockHeld(cs_wallet);
|
||||||
|
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (addrPtr == nullptr) {
|
|
||||||
return false;
|
|
||||||
} else {
|
} 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 LoadUnifiedAccountMetadata(const ZcashdUnifiedAccountMetadata &skmeta);
|
||||||
bool LoadUnifiedAddressMetadata(const ZcashdUnifiedAddressMetadata &addrmeta);
|
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::UFVKId> FindUnifiedFullViewingKey(const libzcash::UnifiedAddress& addr) const;
|
||||||
std::optional<libzcash::AccountId> GetUnifiedAccountId(const libzcash::UFVKId& ufvkId) const;
|
std::optional<libzcash::AccountId> GetUnifiedAccountId(const libzcash::UFVKId& ufvkId) const;
|
||||||
|
|
||||||
|
|
|
@ -963,6 +963,14 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
|
||||||
}
|
}
|
||||||
pcursor->close();
|
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.
|
// Run the Orchard batch validator; if it fails, treat it like a bad transaction record.
|
||||||
if (!wss.orchardAuth.Validate()) {
|
if (!wss.orchardAuth.Validate()) {
|
||||||
fNoncriticalErrors = true;
|
fNoncriticalErrors = true;
|
||||||
|
@ -1283,6 +1291,13 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKe
|
||||||
ptxn->commit(0);
|
ptxn->commit(0);
|
||||||
pdbCopy->close(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;
|
return fSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue