aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/walletdb.cpp
diff options
context:
space:
mode:
authorAndrew Chow <achow101-github@achow101.com>2022-04-13 12:18:11 -0400
committerAndrew Chow <github@achow101.com>2023-06-27 11:07:38 -0400
commit6fabb7fc99e60584d5f3a2cb01d39f761769a25d (patch)
tree7ba297d0eff2ba939efc1791cd42735d48c337a8 /src/wallet/walletdb.cpp
parentabcc13dd24889bc1c6af7b10da1da96d86aeafed (diff)
downloadbitcoin-6fabb7fc99e60584d5f3a2cb01d39f761769a25d.tar.xz
walletdb: refactor tx loading
Instead of loading tx records as we come across them when iterating the database, load them explicitly.
Diffstat (limited to 'src/wallet/walletdb.cpp')
-rw-r--r--src/wallet/walletdb.cpp164
1 files changed, 96 insertions, 68 deletions
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index c0a2cb6d60..f3a82734c1 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -301,11 +301,8 @@ bool WalletBatch::EraseLockedUTXO(const COutPoint& output)
class CWalletScanState {
public:
unsigned int m_unknown_records{0};
- bool fAnyUnordered{false};
- std::vector<uint256> vWalletUpgrade;
std::map<OutputType, uint256> m_active_external_spks;
std::map<OutputType, uint256> m_active_internal_spks;
- bool tx_corrupt{false};
CWalletScanState() = default;
};
@@ -473,51 +470,6 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue,
if (strType == DBKeys::NAME) {
} else if (strType == DBKeys::PURPOSE) {
} else if (strType == DBKeys::TX) {
- uint256 hash;
- ssKey >> hash;
- // LoadToWallet call below creates a new CWalletTx that fill_wtx
- // callback fills with transaction metadata.
- auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
- if(!new_tx) {
- // There's some corruption here since the tx we just tried to load was already in the wallet.
- // We don't consider this type of corruption critical, and can fix it by removing tx data and
- // rescanning.
- wss.tx_corrupt = true;
- return false;
- }
- ssValue >> wtx;
- if (wtx.GetHash() != hash)
- return false;
-
- // Undo serialize changes in 31600
- if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
- {
- if (!ssValue.empty())
- {
- uint8_t fTmp;
- uint8_t fUnused;
- std::string unused_string;
- ssValue >> fTmp >> fUnused >> unused_string;
- strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s",
- wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString());
- wtx.fTimeReceivedIsTxTime = fTmp;
- }
- else
- {
- strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
- wtx.fTimeReceivedIsTxTime = 0;
- }
- wss.vWalletUpgrade.push_back(hash);
- }
-
- if (wtx.nOrderPos == -1)
- wss.fAnyUnordered = true;
-
- return true;
- };
- if (!pwallet->LoadToWallet(hash, fill_wtx)) {
- return false;
- }
} else if (strType == DBKeys::WATCHS) {
} else if (strType == DBKeys::KEY) {
} else if (strType == DBKeys::MASTER_KEY) {
@@ -537,7 +489,6 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue,
} else if (strType == DBKeys::POOL) {
} else if (strType == DBKeys::CSCRIPT) {
} else if (strType == DBKeys::ORDERPOSNEXT) {
- ssValue >> pwallet->nOrderPosNext;
} else if (strType == DBKeys::DESTDATA) {
} else if (strType == DBKeys::HDCHAIN) {
} else if (strType == DBKeys::OLD_KEY) {
@@ -562,11 +513,6 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue,
} else if (strType == DBKeys::WALLETDESCRIPTORKEY) {
} else if (strType == DBKeys::WALLETDESCRIPTORCKEY) {
} else if (strType == DBKeys::LOCKED_UTXO) {
- uint256 hash;
- uint32_t n;
- ssKey >> hash;
- ssKey >> n;
- pwallet->LockCoin(COutPoint(hash, n));
} else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
strType != DBKeys::VERSION && strType != DBKeys::SETTINGS &&
@@ -1112,12 +1058,101 @@ static DBErrors LoadAddressBookRecords(CWallet* pwallet, DatabaseBatch& batch) E
return result;
}
+static DBErrors LoadTxRecords(CWallet* pwallet, DatabaseBatch& batch, std::vector<uint256>& upgraded_txs, bool& any_unordered) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+{
+ AssertLockHeld(pwallet->cs_wallet);
+ DBErrors result = DBErrors::LOAD_OK;
+
+ // Load tx record
+ any_unordered = false;
+ LoadResult tx_res = LoadRecords(pwallet, batch, DBKeys::TX,
+ [&any_unordered, &upgraded_txs] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
+ DBErrors result = DBErrors::LOAD_OK;
+ uint256 hash;
+ key >> hash;
+ // LoadToWallet call below creates a new CWalletTx that fill_wtx
+ // callback fills with transaction metadata.
+ auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
+ if(!new_tx) {
+ // There's some corruption here since the tx we just tried to load was already in the wallet.
+ err = "Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.";
+ result = DBErrors::CORRUPT;
+ return false;
+ }
+ value >> wtx;
+ if (wtx.GetHash() != hash)
+ return false;
+
+ // Undo serialize changes in 31600
+ if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
+ {
+ if (!value.empty())
+ {
+ uint8_t fTmp;
+ uint8_t fUnused;
+ std::string unused_string;
+ value >> fTmp >> fUnused >> unused_string;
+ pwallet->WalletLogPrintf("LoadWallet() upgrading tx ver=%d %d %s\n",
+ wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString());
+ wtx.fTimeReceivedIsTxTime = fTmp;
+ }
+ else
+ {
+ pwallet->WalletLogPrintf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString());
+ wtx.fTimeReceivedIsTxTime = 0;
+ }
+ upgraded_txs.push_back(hash);
+ }
+
+ if (wtx.nOrderPos == -1)
+ any_unordered = true;
+
+ return true;
+ };
+ if (!pwallet->LoadToWallet(hash, fill_wtx)) {
+ // Use std::max as fill_wtx may have already set result to CORRUPT
+ result = std::max(result, DBErrors::NEED_RESCAN);
+ }
+ return result;
+ });
+ result = std::max(result, tx_res.m_result);
+
+ // Load locked utxo record
+ LoadResult locked_utxo_res = LoadRecords(pwallet, batch, DBKeys::LOCKED_UTXO,
+ [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
+ uint256 hash;
+ uint32_t n;
+ key >> hash;
+ key >> n;
+ pwallet->LockCoin(COutPoint(hash, n));
+ return DBErrors::LOAD_OK;
+ });
+ result = std::max(result, locked_utxo_res.m_result);
+
+ // Load orderposnext record
+ // Note: There should only be one ORDERPOSNEXT record with nothing trailing the type
+ LoadResult order_pos_res = LoadRecords(pwallet, batch, DBKeys::ORDERPOSNEXT,
+ [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
+ try {
+ value >> pwallet->nOrderPosNext;
+ } catch (const std::exception& e) {
+ err = e.what();
+ return DBErrors::NONCRITICAL_ERROR;
+ }
+ return DBErrors::LOAD_OK;
+ });
+ result = std::max(result, order_pos_res.m_result);
+
+ return result;
+}
+
DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
{
CWalletScanState wss;
bool fNoncriticalErrors = false;
- bool rescan_required = false;
DBErrors result = DBErrors::LOAD_OK;
+ bool any_unordered = false;
+ std::vector<uint256> upgraded_txs;
LOCK(pwallet->cs_wallet);
@@ -1153,6 +1188,9 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
// Load address book
result = std::max(LoadAddressBookRecords(pwallet, *m_batch), result);
+ // Load tx records
+ result = std::max(LoadTxRecords(pwallet, *m_batch, upgraded_txs, any_unordered), result);
+
// Get cursor
std::unique_ptr<DatabaseCursor> cursor = m_batch->GetNewCursor();
if (!cursor)
@@ -1184,17 +1222,9 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
if (strType == DBKeys::MASTER_KEY ||
strType == DBKeys::DEFAULTKEY) {
result = DBErrors::CORRUPT;
- } else if (wss.tx_corrupt) {
- pwallet->WalletLogPrintf("Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.\n");
- // Set tx_corrupt back to false so that the error is only printed once (per corrupt tx)
- wss.tx_corrupt = false;
- result = DBErrors::CORRUPT;
} else {
// Leave other errors alone, if we try to fix them we might make things worse.
fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
- if (strType == DBKeys::TX)
- // Rescan if there is a bad transaction record:
- rescan_required = true;
}
}
if (!strErr.empty())
@@ -1214,9 +1244,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
pwallet->LoadActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /*internal=*/true);
}
- if (rescan_required && result == DBErrors::LOAD_OK) {
- result = DBErrors::NEED_RESCAN;
- } else if (fNoncriticalErrors && result == DBErrors::LOAD_OK) {
+ if (fNoncriticalErrors && result == DBErrors::LOAD_OK) {
result = DBErrors::NONCRITICAL_ERROR;
}
@@ -1225,13 +1253,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
if (result != DBErrors::LOAD_OK)
return result;
- for (const uint256& hash : wss.vWalletUpgrade)
+ for (const uint256& hash : upgraded_txs)
WriteTx(pwallet->mapWallet.at(hash));
if (!has_last_client || last_client != CLIENT_VERSION) // Update
m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);
- if (wss.fAnyUnordered)
+ if (any_unordered)
result = pwallet->ReorderTransactions();
// Upgrade all of the wallet keymetadata to have the hd master key id