Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1001while (true) {1002DatabaseCursor::Status status = cursor->Next(key, value);1003if (status == DatabaseCursor::Status::DONE) break;1004if (status == DatabaseCursor::Status::FAIL) {1005throw std::runtime_error(strprintf("Error reading '%s' record", DBKeys::WTX_VARIANT));1006}1007CTransactionRef tx;1008value >> TX_WITH_WITNESS(tx);1009if (tx->GetHash() != txid) {1010throw std::runtime_error(strprintf("Corrupted witness variant, tx hash differs"));1011}1012if (!variants.emplace(tx->GetWitnessHash(), std::move(tx)).second) {1013throw std::runtime_error(strprintf("Duplicate witness variant"));1014}1015}1016return variants;1017}1019static DBErrors LoadTxRecords(CWallet* pwallet, DatabaseBatch& batch, bool& any_unordered) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)1020{1021AssertLockHeld(pwallet->cs_wallet);1022DBErrors result = DBErrors::LOAD_OK;1024// Load tx record1025any_unordered = false;1026LoadResult tx_res = LoadRecords(pwallet, batch, DBKeys::TX,1027[&any_unordered, &batch] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {1028DBErrors result = DBErrors::LOAD_OK;1029Txid hash;1030key >> hash;1031try {1032CWalletTx wtx{deserialize, value, ReadWtxVariants(batch, hash)};1033if (wtx.GetHash() != hash) {1034result = std::max(result, DBErrors::NEED_RESCAN);1035}1037if (wtx.nOrderPos == -1) {1038any_unordered = true;1039}1041if (!pwallet->LoadToWallet(std::move(wtx))) {1042err = "Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.";1043return DBErrors::CORRUPT;1044}1045} catch (const std::exception& e) {1046err = strprintf("Error: Corrupt tx record found: %s" ,e.what());1047return DBErrors::CORRUPT;1048}1049return result;1050});1051result = std::max(result, tx_res.m_result);1053// Load locked utxo record1054LoadResult locked_utxo_res = LoadRecords(pwallet, batch, DBKeys::LOCKED_UTXO,1055[] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {1056Txid hash;1057uint32_t n;1058key >> hash;1059key >> n;1060pwallet->LoadLockedCoin(COutPoint(hash, n), /*persistent=*/true);1061return DBErrors::LOAD_OK;1062});1063result = std::max(result, locked_utxo_res.m_result);1065// Load orderposnext record1066// Note: There should only be one ORDERPOSNEXT record with nothing trailing the type1067LoadResult order_pos_res = LoadRecords(pwallet, batch, DBKeys::ORDERPOSNEXT,1068[] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {1069try {1070value >> pwallet->nOrderPosNext;1071} catch (const std::exception& e) {1072err = e.what();1073return DBErrors::NONCRITICAL_ERROR;1074}1075return DBErrors::LOAD_OK;1076});1077result = std::max(result, order_pos_res.m_result);1079// After loading all tx records, abandon any coinbase that is no longer in the active chain.1080// This could happen during an external wallet load, or if the user replaced the chain data.1081for (auto& [id, wtx] : pwallet->mapWallet) {1082if (wtx.IsCoinBase() && wtx.isInactive()) {1083pwallet->AbandonTransaction(wtx);1084}1085}1087return result;1088}1090static DBErrors LoadActiveSPKMs(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)1091{1092AssertLockHeld(pwallet->cs_wallet);1093DBErrors result = DBErrors::LOAD_OK;1095// Load spk records1096std::set<std::pair<OutputType, bool>> seen_spks;1097for (const auto& spk_key : {DBKeys::ACTIVEEXTERNALSPK, DBKeys::ACTIVEINTERNALSPK}) {1098LoadResult spkm_res = LoadRecords(pwallet, batch, spk_key,1099[&seen_spks, &spk_key] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {1100uint8_t output_type;1101key >> output_type;1102uint256 id;1103value >> id;1105bool internal = spk_key == DBKeys::ACTIVEINTERNALSPK;1106auto [it, insert] = seen_spks.emplace(static_cast<OutputType>(output_type), internal);1107if (!insert) {1108strErr = "Multiple ScriptpubKeyMans specified for a single type";1109return DBErrors::CORRUPT;1110}1111pwallet->LoadActiveScriptPubKeyMan(id, static_cast<OutputType>(output_type), /*internal=*/internal);1112return DBErrors::LOAD_OK;1113});1114result = std::max(result, spkm_res.m_result);1115}1116return result;1117}1119static DBErrors LoadDecryptionKeys(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)1120{1121AssertLockHeld(pwallet->cs_wallet);1123// Load decryption key (mkey) records1124LoadResult mkey_res = LoadRecords(pwallet, batch, DBKeys::MASTER_KEY,1125[] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {1126if (!LoadEncryptionKey(pwallet, key, value, err)) {1127return DBErrors::CORRUPT;1128}1129return DBErrors::LOAD_OK;1130});1131return mkey_res.m_result;1132}1134DBErrors WalletBatch::LoadWallet(CWallet* pwallet)1135{1136DBErrors result = DBErrors::LOAD_OK;1137bool any_unordered = false;1139LOCK(pwallet->cs_wallet);1141// Last client version to open this wallet1142int last_client = CLIENT_VERSION;1143bool has_last_client = m_batch->Read(DBKeys::VERSION, last_client);1144if (has_last_client) pwallet->WalletLogPrintf("Last client version = %d\n", last_client);1146try {1147// Load wallet flags, so they are known when processing other records.1148// The FLAGS key is absent during wallet creation.1149if ((result = LoadWalletFlags(pwallet, *m_batch)) != DBErrors::LOAD_OK) return result;1151#ifndef ENABLE_EXTERNAL_SIGNER1152if (pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {1153pwallet->WalletLogPrintf("Error: External signer wallet being loaded without external signer support compiled\n");1154return DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED;1155}1156#endif1158// Load legacy wallet keys1159result = std::max(LoadLegacyWalletRecords(pwallet, *m_batch, last_client), result);1161// Load descriptors1162result = std::max(LoadDescriptorWalletRecords(pwallet, *m_batch, last_client), result);1163// Early return if there are unknown descriptors. Later loading of ACTIVEINTERNALSPK and ACTIVEEXTERNALEXPK1164// may reference the unknown descriptor's ID which can result in a misleading corruption error1165// when in reality the wallet is simply too new.1166if (result == DBErrors::UNKNOWN_DESCRIPTOR) return result;1168// Load address book1169result = std::max(LoadAddressBookRecords(pwallet, *m_batch), result);1171// Load SPKMs1172result = std::max(LoadActiveSPKMs(pwallet, *m_batch), result);1174// Load decryption keys1175result = std::max(LoadDecryptionKeys(pwallet, *m_batch), result);1177// Load tx records1178result = std::max(LoadTxRecords(pwallet, *m_batch, any_unordered), result);1179} catch (std::runtime_error& e) {1180// Exceptions that can be ignored or treated as non-critical are handled by the individual loading functions.1181// Any uncaught exceptions will be caught here and treated as critical.1182// Catch std::runtime_error specifically as many functions throw these and they at least have some message that1183// we can log1184pwallet->WalletLogPrintf("%s\n", e.what());1185result = DBErrors::CORRUPT;1186} catch (...) {1187// All other exceptions are still problematic, but we can't log them1188result = DBErrors::CORRUPT;1189}1191// Any wallet corruption at all: skip any rewriting or1192// upgrading, we don't want to make it worse.1193if (result != DBErrors::LOAD_OK)1194return result;1196if (!has_last_client || last_client != CLIENT_VERSION) // Update1197this->WriteVersion(CLIENT_VERSION);1199if (any_unordered)1200result = pwallet->ReorderTransactions();1202// Upgrade all of the descriptor caches to cache the last hardened xpub1203// This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible1204try {1205pwallet->UpgradeDescriptorCache();1206} catch (...) {1207result = DBErrors::CORRUPT;1208}1210// Since it was accidentally possible to "encrypt" a wallet with private keys disabled, we should check if this is1211// such a wallet and remove the encryption key records to avoid any future issues.1212// Although wallets without private keys should not have *ckey records, we should double check that.1213// Removing the mkey records is only safe if there are no *ckey records.1214if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && pwallet->HasEncryptionKeys() && !pwallet->HaveCryptedKeys()) {1215pwallet->WalletLogPrintf("Detected extraneous encryption keys in this wallet without private keys. Removing extraneous encryption keys.\n");1216for (const auto& [id, _] : pwallet->mapMasterKeys) {1217if (!EraseMasterKey(id)) {1218pwallet->WalletLogPrintf("Error: Unable to remove extraneous encryption key '%u'. Wallet corrupt.\n", id);1219return DBErrors::CORRUPT;1220}1221}1222pwallet->mapMasterKeys.clear();1223}1225return result;1226}1228static bool RunWithinTxn(WalletBatch& batch, std::string_view process_desc, const std::function<bool(WalletBatch&)>& func)1229{1230if (!batch.TxnBegin()) {1231LogDebug(BCLog::WALLETDB, "Error: cannot create db txn for %s\n", process_desc);1232return false;1233}1235// Run procedure1236if (!func(batch)) {1237LogDebug(BCLog::WALLETDB, "Error: %s failed\n", process_desc);1238batch.TxnAbort();1239return false;1240}1242if (!batch.TxnCommit()) {1243LogDebug(BCLog::WALLETDB, "Error: cannot commit db txn for %s\n", process_desc);1244return false;1245}1247// All good1248return true;1249}