diff --git a/src/init.cpp b/src/init.cpp index 3bf80f4..d97db73 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -424,6 +424,7 @@ std::string HelpMessage() " -keypool= " + _("Set key pool size to (default: 100)") + "\n" + " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + " -postibdrescan " + _("Run the wallet rescan after initial sync in a background thread (default: 1)") + "\n" + + " -zapwallettxes " + _("Delete all wallet transactions and only recover from blockchain on startup") + "\n" + " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" + " -checkblocks= " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + " -checklevel= " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + @@ -578,6 +579,11 @@ bool AppInit2() SoftSetBoolArg("-rescan", true); } + if (GetBoolArg("-zapwallettxes")) { + // Zap all tx from wallet: rescan to rebuild from blockchain + SoftSetBoolArg("-rescan", true); + } + // ********************************************************* Step 3: parameter-to-internal-flags fDebug = GetBoolArg("-debug"); @@ -721,6 +727,13 @@ bool AppInit2() return false; } + if (GetBoolArg("-zapwallettxes") && fs::exists(GetDataDir() / strWalletFileName)) + { + uiInterface.InitMessage(_("Zapping all transactions from wallet...")); + if (!CWalletDB::ZapWalletTx(strWalletFileName)) + return InitError(_("Error: could not zap wallet transactions")); + } + if (fs::exists(GetDataDir() / strWalletFileName)) { CDBEnv::VerifyResult r = bitdb.Verify(strWalletFileName, CWalletDB::Recover); diff --git a/src/miner.cpp b/src/miner.cpp index 9565cdb..1df72fc 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -434,7 +434,10 @@ void StakeMiner(CWallet *pwallet) int64_t nFees; unique_ptr pblock(CreateNewBlock(pwallet, true, &nFees)); if (!pblock.get()) - return; + { + MilliSleep(5000); + continue; + } // Try to sign the block if (pblock->SignBlock(*pwallet, nFees)) diff --git a/src/net.cpp b/src/net.cpp index 3434509..9816047 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1635,18 +1635,35 @@ void static ThreadStakeMiner(void* parg) { printf("ThreadStakeMiner started\n"); CWallet* pwallet = (CWallet*)parg; - try + int nConsecutiveErrors = 0; + while (!fShutdown) { - vnThreadsRunning[THREAD_STAKE_MINER]++; - StakeMiner(pwallet); - vnThreadsRunning[THREAD_STAKE_MINER]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_STAKE_MINER]--; - PrintException(&e, "ThreadStakeMiner()"); - } catch (...) { - vnThreadsRunning[THREAD_STAKE_MINER]--; - PrintException(NULL, "ThreadStakeMiner()"); + try + { + vnThreadsRunning[THREAD_STAKE_MINER]++; + StakeMiner(pwallet); + vnThreadsRunning[THREAD_STAKE_MINER]--; + break; // normal exit + } + catch (std::exception& e) { + vnThreadsRunning[THREAD_STAKE_MINER]--; + nConsecutiveErrors++; + printf("ThreadStakeMiner() exception: %s (attempt %d)\n", e.what(), nConsecutiveErrors); + if (nConsecutiveErrors >= 10) { + printf("ThreadStakeMiner() too many consecutive errors, giving up\n"); + break; + } + MilliSleep(5000); // wait 5 seconds before retrying + } catch (...) { + vnThreadsRunning[THREAD_STAKE_MINER]--; + nConsecutiveErrors++; + printf("ThreadStakeMiner() unknown exception (attempt %d)\n", nConsecutiveErrors); + if (nConsecutiveErrors >= 10) { + printf("ThreadStakeMiner() too many consecutive errors, giving up\n"); + break; + } + MilliSleep(5000); + } } printf("ThreadStakeMiner exiting, %d threads remaining\n", vnThreadsRunning[THREAD_STAKE_MINER]); } diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index c7b5d91..757e844 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1911,7 +1911,8 @@ Value clearwallettransactions(const Array& params, bool fHelp) }; pwalletMain->mapWallet.erase(hash); - pwalletMain->NotifyTransactionChanged(pwalletMain, hash, CT_DELETED); + try { pwalletMain->NotifyTransactionChanged(pwalletMain, hash, CT_DELETED); } + catch (...) { } nTransactions++; }; diff --git a/src/wallet.cpp b/src/wallet.cpp index 1c6c6b3..65addac 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -487,7 +487,10 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock) wtx.MarkSpent(txin.prevout.n); wtx.WriteToDisk(); if (!IsInitialBlockDownload()) - NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); + { + try { NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); } + catch (...) { } + } } } } @@ -505,7 +508,10 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock) wtx.MarkUnspent(&txout - &tx.vout[0]); wtx.WriteToDisk(); if (!IsInitialBlockDownload()) - NotifyTransactionChanged(this, hash, CT_UPDATED); + { + try { NotifyTransactionChanged(this, hash, CT_UPDATED); } + catch (...) { } + } } } } @@ -638,7 +644,13 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) // Notify UI of new or updated transaction (skip during IBD to avoid flooding the event loop) if (!IsInitialBlockDownload()) - NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + { + try { + NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); + } catch (...) { + // Absorb boost::bad_weak_ptr or other signal exceptions from stale slots + } + } // notify an external script when a wallet transaction comes in or is updated std::string strCmd = GetArg("-walletnotify", ""); @@ -1855,18 +1867,37 @@ bool CWallet::GetStakeWeight(const CKeyStore& keystore, uint64_t& nMinWeight, ui if (setCoins.empty()) return false; + // Collect coin hashes under lock, then read DB outside the per-coin lock + // to avoid acquiring/releasing LOCK2 thousands of times for large wallets + struct StakeCoin { + uint256 hash; + int64_t nValue; + unsigned int nTime; + }; + vector vStakeCoins; + { + LOCK(cs_wallet); + vStakeCoins.reserve(setCoins.size()); + for (auto& pcoin : setCoins) + { + StakeCoin sc; + sc.hash = pcoin.first->GetHash(); + sc.nValue = pcoin.first->vout[pcoin.second].nValue; + sc.nTime = pcoin.first->nTime; + vStakeCoins.push_back(sc); + } + } + CTxDB txdb("r"); - for (auto pcoin : setCoins) + int64_t nNow = GetTime(); + for (auto& sc : vStakeCoins) { CTxIndex txindex; - { - LOCK2(cs_main, cs_wallet); - if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) - continue; - } + if (!txdb.ReadTxIndex(sc.hash, txindex)) + continue; - int64_t nTimeWeight = GetWeight((int64_t)pcoin.first->nTime, (int64_t)GetTime()); - CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60); + int64_t nTimeWeight = GetWeight((int64_t)sc.nTime, nNow); + CBigNum bnCoinDayWeight = CBigNum(sc.nValue) * nTimeWeight / COIN / (24 * 60 * 60); // Weight is greater than zero if (nTimeWeight > 0) @@ -2139,7 +2170,8 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) coin.BindWallet(this); coin.MarkSpent(txin.prevout.n); coin.WriteToDisk(); - NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); + try { NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); } + catch (...) { } } if (fFileBacked) @@ -2268,8 +2300,9 @@ bool CWallet::SetAddressBookName(const CTxDestination& address, const string& st const CTrianglesAddress& caddress = address; SecureMsgWalletKeyChanged(caddress.ToString(), strName, nMode); } - NotifyAddressBookChanged(this, address, strName, fOwned, nMode); - + try { NotifyAddressBookChanged(this, address, strName, fOwned, nMode); } + catch (...) { } + if (!fFileBacked) return false; return CWalletDB(strWalletFile).WriteName(CTrianglesAddress(address).ToString(), strName); @@ -2282,7 +2315,7 @@ bool CWallet::DelAddressBookName(const CTxDestination& address) mapAddressBook.erase(address); } - + bool fOwned = ::IsMine(*this, address); string sName = ""; if (fOwned) @@ -2290,7 +2323,8 @@ bool CWallet::DelAddressBookName(const CTxDestination& address) const CTrianglesAddress& caddress = address; SecureMsgWalletKeyChanged(caddress.ToString(), sName, CT_DELETED); } - NotifyAddressBookChanged(this, address, "", fOwned, CT_DELETED); + try { NotifyAddressBookChanged(this, address, "", fOwned, CT_DELETED); } + catch (...) { } if (!fFileBacked) return false; @@ -2768,7 +2802,10 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx) // Only notify UI if this transaction is in this wallet map::const_iterator mi = mapWallet.find(hashTx); if (mi != mapWallet.end() && !IsInitialBlockDownload()) - NotifyTransactionChanged(this, hashTx, CT_UPDATED); + { + try { NotifyTransactionChanged(this, hashTx, CT_UPDATED); } + catch (...) { } + } } } diff --git a/src/walletdb.cpp b/src/walletdb.cpp index fb1437a..afc73b3 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -718,3 +718,64 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) { return CWalletDB::Recover(dbenv, filename, false); } + +bool CWalletDB::ZapWalletTx(const std::string& strWalletFile) +{ + // Open the wallet database directly and delete all "tx" entries, + // keeping keys and other metadata intact. This strips transaction + // history while preserving private keys. A rescan will rebuild + // the transaction list from the blockchain. + printf("ZapWalletTx: erasing transaction records from %s\n", strWalletFile.c_str()); + + CWalletDB walletdb(strWalletFile, "r+"); + if (!walletdb.pdb) + { + printf("ZapWalletTx: failed to open wallet database\n"); + return false; + } + + Dbc* pcursor = walletdb.GetCursor(); + if (!pcursor) + { + printf("ZapWalletTx: failed to get cursor\n"); + return false; + } + + // First pass: collect all tx hashes to erase + std::vector vTxHash; + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + while (true) + { + int ret = walletdb.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); + if (ret == DB_NOTFOUND) + break; + if (ret != 0) + { + printf("ZapWalletTx: cursor read error %d\n", ret); + pcursor->close(); + return false; + } + + std::string strType; + ssKey >> strType; + if (strType == "tx") + { + uint256 hash; + ssKey >> hash; + vTxHash.push_back(hash); + } + } + pcursor->close(); + + // Second pass: erase all collected tx entries + int nErased = 0; + for (const uint256& hash : vTxHash) + { + if (walletdb.EraseTx(hash)) + nErased++; + } + + printf("ZapWalletTx: erased %d of %d transaction records\n", nErased, (int)vTxHash.size()); + return true; +} diff --git a/src/walletdb.h b/src/walletdb.h index 221c8e1..b1b9428 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -223,6 +223,7 @@ public: DBErrors LoadWallet(CWallet* pwallet); static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, std::string filename); + static bool ZapWalletTx(const std::string& strWalletFile); }; #endif // TRIANGLES_WALLETDB_H