6#include <bitcoin-build-config.h>
41const std::string
FLAGS{
"flags"};
44const std::string
KEY{
"key"};
48const std::string
NAME{
"name"};
51const std::string
POOL{
"pool"};
54const std::string
TX{
"tx"};
139 std::vector<unsigned char> val;
140 if (!
m_batch->Read(key, val)) {
300 strErr =
"Error reading wallet database: CPubKey corrupt";
318 catch (
const std::ios_base::failure&) {}
329 strErr =
"Error reading wallet database: CPubKey/CPrivKey corrupt";
338 strErr =
"Error reading wallet database: CPrivKey corrupt";
343 strErr =
"Error reading wallet database: LegacyDataSPKM::LoadKey failed";
346 }
catch (
const std::exception&
e) {
363 strErr =
"Error reading wallet database: CPubKey corrupt";
371 if (!ssValue.
empty()) {
375 strErr =
"Error reading wallet database: Encrypted key corrupt";
382 strErr =
"Error reading wallet database: LegacyDataSPKM::LoadCryptedKey failed";
385 }
catch (
const std::exception&
e) {
412 }
catch (
const std::exception&
e) {
428 }
catch (
const std::exception&
e) {
442 if (!pwallet->LoadWalletFlags(
flags)) {
443 pwallet->WalletLogPrintf(
"Error reading wallet database: Unknown non-tolerable wallet flags found\n");
448 if (pwallet->GetDatabase().Format() !=
"bdb_ro" && !pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
471 pwallet->
WalletLogPrintf(
"Error getting database cursor for '%s' records\n", key);
481 pwallet->
WalletLogPrintf(
"Error reading next '%s' record for wallet database\n", key);
508 const auto& batch =
wallet.GetDatabase().MakeBatch();
523 throw std::runtime_error(
strprintf(
"Error getting database cursor for '%s' records", type));
540 if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
542 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());
562 result = std::max(result,
key_res.m_result);
569 result = std::max(result,
ckey_res.m_result);
580 strErr =
"Error reading wallet database: LegacyDataSPKM::LoadCScript failed";
581 return DBErrors::NONCRITICAL_ERROR;
585 result = std::max(result,
script_res.m_result);
608 bool internal =
false;
611 std::vector<uint32_t> path;
614 path =
keyMeta.key_origin.path;
618 strErr =
"Error reading wallet database: keymeta with invalid HD keypath";
627 if (path.size() != 3) {
628 strErr =
"Error reading wallet database: keymeta found with unexpected path";
631 if (path[0] != 0x80000000) {
632 strErr =
strprintf(
"Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]);
635 if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) {
636 strErr =
strprintf(
"Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]);
639 if ((path[2] & 0x80000000) == 0) {
640 strErr =
strprintf(
"Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]);
643 internal = path[1] == (1 | 0x80000000);
644 index = path[2] & ~0x80000000;
670 for (
const auto& [hd_seed_id, chain] :
hd_chains) {
671 if (hd_seed_id !=
legacy_spkm->GetHDChain().seed_id) {
676 pwallet->
WalletLogPrintf(
"Inactive HD Chains found but no Legacy ScriptPubKeyMan\n");
718 }
catch (
const std::exception&
e) {
723 err =
"Error reading wallet database: Default Key corrupt";
724 return DBErrors::CORRUPT;
733 err =
"Found unsupported 'wkey' record, try loading with version 0.18";
736 result = std::max(result,
wkey_res.m_result);
740 pwallet->
WalletLogPrintf(
"Legacy Wallet Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total.\n",
747template<
typename... Args>
771 }
catch (
const std::ios_base::failure&
e) {
774 "The database might be corrupted or the software version is not compatible with one of your wallet descriptors. ";
775 strErr +=
"Please try running the latest software version";
783 if (
id !=
spkm.GetID()) {
784 strErr =
"The descriptor ID calculated by the wallet differs from the one in DB";
858 strErr =
"Error reading wallet database: descriptor unencrypted key CPubKey corrupt";
873 strErr =
"Error reading wallet database: descriptor unencrypted key CPubKey/CPrivKey corrupt";
879 strErr =
"Error reading wallet database: descriptor unencrypted key CPrivKey corrupt";
885 result = std::max(result,
key_res.m_result);
899 err =
"Error reading wallet database: descriptor encrypted key CPubKey corrupt";
902 std::vector<unsigned char>
privkey;
908 result = std::max(result,
ckey_res.m_result);
916 pwallet->
WalletLogPrintf(
"Descriptors: %u, Descriptor Keys: %u plaintext, %u encrypted, %u total.\n",
931 std::string strAddress;
935 pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label);
936 return DBErrors::LOAD_OK;
938 result = std::max(result,
name_res.m_result);
943 std::string strAddress;
945 std::string purpose_str;
946 value >> purpose_str;
947 std::optional<AddressPurpose> purpose{PurposeFromString(purpose_str)};
959 std::string strAddress, strKey, strValue;
963 const CTxDestination& dest{DecodeDestination(strAddress)};
964 if (
strKey.compare(
"used") == 0) {
971 pwallet->LoadAddressPreviouslySpent(dest);
972 }
else if (
strKey.starts_with(
"rr")) {
975 pwallet->LoadAddressReceiveRequest(dest,
strKey.substr(2),
strValue);
979 result = std::max(result,
dest_res.m_result);
987 DBErrors result = DBErrors::LOAD_OK;
993 DBErrors result = DBErrors::LOAD_OK;
998 auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
1001 err =
"Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.";
1002 result = DBErrors::CORRUPT;
1006 if (wtx.GetHash() != hash)
1009 if (wtx.nOrderPos == -1)
1010 any_unordered = true;
1016 result = std::max(result, DBErrors::NEED_RESCAN);
1020 result = std::max(result,
tx_res.m_result);
1023 LoadResult
locked_utxo_res = LoadRecords(pwallet, batch, DBKeys::LOCKED_UTXO,
1029 pwallet->LoadLockedCoin(COutPoint(hash, n), true);
1030 return DBErrors::LOAD_OK;
1036 LoadResult
order_pos_res = LoadRecords(pwallet, batch, DBKeys::ORDERPOSNEXT,
1039 value >> pwallet->nOrderPosNext;
1040 }
catch (
const std::exception&
e) {
1042 return DBErrors::NONCRITICAL_ERROR;
1044 return DBErrors::LOAD_OK;
1050 for (
auto& [
id, wtx] : pwallet->mapWallet) {
1051 if (wtx.IsCoinBase() && wtx.isInactive()) {
1052 pwallet->AbandonTransaction(wtx);
1062 DBErrors result = DBErrors::LOAD_OK;
1065 std::set<std::pair<OutputType, bool>>
seen_spks;
1066 for (
const auto&
spk_key : {DBKeys::ACTIVEEXTERNALSPK, DBKeys::ACTIVEINTERNALSPK}) {
1074 bool internal =
spk_key == DBKeys::ACTIVEINTERNALSPK;
1077 strErr =
"Multiple ScriptpubKeyMans specified for a single type";
1078 return DBErrors::CORRUPT;
1081 return DBErrors::LOAD_OK;
1083 result = std::max(result,
spkm_res.m_result);
1096 return DBErrors::CORRUPT;
1098 return DBErrors::LOAD_OK;
1105 DBErrors result = DBErrors::LOAD_OK;
1118 if ((result =
LoadWalletFlags(pwallet, *m_batch)) != DBErrors::LOAD_OK)
return result;
1120#ifndef ENABLE_EXTERNAL_SIGNER
1122 pwallet->
WalletLogPrintf(
"Error: External signer wallet being loaded without external signer support compiled\n");
1123 return DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED;
1135 if (result == DBErrors::UNKNOWN_DESCRIPTOR)
return result;
1148 }
catch (std::runtime_error&
e) {
1154 result = DBErrors::CORRUPT;
1157 result = DBErrors::CORRUPT;
1162 if (result != DBErrors::LOAD_OK)
1176 result = DBErrors::CORRUPT;
1184 pwallet->
WalletLogPrintf(
"Detected extraneous encryption keys in this wallet without private keys. Removing extraneous encryption keys.\n");
1186 if (!EraseMasterKey(
id)) {
1187 pwallet->
WalletLogPrintf(
"Error: Unable to remove extraneous encryption key '%u'. Wallet corrupt.\n",
id);
1188 return DBErrors::CORRUPT;
1226bool WalletBatch::WriteAddressPreviouslySpent(
const CTxDestination& dest,
bool previously_spent)
1228 auto key{std::make_pair(DBKeys::DESTDATA, std::make_pair(
EncodeDestination(dest), std::string(
"used")))};
1229 return previously_spent ? WriteIC(key, std::string(
"1")) : EraseIC(key);
1237bool WalletBatch::EraseAddressReceiveRequest(
const CTxDestination& dest,
const std::string&
id)
1239 return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(
EncodeDestination(dest),
"rr" +
id)));
1246 return m_batch->ErasePrefix(
prefix);
1251 return WriteIC(DBKeys::FLAGS,
flags);
1254bool WalletBatch::EraseRecords(
const std::unordered_set<std::string>& types)
1256 return std::all_of(types.begin(), types.end(), [&](
const std::string& type) {
1257 return m_batch->ErasePrefix(DataStream() << type);
1261bool WalletBatch::TxnBegin()
1263 return m_batch->TxnBegin();
1266bool WalletBatch::TxnCommit()
1268 bool res = m_batch->TxnCommit();
1270 for (
const auto&
listener : m_txn_listeners) {
1274 m_txn_listeners.clear();
1279bool WalletBatch::TxnAbort()
1281 bool res = m_batch->TxnAbort();
1283 for (
const auto&
listener : m_txn_listeners) {
1287 m_txn_listeners.clear();
1294 assert(m_batch->HasActiveTxn());
1295 m_txn_listeners.emplace_back(
l);
1302 exists = fs::symlink_status(path).type() != fs::file_type::not_found;
1303 }
catch (
const fs::filesystem_error&
e) {
1305 status = DatabaseStatus::FAILED_BAD_PATH;
1309 std::optional<DatabaseFormat> format;
1312 format = DatabaseFormat::BERKELEY_RO;
1317 status = DatabaseStatus::FAILED_BAD_FORMAT;
1320 format = DatabaseFormat::SQLITE;
1324 status = DatabaseStatus::FAILED_NOT_FOUND;
1330 status = DatabaseStatus::FAILED_BAD_FORMAT;
1336 status = DatabaseStatus::FAILED_ALREADY_EXISTS;
1341 if (format && format == DatabaseFormat::BERKELEY_RO && (!options.
require_format || options.
require_format != DatabaseFormat::BERKELEY_RO)) {
1342 error =
Untranslated(
strprintf(
"Failed to open database path '%s'. The wallet appears to be a Legacy wallet, please use the wallet migration tool (migratewallet RPC or the GUI option).",
fs::PathToString(path)));
1343 status = DatabaseStatus::FAILED_LEGACY_DISABLED;
1350 status = DatabaseStatus::FAILED_BAD_FORMAT;
1358 format = DatabaseFormat::SQLITE;
1361 if (format == DatabaseFormat::SQLITE) {
1365 if (format == DatabaseFormat::BERKELEY_RO) {
1370 status = DatabaseStatus::FAILED_BAD_FORMAT;
std::variant< CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown > CTxDestination
A txout script categorized into standard templates.
bool ParseHDKeypath(const std::string &keypath_str, std::vector< uint32_t > &keypath)
Parse an HD keypaths like "m/7/0'/2000".
catch(const std::exception &e)
#define STR_INTERNAL_BUG(msg)
#define Assume(val)
Assume is the identity function.
An encapsulated private key.
bool Load(const CPrivKey &privkey, const CPubKey &vchPubKey, bool fSkipCheck)
Load private key and check that public key matches.
An outpoint - a combination of a transaction hash and an index n into its vout.
An encapsulated public key.
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Serialized script, used inside transaction inputs and outputs.
A reference to a CScript: the Hash160 of its serialization.
Double ended buffer combining vector and stream-like interfaces.
Cache for single descriptor's derived extended pubkeys.
std::unordered_map< uint32_t, ExtPubKeyMap > GetCachedDerivedExtPubKeys() const
Retrieve all cached derived xpubs.
void CacheDerivedExtPubKey(uint32_t key_exp_pos, uint32_t der_index, const CExtPubKey &xpub)
Cache an xpub derived at an index.
ExtPubKeyMap GetCachedParentExtPubKeys() const
Retrieve all cached parent xpubs.
ExtPubKeyMap GetCachedLastHardenedExtPubKeys() const
Retrieve all cached last hardened xpubs.
void CacheParentExtPubKey(uint32_t key_exp_pos, const CExtPubKey &xpub)
Cache a parent xpub.
void CacheLastHardenedExtPubKey(uint32_t key_exp_pos, const CExtPubKey &xpub)
Cache a last hardened xpub.
constexpr bool IsNull() const
const uint256 & ToUint256() const LIFETIMEBOUND
uint32_t nInternalChainCounter
static const int VERSION_HD_BASE
uint32_t nExternalChainCounter
static const int VERSION_HD_CHAIN_SPLIT
CKeyID seed_id
seed hash160
Private key encryption is done based on a CMasterKey, which holds a salt and random encryption key.
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
void LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal)
Loads an active ScriptPubKeyMan for the specified type and internal.
unsigned int nMasterKeyMaxID
LegacyDataSPKM * GetLegacyDataSPKM() const
Get the LegacyScriptPubKeyMan which is used for all types, internal, and external.
DescriptorScriptPubKeyMan & LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor &desc)
Instantiate a descriptor ScriptPubKeyMan from the WalletDescriptor and load it.
bool HaveCryptedKeys() const
LegacyDataSPKM * GetOrCreateLegacyDataSPKM()
const std::string & GetName() const
Get a name for this wallet for logging/debugging purposes.
void WalletLogPrintf(util::ConstevalFormatString< sizeof...(Params)> wallet_fmt, const Params &... params) const
Prepends the wallet name in logging output to ease debugging in multi-wallet use cases.
bool HasEncryptionKeys() const override
MasterKeyMap mapMasterKeys
ScriptPubKeyMan * GetScriptPubKeyMan(const OutputType &type, bool internal) const
Get the ScriptPubKeyMan for the given OutputType and internal/external chain.
RecursiveMutex cs_wallet
Main wallet lock.
A transaction with a bunch of additional info that only the owner cares about.
const Txid & GetHash() const LIFETIMEBOUND
RAII class that provides access to a WalletDatabase.
virtual std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(std::span< const std::byte > prefix)=0
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector< unsigned char > &vchCryptedSecret, bool checksum_valid)
Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
void AddInactiveHDChain(const CHDChain &chain)
bool LoadKey(const CKey &key, const CPubKey &pubkey)
Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadCScript(const CScript &redeemScript)
Adds a CScript to the store.
void LoadHDChain(const CHDChain &chain)
Load a HD chain model (used by LoadWallet)
Access to the wallet database.
bool WriteDescriptor(const uint256 &desc_id, const WalletDescriptor &descriptor)
bool TxnAbort()
Abort current transaction.
bool WriteDescriptorParentCache(const CExtPubKey &xpub, const uint256 &desc_id, uint32_t key_exp_index)
bool EraseName(const std::string &strAddress)
bool WriteBestBlock(const CBlockLocator &locator)
bool ReadBestBlock(CBlockLocator &locator)
bool WriteDescriptorCacheItems(const uint256 &desc_id, const DescriptorCache &cache)
bool WriteMasterKey(unsigned int nID, const CMasterKey &kMasterKey)
bool WriteWatchOnly(const CScript &script, const CKeyMetadata &keymeta)
bool TxnBegin()
Begin a new transaction.
bool TxnCommit()
Commit current transaction.
bool WriteName(const std::string &strAddress, const std::string &strName)
bool WritePurpose(const std::string &strAddress, const std::string &purpose)
std::unique_ptr< DatabaseBatch > m_batch
bool WriteDescriptorLastHardenedCache(const CExtPubKey &xpub, const uint256 &desc_id, uint32_t key_exp_index)
bool WriteIC(const K &key, const T &value, bool fOverwrite=true)
bool WriteOrderPosNext(int64_t nOrderPosNext)
bool WriteTx(const CWalletTx &wtx)
bool WriteKey(const CPubKey &vchPubKey, const CPrivKey &vchPrivKey, const CKeyMetadata &keyMeta)
bool EraseIC(const K &key)
bool WriteCryptedKey(const CPubKey &vchPubKey, const std::vector< unsigned char > &vchCryptedSecret, const CKeyMetadata &keyMeta)
bool ErasePurpose(const std::string &strAddress)
bool EraseLockedUTXO(const COutPoint &output)
bool WriteDescriptorDerivedCache(const CExtPubKey &xpub, const uint256 &desc_id, uint32_t key_exp_index, uint32_t der_index)
bool WriteCryptedDescriptorKey(const uint256 &desc_id, const CPubKey &pubkey, const std::vector< unsigned char > &secret)
bool WriteLockedUTXO(const COutPoint &output)
bool EraseMasterKey(unsigned int id)
bool WriteActiveScriptPubKeyMan(uint8_t type, const uint256 &id, bool internal)
bool EraseActiveScriptPubKeyMan(uint8_t type, bool internal)
bool WriteKeyMetadata(const CKeyMetadata &meta, const CPubKey &pubkey, bool overwrite)
bool WriteDescriptorKey(const uint256 &desc_id, const CPubKey &pubkey, const CPrivKey &privkey)
bool EraseWatchOnly(const CScript &script)
An instance of this class represents one database.
Descriptor with some wallet metadata.
static const int CLIENT_VERSION
static std::string PathToString(const path &path)
Convert path object to a byte string.
bool IsWalletFlagSet(uint64_t flag) const override
check if a certain wallet flag is set
bool LoadToWallet(const Txid &hash, const UpdateWalletTxFn &fill_wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
DBErrors ReorderTransactions()
void UpgradeDescriptorCache() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Upgrade DescriptorCaches.
uint256 Hash(const T &in1)
Compute the 256-bit hash of an object.
std::vector< unsigned char, secure_allocator< unsigned char > > CPrivKey
CPrivKey is a serialized private key, with all parameters included (SIZE bytes)
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg, std::vector< int > *error_locations)
std::string EncodeDestination(const CTxDestination &dest)
#define LogDebug(category,...)
const std::string BESTBLOCK
const std::string WALLETDESCRIPTORCKEY
const std::string WALLETDESCRIPTORLHCACHE
const std::string MINVERSION
const std::string WATCHMETA
const std::string DEFAULTKEY
const std::string OLD_KEY
const std::string WALLETDESCRIPTORKEY
const std::string ACENTRY
const std::string ACTIVEEXTERNALSPK
const std::string CRYPTED_KEY
const std::string DESTDATA
const std::string CSCRIPT
const std::unordered_set< std::string > LEGACY_TYPES
const std::string SETTINGS
const std::string BESTBLOCK_NOMERKLE
const std::string LOCKED_UTXO
const std::string ACTIVEINTERNALSPK
const std::string HDCHAIN
const std::string ORDERPOSNEXT
const std::string VERSION
const std::string WALLETDESCRIPTORCACHE
const std::string MASTER_KEY
const std::string KEYMETA
const std::string PURPOSE
const std::string WALLETDESCRIPTOR
std::unique_ptr< WalletDatabase > MakeDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
bool LoadKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
static DataStream PrefixStream(const Args &... args)
static DBErrors LoadLegacyWalletRecords(CWallet *pwallet, DatabaseBatch &batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
static bool RunWithinTxn(WalletBatch &batch, std::string_view process_desc, const std::function< bool(WalletBatch &)> &func)
std::function< DBErrors(CWallet *pwallet, DataStream &key, DataStream &value, std::string &err)> LoadFunc
bool LoadCryptedKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
std::unique_ptr< SQLiteDatabase > MakeSQLiteDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
fs::path SQLiteDataFile(const fs::path &path)
DBErrors
Overview of wallet database classes:
@ UNEXPECTED_LEGACY_ENTRY
static DBErrors LoadWalletFlags(CWallet *pwallet, DatabaseBatch &batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
static DBErrors LoadActiveSPKMs(CWallet *pwallet, DatabaseBatch &batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
static DBErrors LoadDecryptionKeys(CWallet *pwallet, DatabaseBatch &batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
bool LoadEncryptionKey(CWallet *pwallet, DataStream &ssKey, DataStream &ssValue, std::string &strErr)
bool HasLegacyRecords(CWallet &wallet)
Returns true if there are any DBKeys::LEGACY_TYPES record in the wallet db.
bool IsBDBFile(const fs::path &path)
fs::path BDBDataFile(const fs::path &wallet_path)
bool LoadHDChain(CWallet *pwallet, DataStream &ssValue, std::string &strErr)
static DBErrors LoadTxRecords(CWallet *pwallet, DatabaseBatch &batch, bool &any_unordered) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
std::unique_ptr< BerkeleyRODatabase > MakeBerkeleyRODatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Return object giving access to Berkeley Read Only database at specified path.
std::string SQLiteDatabaseVersion()
bool IsSQLiteFile(const fs::path &path)
static DBErrors LoadAddressBookRecords(CWallet *pwallet, DatabaseBatch &batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
static LoadResult LoadRecords(CWallet *pwallet, DatabaseBatch &batch, const std::string &key, DataStream &prefix, LoadFunc load_func)
static DBErrors LoadDescriptorWalletRecords(CWallet *pwallet, DatabaseBatch &batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet -> cs_wallet)
const unsigned int BIP32_EXTKEY_SIZE
void SerializeMany(Stream &s, const Args &... args)
Support for (un)serializing many things at once.
Describes a place in the block chain to another node such that if the other node doesn't have the sam...
std::vector< uint256 > vHave
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE])
std::optional< DatabaseFormat > require_format
#define AssertLockHeld(cs)
#define EXCLUSIVE_LOCKS_REQUIRED(...)
consteval auto _(util::TranslatedLiteral str)
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.