diff options
author | Andrew Chow <achow101-github@achow101.com> | 2022-04-12 14:27:39 -0400 |
---|---|---|
committer | Andrew Chow <github@achow101.com> | 2023-06-27 11:00:47 -0400 |
commit | 30ab11c49793d5d55d66c4dedfa576ae8fd6129c (patch) | |
tree | a4530899bbeb7f99ad6214ad9b05960f707fb610 /src/wallet | |
parent | 9e077d9b422ac3c371fe0f63da40e5092171a25e (diff) |
walletdb: Refactor legacy wallet record loading into its own function
Instead of loading legacy wallet records as we come across them when
iterating the database, load them explicitly.
Exception handling for these records changes to a per-record type basis,
rather than globally. This results in some records now failing with a
critical error rather than a non-critical one.
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/walletdb.cpp | 410 | ||||
-rw-r--r-- | src/wallet/walletdb.h | 26 |
2 files changed, 283 insertions, 153 deletions
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 4cf3be436b..cf79025801 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -301,10 +301,8 @@ class CWalletScanState { public: unsigned int nKeys{0}; unsigned int nCKeys{0}; - unsigned int nWatchKeys{0}; unsigned int nKeyMeta{0}; unsigned int m_unknown_records{0}; - bool fIsEncrypted{false}; bool fAnyUnordered{false}; std::vector<uint256> vWalletUpgrade; std::map<OutputType, uint256> m_active_external_spks; @@ -312,10 +310,8 @@ public: std::map<uint256, DescriptorCache> m_descriptor_caches; std::map<std::pair<uint256, CKeyID>, CKey> m_descriptor_keys; std::map<std::pair<uint256, CKeyID>, std::pair<CPubKey, std::vector<unsigned char>>> m_descriptor_crypt_keys; - std::map<uint160, CHDChain> m_hd_chains; bool tx_corrupt{false}; bool descriptor_unknown{false}; - bool unexpected_legacy_entry{false}; CWalletScanState() = default; }; @@ -477,10 +473,8 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, // Taking advantage of the fact that pair serialization // is just the two items serialized one after the other ssKey >> strType; - // Legacy entries in descriptor wallets are not allowed, abort immediately if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) && DBKeys::LEGACY_TYPES.count(strType) > 0) { - wss.unexpected_legacy_entry = true; - return false; + return true; } if (strType == DBKeys::NAME) { std::string strAddress; @@ -545,97 +539,16 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, return false; } } else if (strType == DBKeys::WATCHS) { - wss.nWatchKeys++; - CScript script; - ssKey >> script; - uint8_t fYes; - ssValue >> fYes; - if (fYes == '1') { - pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script); - } } else if (strType == DBKeys::KEY) { wss.nKeys++; - if (!LoadKey(pwallet, ssKey, ssValue, strErr)) return false; } else if (strType == DBKeys::MASTER_KEY) { if (!LoadEncryptionKey(pwallet, ssKey, ssValue, strErr)) return false; } else if (strType == DBKeys::CRYPTED_KEY) { wss.nCKeys++; - if (!LoadCryptedKey(pwallet, ssKey, ssValue, strErr)) return false; - wss.fIsEncrypted = true; } else if (strType == DBKeys::KEYMETA) { - CPubKey vchPubKey; - ssKey >> vchPubKey; - CKeyMetadata keyMeta; - ssValue >> keyMeta; wss.nKeyMeta++; - pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta); - - // Extract some CHDChain info from this metadata if it has any - if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) { - // Get the path from the key origin or from the path string - // Not applicable when path is "s" or "m" as those indicate a seed - // See https://github.com/bitcoin/bitcoin/pull/12924 - bool internal = false; - uint32_t index = 0; - if (keyMeta.hdKeypath != "s" && keyMeta.hdKeypath != "m") { - std::vector<uint32_t> path; - if (keyMeta.has_key_origin) { - // We have a key origin, so pull it from its path vector - path = keyMeta.key_origin.path; - } else { - // No key origin, have to parse the string - if (!ParseHDKeypath(keyMeta.hdKeypath, path)) { - strErr = "Error reading wallet database: keymeta with invalid HD keypath"; - return false; - } - } - - // Extract the index and internal from the path - // Path string is m/0'/k'/i' - // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit - // k == 0 for external, 1 for internal. i is the index - if (path.size() != 3) { - strErr = "Error reading wallet database: keymeta found with unexpected path"; - return false; - } - if (path[0] != 0x80000000) { - strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]); - return false; - } - if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) { - strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]); - return false; - } - if ((path[2] & 0x80000000) == 0) { - strErr = strprintf("Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]); - return false; - } - internal = path[1] == (1 | 0x80000000); - index = path[2] & ~0x80000000; - } - - // Insert a new CHDChain, or get the one that already exists - auto ins = wss.m_hd_chains.emplace(keyMeta.hd_seed_id, CHDChain()); - CHDChain& chain = ins.first->second; - if (ins.second) { - // For new chains, we want to default to VERSION_HD_BASE until we see an internal - chain.nVersion = CHDChain::VERSION_HD_BASE; - chain.seed_id = keyMeta.hd_seed_id; - } - if (internal) { - chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT; - chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index + 1); - } else { - chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index + 1); - } - } } else if (strType == DBKeys::WATCHMETA) { - CScript script; - ssKey >> script; - CKeyMetadata keyMeta; - ssValue >> keyMeta; wss.nKeyMeta++; - pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta); } else if (strType == DBKeys::DEFAULTKEY) { // We don't want or need the default key, but if there is one set, // we want to make sure that it is valid so that we can detect corruption @@ -646,22 +559,7 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, return false; } } else if (strType == DBKeys::POOL) { - int64_t nIndex; - ssKey >> nIndex; - CKeyPool keypool; - ssValue >> keypool; - - pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool); } else if (strType == DBKeys::CSCRIPT) { - uint160 hash; - ssKey >> hash; - CScript script; - ssValue >> script; - if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script)) - { - strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed"; - return false; - } } else if (strType == DBKeys::ORDERPOSNEXT) { ssValue >> pwallet->nOrderPosNext; } else if (strType == DBKeys::DESTDATA) { @@ -684,7 +582,6 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, pwallet->LoadAddressReceiveRequest(dest, strKey.substr(2), strValue); } } else if (strType == DBKeys::HDCHAIN) { - if (!LoadHDChain(pwallet, ssValue, strErr)) return false; } else if (strType == DBKeys::OLD_KEY) { strErr = "Found unsupported 'wkey' record, try loading with version 0.18"; return false; @@ -803,7 +700,6 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, wss.nCKeys++; wss.m_descriptor_crypt_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(pubkey, privkey))); - wss.fIsEncrypted = true; } else if (strType == DBKeys::LOCKED_UTXO) { uint256 hash; uint32_t n; @@ -855,6 +751,268 @@ static DBErrors LoadWalletFlags(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIV return DBErrors::LOAD_OK; } +struct LoadResult +{ + DBErrors m_result{DBErrors::LOAD_OK}; + int m_records{0}; +}; + +using LoadFunc = std::function<DBErrors(CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err)>; +static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std::string& key, LoadFunc load_func) +{ + LoadResult result; + DataStream ssKey; + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + + DataStream prefix; + prefix << key; + std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix); + if (!cursor) { + pwallet->WalletLogPrintf("Error getting database cursor for '%s' records\n", key); + result.m_result = DBErrors::CORRUPT; + return result; + } + + while (true) { + DatabaseCursor::Status status = cursor->Next(ssKey, ssValue); + if (status == DatabaseCursor::Status::DONE) { + break; + } else if (status == DatabaseCursor::Status::FAIL) { + pwallet->WalletLogPrintf("Error reading next '%s' record for wallet database\n", key); + result.m_result = DBErrors::CORRUPT; + return result; + } + std::string type; + ssKey >> type; + assert(type == key); + std::string error; + DBErrors record_res = load_func(pwallet, ssKey, ssValue, error); + if (record_res != DBErrors::LOAD_OK) { + pwallet->WalletLogPrintf("%s\n", error); + } + result.m_result = std::max(result.m_result, record_res); + ++result.m_records; + } + return result; +} + +static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +{ + AssertLockHeld(pwallet->cs_wallet); + DBErrors result = DBErrors::LOAD_OK; + + // Make sure descriptor wallets don't have any legacy records + if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { + for (const auto& type : DBKeys::LEGACY_TYPES) { + DataStream key; + CDataStream value(SER_DISK, CLIENT_VERSION); + + DataStream prefix; + prefix << type; + std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix); + if (!cursor) { + pwallet->WalletLogPrintf("Error getting database cursor for '%s' records\n", type); + return DBErrors::CORRUPT; + } + + DatabaseCursor::Status status = cursor->Next(key, value); + if (status != DatabaseCursor::Status::DONE) { + pwallet->WalletLogPrintf("Error: Unexpected legacy entry found in descriptor wallet %s. The wallet might have been tampered with or created with malicious intent.\n", pwallet->GetName()); + return DBErrors::UNEXPECTED_LEGACY_ENTRY; + } + } + + return DBErrors::LOAD_OK; + } + + // Load HD Chain + // Note: There should only be one HDCHAIN record with no data following the type + LoadResult hd_chain_res = LoadRecords(pwallet, batch, DBKeys::HDCHAIN, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + return LoadHDChain(pwallet, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, hd_chain_res.m_result); + + // Load unencrypted keys + LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::KEY, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + return LoadKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, key_res.m_result); + + // Load encrypted keys + LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::CRYPTED_KEY, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + return LoadCryptedKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, ckey_res.m_result); + + // Load scripts + LoadResult script_res = LoadRecords(pwallet, batch, DBKeys::CSCRIPT, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) { + uint160 hash; + key >> hash; + CScript script; + value >> script; + if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script)) + { + strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed"; + return DBErrors::NONCRITICAL_ERROR; + } + return DBErrors::LOAD_OK; + }); + result = std::max(result, script_res.m_result); + + // Check whether rewrite is needed + if (ckey_res.m_records > 0) { + // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: + if (last_client == 40000 || last_client == 50000) result = std::max(result, DBErrors::NEED_REWRITE); + } + + // Load keymeta + std::map<uint160, CHDChain> hd_chains; + LoadResult keymeta_res = LoadRecords(pwallet, batch, DBKeys::KEYMETA, + [&hd_chains] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) { + CPubKey vchPubKey; + key >> vchPubKey; + CKeyMetadata keyMeta; + value >> keyMeta; + pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta); + + // Extract some CHDChain info from this metadata if it has any + if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) { + // Get the path from the key origin or from the path string + // Not applicable when path is "s" or "m" as those indicate a seed + // See https://github.com/bitcoin/bitcoin/pull/12924 + bool internal = false; + uint32_t index = 0; + if (keyMeta.hdKeypath != "s" && keyMeta.hdKeypath != "m") { + std::vector<uint32_t> path; + if (keyMeta.has_key_origin) { + // We have a key origin, so pull it from its path vector + path = keyMeta.key_origin.path; + } else { + // No key origin, have to parse the string + if (!ParseHDKeypath(keyMeta.hdKeypath, path)) { + strErr = "Error reading wallet database: keymeta with invalid HD keypath"; + return DBErrors::NONCRITICAL_ERROR; + } + } + + // Extract the index and internal from the path + // Path string is m/0'/k'/i' + // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit + // k == 0 for external, 1 for internal. i is the index + if (path.size() != 3) { + strErr = "Error reading wallet database: keymeta found with unexpected path"; + return DBErrors::NONCRITICAL_ERROR; + } + if (path[0] != 0x80000000) { + strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]); + return DBErrors::NONCRITICAL_ERROR; + } + if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) { + strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]); + return DBErrors::NONCRITICAL_ERROR; + } + if ((path[2] & 0x80000000) == 0) { + strErr = strprintf("Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]); + return DBErrors::NONCRITICAL_ERROR; + } + internal = path[1] == (1 | 0x80000000); + index = path[2] & ~0x80000000; + } + + // Insert a new CHDChain, or get the one that already exists + auto [ins, inserted] = hd_chains.emplace(keyMeta.hd_seed_id, CHDChain()); + CHDChain& chain = ins->second; + if (inserted) { + // For new chains, we want to default to VERSION_HD_BASE until we see an internal + chain.nVersion = CHDChain::VERSION_HD_BASE; + chain.seed_id = keyMeta.hd_seed_id; + } + if (internal) { + chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT; + chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index + 1); + } else { + chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index + 1); + } + } + return DBErrors::LOAD_OK; + }); + result = std::max(result, keymeta_res.m_result); + + // Set inactive chains + if (!hd_chains.empty()) { + LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan(); + if (legacy_spkm) { + for (const auto& [hd_seed_id, chain] : hd_chains) { + if (hd_seed_id != legacy_spkm->GetHDChain().seed_id) { + legacy_spkm->AddInactiveHDChain(chain); + } + } + } else { + pwallet->WalletLogPrintf("Inactive HD Chains found but no Legacy ScriptPubKeyMan\n"); + result = DBErrors::CORRUPT; + } + } + + // Load watchonly scripts + LoadResult watch_script_res = LoadRecords(pwallet, batch, DBKeys::WATCHS, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + CScript script; + key >> script; + uint8_t fYes; + value >> fYes; + if (fYes == '1') { + pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script); + } + return DBErrors::LOAD_OK; + }); + result = std::max(result, watch_script_res.m_result); + + // Load watchonly meta + LoadResult watch_meta_res = LoadRecords(pwallet, batch, DBKeys::WATCHMETA, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + CScript script; + key >> script; + CKeyMetadata keyMeta; + value >> keyMeta; + pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta); + return DBErrors::LOAD_OK; + }); + result = std::max(result, watch_meta_res.m_result); + + // Load keypool + LoadResult pool_res = LoadRecords(pwallet, batch, DBKeys::POOL, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + int64_t nIndex; + key >> nIndex; + CKeyPool keypool; + value >> keypool; + pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool); + return DBErrors::LOAD_OK; + }); + result = std::max(result, pool_res.m_result); + + if (result <= DBErrors::NONCRITICAL_ERROR) { + // Only do logging and time first key update if there were no critical errors + pwallet->WalletLogPrintf("Legacy Wallet Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total.\n", + key_res.m_records, ckey_res.m_records, keymeta_res.m_records, key_res.m_records + ckey_res.m_records); + + // nTimeFirstKey is only reliable if all keys have metadata + if (pwallet->IsLegacy() && (key_res.m_records + ckey_res.m_records + watch_script_res.m_records) != (keymeta_res.m_records + watch_meta_res.m_records)) { + auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan(); + if (spk_man) { + LOCK(spk_man->cs_KeyStore); + spk_man->UpdateTimeFirstKey(1); + } + } + } + + return result; +} + DBErrors WalletBatch::LoadWallet(CWallet* pwallet) { CWalletScanState wss; @@ -883,6 +1041,9 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) } #endif + // Load legacy wallet keys + result = std::max(LoadLegacyWalletRecords(pwallet, *m_batch, last_client), result); + // Get cursor std::unique_ptr<DatabaseCursor> cursor = m_batch->GetNewCursor(); if (!cursor) @@ -909,17 +1070,9 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) std::string strType, strErr; if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { - if (wss.unexpected_legacy_entry) { - strErr = strprintf("Error: Unexpected legacy entry found in descriptor wallet %s. ", pwallet->GetName()); - strErr += "The wallet might have been tampered with or created with malicious intent."; - pwallet->WalletLogPrintf("%s\n", strErr); - return DBErrors::UNEXPECTED_LEGACY_ENTRY; - } // losing keys is considered a catastrophic error, anything else // we assume the user can live with: - if (strType == DBKeys::KEY || - strType == DBKeys::MASTER_KEY || - strType == DBKeys::CRYPTED_KEY || + if (strType == DBKeys::MASTER_KEY || strType == DBKeys::DEFAULTKEY) { result = DBErrors::CORRUPT; } else if (wss.tx_corrupt) { @@ -946,6 +1099,8 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) pwallet->WalletLogPrintf("%s\n", strErr); } } catch (...) { + // Exceptions that can be ignored or treated as non-critical are handled by the individual loading functions. + // Any uncaught exceptions will be caught here and treated as critical. result = DBErrors::CORRUPT; } @@ -988,22 +1143,9 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n", wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records); - // nTimeFirstKey is only reliable if all keys have metadata - if (pwallet->IsLegacy() && (wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) { - auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan(); - if (spk_man) { - LOCK(spk_man->cs_KeyStore); - spk_man->UpdateTimeFirstKey(1); - } - } - for (const uint256& hash : wss.vWalletUpgrade) WriteTx(pwallet->mapWallet.at(hash)); - // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: - if (wss.fIsEncrypted && (last_client == 40000 || last_client == 50000)) - return DBErrors::NEED_REWRITE; - if (!has_last_client || last_client != CLIENT_VERSION) // Update m_batch->Write(DBKeys::VERSION, CLIENT_VERSION); @@ -1026,20 +1168,6 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) result = DBErrors::CORRUPT; } - // Set the inactive chain - if (wss.m_hd_chains.size() > 0) { - LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan(); - if (!legacy_spkm) { - pwallet->WalletLogPrintf("Inactive HD Chains found but no Legacy ScriptPubKeyMan\n"); - return DBErrors::CORRUPT; - } - for (const auto& chain_pair : wss.m_hd_chains) { - if (chain_pair.first != pwallet->GetLegacyScriptPubKeyMan()->GetHDChain().seed_id) { - pwallet->GetLegacyScriptPubKeyMan()->AddInactiveHDChain(chain_pair.second); - } - } - } - return result; } diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 9be1dcec48..8f7c2f030c 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -42,19 +42,21 @@ struct WalletContext; static const bool DEFAULT_FLUSHWALLET = true; -/** Error statuses for the wallet database */ -enum class DBErrors +/** Error statuses for the wallet database. + * Values are in order of severity. When multiple errors occur, the most severe (highest value) will be returned. + */ +enum class DBErrors : int { - LOAD_OK, - CORRUPT, - NONCRITICAL_ERROR, - TOO_NEW, - EXTERNAL_SIGNER_SUPPORT_REQUIRED, - LOAD_FAIL, - NEED_REWRITE, - NEED_RESCAN, - UNKNOWN_DESCRIPTOR, - UNEXPECTED_LEGACY_ENTRY + LOAD_OK = 0, + NEED_RESCAN = 1, + NEED_REWRITE = 2, + EXTERNAL_SIGNER_SUPPORT_REQUIRED = 3, + NONCRITICAL_ERROR = 4, + TOO_NEW = 5, + UNKNOWN_DESCRIPTOR = 6, + LOAD_FAIL = 7, + UNEXPECTED_LEGACY_ENTRY = 8, + CORRUPT = 9, }; namespace DBKeys { |