Skip to content

Commit 6fabb7f

Browse files
committed
walletdb: refactor tx loading
Instead of loading tx records as we come across them when iterating the database, load them explicitly.
1 parent abcc13d commit 6fabb7f

File tree

1 file changed

+96
-68
lines changed

1 file changed

+96
-68
lines changed

src/wallet/walletdb.cpp

Lines changed: 96 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -301,11 +301,8 @@ bool WalletBatch::EraseLockedUTXO(const COutPoint& output)
301301
class CWalletScanState {
302302
public:
303303
unsigned int m_unknown_records{0};
304-
bool fAnyUnordered{false};
305-
std::vector<uint256> vWalletUpgrade;
306304
std::map<OutputType, uint256> m_active_external_spks;
307305
std::map<OutputType, uint256> m_active_internal_spks;
308-
bool tx_corrupt{false};
309306

310307
CWalletScanState() = default;
311308
};
@@ -473,51 +470,6 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue,
473470
if (strType == DBKeys::NAME) {
474471
} else if (strType == DBKeys::PURPOSE) {
475472
} else if (strType == DBKeys::TX) {
476-
uint256 hash;
477-
ssKey >> hash;
478-
// LoadToWallet call below creates a new CWalletTx that fill_wtx
479-
// callback fills with transaction metadata.
480-
auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
481-
if(!new_tx) {
482-
// There's some corruption here since the tx we just tried to load was already in the wallet.
483-
// We don't consider this type of corruption critical, and can fix it by removing tx data and
484-
// rescanning.
485-
wss.tx_corrupt = true;
486-
return false;
487-
}
488-
ssValue >> wtx;
489-
if (wtx.GetHash() != hash)
490-
return false;
491-
492-
// Undo serialize changes in 31600
493-
if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
494-
{
495-
if (!ssValue.empty())
496-
{
497-
uint8_t fTmp;
498-
uint8_t fUnused;
499-
std::string unused_string;
500-
ssValue >> fTmp >> fUnused >> unused_string;
501-
strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s",
502-
wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString());
503-
wtx.fTimeReceivedIsTxTime = fTmp;
504-
}
505-
else
506-
{
507-
strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
508-
wtx.fTimeReceivedIsTxTime = 0;
509-
}
510-
wss.vWalletUpgrade.push_back(hash);
511-
}
512-
513-
if (wtx.nOrderPos == -1)
514-
wss.fAnyUnordered = true;
515-
516-
return true;
517-
};
518-
if (!pwallet->LoadToWallet(hash, fill_wtx)) {
519-
return false;
520-
}
521473
} else if (strType == DBKeys::WATCHS) {
522474
} else if (strType == DBKeys::KEY) {
523475
} else if (strType == DBKeys::MASTER_KEY) {
@@ -537,7 +489,6 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue,
537489
} else if (strType == DBKeys::POOL) {
538490
} else if (strType == DBKeys::CSCRIPT) {
539491
} else if (strType == DBKeys::ORDERPOSNEXT) {
540-
ssValue >> pwallet->nOrderPosNext;
541492
} else if (strType == DBKeys::DESTDATA) {
542493
} else if (strType == DBKeys::HDCHAIN) {
543494
} else if (strType == DBKeys::OLD_KEY) {
@@ -562,11 +513,6 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue,
562513
} else if (strType == DBKeys::WALLETDESCRIPTORKEY) {
563514
} else if (strType == DBKeys::WALLETDESCRIPTORCKEY) {
564515
} else if (strType == DBKeys::LOCKED_UTXO) {
565-
uint256 hash;
566-
uint32_t n;
567-
ssKey >> hash;
568-
ssKey >> n;
569-
pwallet->LockCoin(COutPoint(hash, n));
570516
} else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
571517
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
572518
strType != DBKeys::VERSION && strType != DBKeys::SETTINGS &&
@@ -1112,12 +1058,101 @@ static DBErrors LoadAddressBookRecords(CWallet* pwallet, DatabaseBatch& batch) E
11121058
return result;
11131059
}
11141060

1061+
static DBErrors LoadTxRecords(CWallet* pwallet, DatabaseBatch& batch, std::vector<uint256>& upgraded_txs, bool& any_unordered) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
1062+
{
1063+
AssertLockHeld(pwallet->cs_wallet);
1064+
DBErrors result = DBErrors::LOAD_OK;
1065+
1066+
// Load tx record
1067+
any_unordered = false;
1068+
LoadResult tx_res = LoadRecords(pwallet, batch, DBKeys::TX,
1069+
[&any_unordered, &upgraded_txs] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1070+
DBErrors result = DBErrors::LOAD_OK;
1071+
uint256 hash;
1072+
key >> hash;
1073+
// LoadToWallet call below creates a new CWalletTx that fill_wtx
1074+
// callback fills with transaction metadata.
1075+
auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
1076+
if(!new_tx) {
1077+
// There's some corruption here since the tx we just tried to load was already in the wallet.
1078+
err = "Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.";
1079+
result = DBErrors::CORRUPT;
1080+
return false;
1081+
}
1082+
value >> wtx;
1083+
if (wtx.GetHash() != hash)
1084+
return false;
1085+
1086+
// Undo serialize changes in 31600
1087+
if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
1088+
{
1089+
if (!value.empty())
1090+
{
1091+
uint8_t fTmp;
1092+
uint8_t fUnused;
1093+
std::string unused_string;
1094+
value >> fTmp >> fUnused >> unused_string;
1095+
pwallet->WalletLogPrintf("LoadWallet() upgrading tx ver=%d %d %s\n",
1096+
wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString());
1097+
wtx.fTimeReceivedIsTxTime = fTmp;
1098+
}
1099+
else
1100+
{
1101+
pwallet->WalletLogPrintf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString());
1102+
wtx.fTimeReceivedIsTxTime = 0;
1103+
}
1104+
upgraded_txs.push_back(hash);
1105+
}
1106+
1107+
if (wtx.nOrderPos == -1)
1108+
any_unordered = true;
1109+
1110+
return true;
1111+
};
1112+
if (!pwallet->LoadToWallet(hash, fill_wtx)) {
1113+
// Use std::max as fill_wtx may have already set result to CORRUPT
1114+
result = std::max(result, DBErrors::NEED_RESCAN);
1115+
}
1116+
return result;
1117+
});
1118+
result = std::max(result, tx_res.m_result);
1119+
1120+
// Load locked utxo record
1121+
LoadResult locked_utxo_res = LoadRecords(pwallet, batch, DBKeys::LOCKED_UTXO,
1122+
[] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1123+
uint256 hash;
1124+
uint32_t n;
1125+
key >> hash;
1126+
key >> n;
1127+
pwallet->LockCoin(COutPoint(hash, n));
1128+
return DBErrors::LOAD_OK;
1129+
});
1130+
result = std::max(result, locked_utxo_res.m_result);
1131+
1132+
// Load orderposnext record
1133+
// Note: There should only be one ORDERPOSNEXT record with nothing trailing the type
1134+
LoadResult order_pos_res = LoadRecords(pwallet, batch, DBKeys::ORDERPOSNEXT,
1135+
[] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1136+
try {
1137+
value >> pwallet->nOrderPosNext;
1138+
} catch (const std::exception& e) {
1139+
err = e.what();
1140+
return DBErrors::NONCRITICAL_ERROR;
1141+
}
1142+
return DBErrors::LOAD_OK;
1143+
});
1144+
result = std::max(result, order_pos_res.m_result);
1145+
1146+
return result;
1147+
}
1148+
11151149
DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
11161150
{
11171151
CWalletScanState wss;
11181152
bool fNoncriticalErrors = false;
1119-
bool rescan_required = false;
11201153
DBErrors result = DBErrors::LOAD_OK;
1154+
bool any_unordered = false;
1155+
std::vector<uint256> upgraded_txs;
11211156

11221157
LOCK(pwallet->cs_wallet);
11231158

@@ -1153,6 +1188,9 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
11531188
// Load address book
11541189
result = std::max(LoadAddressBookRecords(pwallet, *m_batch), result);
11551190

1191+
// Load tx records
1192+
result = std::max(LoadTxRecords(pwallet, *m_batch, upgraded_txs, any_unordered), result);
1193+
11561194
// Get cursor
11571195
std::unique_ptr<DatabaseCursor> cursor = m_batch->GetNewCursor();
11581196
if (!cursor)
@@ -1184,17 +1222,9 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
11841222
if (strType == DBKeys::MASTER_KEY ||
11851223
strType == DBKeys::DEFAULTKEY) {
11861224
result = DBErrors::CORRUPT;
1187-
} else if (wss.tx_corrupt) {
1188-
pwallet->WalletLogPrintf("Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.\n");
1189-
// Set tx_corrupt back to false so that the error is only printed once (per corrupt tx)
1190-
wss.tx_corrupt = false;
1191-
result = DBErrors::CORRUPT;
11921225
} else {
11931226
// Leave other errors alone, if we try to fix them we might make things worse.
11941227
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
1195-
if (strType == DBKeys::TX)
1196-
// Rescan if there is a bad transaction record:
1197-
rescan_required = true;
11981228
}
11991229
}
12001230
if (!strErr.empty())
@@ -1214,9 +1244,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
12141244
pwallet->LoadActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /*internal=*/true);
12151245
}
12161246

1217-
if (rescan_required && result == DBErrors::LOAD_OK) {
1218-
result = DBErrors::NEED_RESCAN;
1219-
} else if (fNoncriticalErrors && result == DBErrors::LOAD_OK) {
1247+
if (fNoncriticalErrors && result == DBErrors::LOAD_OK) {
12201248
result = DBErrors::NONCRITICAL_ERROR;
12211249
}
12221250

@@ -1225,13 +1253,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
12251253
if (result != DBErrors::LOAD_OK)
12261254
return result;
12271255

1228-
for (const uint256& hash : wss.vWalletUpgrade)
1256+
for (const uint256& hash : upgraded_txs)
12291257
WriteTx(pwallet->mapWallet.at(hash));
12301258

12311259
if (!has_last_client || last_client != CLIENT_VERSION) // Update
12321260
m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);
12331261

1234-
if (wss.fAnyUnordered)
1262+
if (any_unordered)
12351263
result = pwallet->ReorderTransactions();
12361264

12371265
// Upgrade all of the wallet keymetadata to have the hd master key id

0 commit comments

Comments
 (0)