// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2019 The PIVX developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "wallet/walletdb.h" #include "base58.h" #include "protocol.h" #include "serialize.h" #include "sync.h" #include "txdb.h" #include "util.h" #include "utiltime.h" #include "wallet/wallet.h" #include #include #include #include #include using namespace boost; using namespace std; static uint64_t nAccountingEntryNumber = 0; // // CWalletDB // bool CWalletDB::WriteName(const string& strAddress, const string& strName) { nWalletDBUpdated++; return Write(make_pair(string("name"), strAddress), strName); } bool CWalletDB::EraseName(const string& strAddress) { // This should only be used for sending addresses, never for receiving addresses, // receiving addresses must always have an address book entry if they're not change return. nWalletDBUpdated++; return Erase(make_pair(string("name"), strAddress)); } bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose) { nWalletDBUpdated++; return Write(make_pair(string("purpose"), strAddress), strPurpose); } bool CWalletDB::ErasePurpose(const string& strPurpose) { nWalletDBUpdated++; return Erase(make_pair(string("purpose"), strPurpose)); } bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx) { nWalletDBUpdated++; return Write(std::make_pair(std::string("tx"), hash), wtx); } bool CWalletDB::EraseTx(uint256 hash) { nWalletDBUpdated++; return Erase(std::make_pair(std::string("tx"), hash)); } bool CWalletDB::WriteAutoConvertKey(const CBitcoinAddress& btcAddress) { CKeyID keyID; if (!btcAddress.GetKeyID(keyID)) return false; return Write(std::make_pair(std::string("automint"), keyID), btcAddress.ToString()); } void CWalletDB::LoadAutoConvertKeys(std::set& setAddresses) { setAddresses.clear(); Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error(std::string(__func__)+" : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; for (;;) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("automint"), CKeyID()); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error(std::string(__func__)+" : error scanning DB"); } // Unserialize std::string strType; try { ssKey >> strType; } catch(...) { break; } if (strType != "automint") break; CKeyID keyID; ssKey >> keyID; std::string strAddress; ssValue >> strAddress; setAddresses.emplace(strAddress); } pcursor->close(); } bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) { nWalletDBUpdated++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) return false; // hash pubkey/privkey to accelerate wallet load std::vector vchKey; vchKey.reserve(vchPubKey.size() + vchPrivKey.size()); vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); } bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, const CKeyMetadata& keyMeta) { const bool fEraseUnencryptedKey = true; nWalletDBUpdated++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) return false; if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) return false; if (fEraseUnencryptedKey) { Erase(std::make_pair(std::string("key"), vchPubKey)); Erase(std::make_pair(std::string("wkey"), vchPubKey)); } return true; } bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) { nWalletDBUpdated++; return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { nWalletDBUpdated++; return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); } bool CWalletDB::WriteWatchOnly(const CScript& dest) { nWalletDBUpdated++; return Write(std::make_pair(std::string("watchs"), dest), '1'); } bool CWalletDB::EraseWatchOnly(const CScript& dest) { nWalletDBUpdated++; return Erase(std::make_pair(std::string("watchs"), dest)); } bool CWalletDB::WriteMultiSig(const CScript& dest) { nWalletDBUpdated++; return Write(std::make_pair(std::string("multisig"), dest), '1'); } bool CWalletDB::EraseMultiSig(const CScript& dest) { nWalletDBUpdated++; return Erase(std::make_pair(std::string("multisig"), dest)); } bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; return Write(std::string("bestblock"), locator); } bool CWalletDB::ReadBestBlock(CBlockLocator& locator) { return Read(std::string("bestblock"), locator); } bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) { nWalletDBUpdated++; return Write(std::string("orderposnext"), nOrderPosNext); } // presstab HyperStake bool CWalletDB::WriteStakeSplitThreshold(uint64_t nStakeSplitThreshold) { nWalletDBUpdated++; return Write(std::string("stakeSplitThreshold"), nStakeSplitThreshold); } //presstab HyperStake bool CWalletDB::WriteMultiSend(std::vector > vMultiSend) { nWalletDBUpdated++; bool ret = true; for (unsigned int i = 0; i < vMultiSend.size(); i++) { std::pair pMultiSend; pMultiSend = vMultiSend[i]; if (!Write(std::make_pair(std::string("multisend"), i), pMultiSend, true)) ret = false; } return ret; } //presstab HyperStake bool CWalletDB::EraseMultiSend(std::vector > vMultiSend) { nWalletDBUpdated++; bool ret = true; for (unsigned int i = 0; i < vMultiSend.size(); i++) { std::pair pMultiSend; pMultiSend = vMultiSend[i]; if (!Erase(std::make_pair(std::string("multisend"), i))) ret = false; } return ret; } //presstab HyperStake bool CWalletDB::WriteMSettings(bool fMultiSendStake, bool fMultiSendMasternode, int nLastMultiSendHeight) { nWalletDBUpdated++; std::pair enabledMS(fMultiSendStake, fMultiSendMasternode); std::pair, int> pSettings(enabledMS, nLastMultiSendHeight); return Write(std::string("msettingsv2"), pSettings, true); } //presstab HyperStake bool CWalletDB::WriteMSDisabledAddresses(std::vector vDisabledAddresses) { nWalletDBUpdated++; bool ret = true; for (unsigned int i = 0; i < vDisabledAddresses.size(); i++) { if (!Write(std::make_pair(std::string("mdisabled"), i), vDisabledAddresses[i])) ret = false; } return ret; } //presstab HyperStake bool CWalletDB::EraseMSDisabledAddresses(std::vector vDisabledAddresses) { nWalletDBUpdated++; bool ret = true; for (unsigned int i = 0; i < vDisabledAddresses.size(); i++) { if (!Erase(std::make_pair(std::string("mdisabled"), i))) ret = false; } return ret; } bool CWalletDB::WriteAutoCombineSettings(bool fEnable, CAmount nCombineThreshold) { nWalletDBUpdated++; std::pair pSettings; pSettings.first = fEnable; pSettings.second = nCombineThreshold; return Write(std::string("autocombinesettings"), pSettings, true); } bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) { nWalletDBUpdated++; return Write(std::string("defaultkey"), vchPubKey); } bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) { return Read(std::make_pair(std::string("pool"), nPool), keypool); } bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) { nWalletDBUpdated++; return Write(std::make_pair(std::string("pool"), nPool), keypool); } bool CWalletDB::ErasePool(int64_t nPool) { nWalletDBUpdated++; return Erase(std::make_pair(std::string("pool"), nPool)); } bool CWalletDB::WriteMinVersion(int nVersion) { return Write(std::string("minversion"), nVersion); } bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) { account.SetNull(); return Read(make_pair(string("acc"), strAccount), account); } bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) { return Write(make_pair(string("acc"), strAccount), account); } bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) { return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); } bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry) { return WriteAccountingEntry(++nAccountingEntryNumber, acentry); } CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount) { list entries; ListAccountCreditDebit(strAccount, entries); CAmount nCreditDebit = 0; for (const CAccountingEntry& entry : entries) nCreditDebit += entry.nCreditDebit; return nCreditDebit; } void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) { bool fAllAccounts = (strAccount == "*"); Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; while (true) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? string("") : strAccount), uint64_t(0))); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB"); } // Unserialize string strType; ssKey >> strType; if (strType != "acentry") break; CAccountingEntry acentry; ssKey >> acentry.strAccount; if (!fAllAccounts && acentry.strAccount != strAccount) break; ssValue >> acentry; ssKey >> acentry.nEntryNo; entries.push_back(acentry); } pcursor->close(); } DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) { LOCK(pwallet->cs_wallet); // Old wallets didn't have any defined order for transactions // Probably a bad idea to change the output of this // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. typedef pair TxPair; typedef multimap TxItems; TxItems txByTime; for (map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { CWalletTx* wtx = &((*it).second); txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); } list acentries; ListAccountCreditDebit("", acentries); for (CAccountingEntry& entry : acentries) { txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); } int64_t& nOrderPosNext = pwallet->nOrderPosNext; nOrderPosNext = 0; std::vector nOrderPosOffsets; for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) { CWalletTx* const pwtx = (*it).second.first; CAccountingEntry* const pacentry = (*it).second.second; int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; if (nOrderPos == -1) { nOrderPos = nOrderPosNext++; nOrderPosOffsets.push_back(nOrderPos); if (pwtx) { if (!WriteTx(pwtx->GetHash(), *pwtx)) return DB_LOAD_FAIL; } else if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) return DB_LOAD_FAIL; } else { int64_t nOrderPosOff = 0; for (const int64_t& nOffsetStart : nOrderPosOffsets) { if (nOrderPos >= nOffsetStart) ++nOrderPosOff; } nOrderPos += nOrderPosOff; nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); if (!nOrderPosOff) continue; // Since we're changing the order, write it back if (pwtx) { if (!WriteTx(pwtx->GetHash(), *pwtx)) return DB_LOAD_FAIL; } else if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) return DB_LOAD_FAIL; } } WriteOrderPosNext(nOrderPosNext); return DB_LOAD_OK; } class CWalletScanState { public: unsigned int nKeys; unsigned int nCKeys; unsigned int nKeyMeta; bool fIsEncrypted; bool fAnyUnordered; int nFileVersion; vector vWalletUpgrade; CWalletScanState() { nKeys = nCKeys = nKeyMeta = 0; fIsEncrypted = false; fAnyUnordered = false; nFileVersion = 0; } }; bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CWalletScanState& wss, string& strType, string& strErr) { try { // Unserialize // Taking advantage of the fact that pair serialization // is just the two items serialized one after the other ssKey >> strType; if (strType == "name") { string strAddress; ssKey >> strAddress; ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; } else if (strType == "purpose") { string strAddress; ssKey >> strAddress; ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose; } else if (strType == "tx") { uint256 hash; ssKey >> hash; CWalletTx wtx; ssValue >> wtx; CValidationState state; // false because there is no reason to go through the zerocoin checks for our own wallet if (!(CheckTransaction(wtx, false, false, state) && (wtx.GetHash() == hash) && state.IsValid())) return false; // Undo serialize changes in 31600 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) { if (!ssValue.empty()) { char fTmp; char fUnused; ssValue >> fTmp >> fUnused >> wtx.strFromAccount; strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, 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; pwallet->AddToWallet(wtx, true); } else if (strType == "acentry") { string strAccount; ssKey >> strAccount; uint64_t nNumber; ssKey >> nNumber; if (nNumber > nAccountingEntryNumber) nAccountingEntryNumber = nNumber; if (!wss.fAnyUnordered) { CAccountingEntry acentry; ssValue >> acentry; if (acentry.nOrderPos == -1) wss.fAnyUnordered = true; } } else if (strType == "watchs") { CScript script; ssKey >> script; char fYes; ssValue >> fYes; if (fYes == '1') pwallet->LoadWatchOnly(script); // Watch-only addresses have no birthday information for now, // so set the wallet birthday to the beginning of time. pwallet->nTimeFirstKey = 1; } else if (strType == "multisig") { CScript script; ssKey >> script; char fYes; ssValue >> fYes; if (fYes == '1') pwallet->LoadMultiSig(script); // MultiSig addresses have no birthday information for now, // so set the wallet birthday to the beginning of time. pwallet->nTimeFirstKey = 1; } else if (strType == "key" || strType == "wkey") { CPubKey vchPubKey; ssKey >> vchPubKey; if (!vchPubKey.IsValid()) { strErr = "Error reading wallet database: CPubKey corrupt"; return false; } CKey key; CPrivKey pkey; uint256 hash = 0; if (strType == "key") { wss.nKeys++; ssValue >> pkey; } else { CWalletKey wkey; ssValue >> wkey; pkey = wkey.vchPrivKey; } // Old wallets store keys as "key" [pubkey] => [privkey] // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key // using EC operations as a checksum. // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while // remaining backwards-compatible. try { ssValue >> hash; } catch (...) { } bool fSkipCheck = false; if (hash != 0) { // hash pubkey/privkey to accelerate wallet load std::vector vchKey; vchKey.reserve(vchPubKey.size() + pkey.size()); vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); vchKey.insert(vchKey.end(), pkey.begin(), pkey.end()); if (Hash(vchKey.begin(), vchKey.end()) != hash) { strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; return false; } fSkipCheck = true; } if (!key.Load(pkey, vchPubKey, fSkipCheck)) { strErr = "Error reading wallet database: CPrivKey corrupt"; return false; } if (!pwallet->LoadKey(key, vchPubKey)) { strErr = "Error reading wallet database: LoadKey failed"; return false; } } else if (strType == "mkey") { unsigned int nID; ssKey >> nID; CMasterKey kMasterKey; ssValue >> kMasterKey; if (pwallet->mapMasterKeys.count(nID) != 0) { strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); return false; } pwallet->mapMasterKeys[nID] = kMasterKey; if (pwallet->nMasterKeyMaxID < nID) pwallet->nMasterKeyMaxID = nID; } else if (strType == "ckey") { CPubKey vchPubKey; ssKey >> vchPubKey; vector vchPrivKey; ssValue >> vchPrivKey; wss.nCKeys++; if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) { strErr = "Error reading wallet database: LoadCryptedKey failed"; return false; } wss.fIsEncrypted = true; } else if (strType == "keymeta") { CPubKey vchPubKey; ssKey >> vchPubKey; CKeyMetadata keyMeta; ssValue >> keyMeta; wss.nKeyMeta++; pwallet->LoadKeyMetadata(vchPubKey, keyMeta); // find earliest key creation time, as wallet birthday if (!pwallet->nTimeFirstKey || (keyMeta.nCreateTime < pwallet->nTimeFirstKey)) pwallet->nTimeFirstKey = keyMeta.nCreateTime; } else if (strType == "defaultkey") { ssValue >> pwallet->vchDefaultKey; } else if (strType == "pool") { int64_t nIndex; ssKey >> nIndex; CKeyPool keypool; ssValue >> keypool; pwallet->setKeyPool.insert(nIndex); // If no metadata exists yet, create a default with the pool key's // creation time. Note that this may be overwritten by actually // stored metadata for that key later, which is fine. CKeyID keyid = keypool.vchPubKey.GetID(); if (pwallet->mapKeyMetadata.count(keyid) == 0) pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); } else if (strType == "version") { ssValue >> wss.nFileVersion; if (wss.nFileVersion == 10300) wss.nFileVersion = 300; } else if (strType == "cscript") { uint160 hash; ssKey >> hash; CScript script; ssValue >> script; if (!pwallet->LoadCScript(script)) { strErr = "Error reading wallet database: LoadCScript failed"; return false; } } else if (strType == "orderposnext") { ssValue >> pwallet->nOrderPosNext; } else if (strType == "stakeSplitThreshold") //presstab HyperStake { ssValue >> pwallet->nStakeSplitThreshold; } else if (strType == "multisend") //presstab HyperStake { unsigned int i; ssKey >> i; std::pair pMultiSend; ssValue >> pMultiSend; if (CBitcoinAddress(pMultiSend.first).IsValid()) { pwallet->vMultiSend.push_back(pMultiSend); } } else if (strType == "msettingsv2") //presstab HyperStake { std::pair, int> pSettings; ssValue >> pSettings; pwallet->fMultiSendStake = pSettings.first.first; pwallet->fMultiSendMasternodeReward = pSettings.first.second; pwallet->nLastMultiSendHeight = pSettings.second; } else if (strType == "mdisabled") //presstab HyperStake { std::string strDisabledAddress; ssValue >> strDisabledAddress; pwallet->vDisabledAddresses.push_back(strDisabledAddress); } else if (strType == "autocombinesettings") { std::pair pSettings; ssValue >> pSettings; pwallet->fCombineDust = pSettings.first; pwallet->nAutoCombineThreshold = pSettings.second; } else if (strType == "destdata") { std::string strAddress, strKey, strValue; ssKey >> strAddress; ssKey >> strKey; ssValue >> strValue; if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue)) { strErr = "Error reading wallet database: LoadDestData failed"; return false; } } } catch (...) { return false; } return true; } static bool IsKeyType(string strType) { return (strType == "key" || strType == "wkey" || strType == "mkey" || strType == "ckey"); } DBErrors CWalletDB::LoadWallet(CWallet* pwallet) { pwallet->vchDefaultKey = CPubKey(); CWalletScanState wss; bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; try { LOCK(pwallet->cs_wallet); int nMinVersion = 0; if (Read((string) "minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) return DB_TOO_NEW; pwallet->LoadMinVersion(nMinVersion); } // Get cursor Dbc* pcursor = GetCursor(); if (!pcursor) { LogPrintf("Error getting wallet database cursor\n"); return DB_CORRUPT; } while (true) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; else if (ret != 0) { LogPrintf("Error reading next record from wallet database\n"); return DB_CORRUPT; } // Try to be tolerant of single corrupt records: string strType, strErr; if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { // losing keys is considered a catastrophic error, anything else // we assume the user can live with: if (IsKeyType(strType)) result = DB_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 == "tx") // Rescan if there is a bad transaction record: SoftSetBoolArg("-rescan", true); } } if (!strErr.empty()) LogPrintf("%s\n", strErr); } pcursor->close(); } catch (boost::thread_interrupted) { throw; } catch (...) { result = DB_CORRUPT; } if (fNoncriticalErrors && result == DB_LOAD_OK) result = DB_NONCRITICAL_ERROR; // Any wallet corruption at all: skip any rewriting or // upgrading, we don't want to make it worse. if (result != DB_LOAD_OK) return result; LogPrintf("nFileVersion = %d\n", wss.nFileVersion); LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); // nTimeFirstKey is only reliable if all keys have metadata if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta) pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value' for (uint256 hash : wss.vWalletUpgrade) WriteTx(hash, pwallet->mapWallet[hash]); // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) return DB_NEED_REWRITE; if (wss.nFileVersion < CLIENT_VERSION) // Update WriteVersion(CLIENT_VERSION); if (wss.fAnyUnordered) result = ReorderTransactions(pwallet); pwallet->laccentries.clear(); ListAccountCreditDebit("*", pwallet->laccentries); for (CAccountingEntry& entry : pwallet->laccentries) { pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair((CWalletTx*)0, &entry))); } return result; } DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector& vTxHash, vector& vWtx) { pwallet->vchDefaultKey = CPubKey(); bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; try { LOCK(pwallet->cs_wallet); int nMinVersion = 0; if (Read((string) "minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) return DB_TOO_NEW; pwallet->LoadMinVersion(nMinVersion); } // Get cursor Dbc* pcursor = GetCursor(); if (!pcursor) { LogPrintf("Error getting wallet database cursor\n"); return DB_CORRUPT; } while (true) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; else if (ret != 0) { LogPrintf("Error reading next record from wallet database\n"); return DB_CORRUPT; } string strType; ssKey >> strType; if (strType == "tx") { uint256 hash; ssKey >> hash; CWalletTx wtx; ssValue >> wtx; vTxHash.push_back(hash); vWtx.push_back(wtx); } } pcursor->close(); } catch (boost::thread_interrupted) { throw; } catch (...) { result = DB_CORRUPT; } if (fNoncriticalErrors && result == DB_LOAD_OK) result = DB_NONCRITICAL_ERROR; return result; } DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector& vWtx) { // build list of wallet TXs vector vTxHash; DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); if (err != DB_LOAD_OK) return err; // erase each wallet TX for (uint256& hash : vTxHash) { if (!EraseTx(hash)) return DB_CORRUPT; } return DB_LOAD_OK; } void ThreadFlushWalletDB(const string& strFile) { // Make this thread recognisable as the wallet flushing thread RenameThread("agrarian-wallet"); static bool fOneThread; if (fOneThread) return; fOneThread = true; if (!GetBoolArg("-flushwallet", true)) return; unsigned int nLastSeen = nWalletDBUpdated; unsigned int nLastFlushed = nWalletDBUpdated; int64_t nLastWalletUpdate = GetTime(); while (true) { MilliSleep(500); if (nLastSeen != nWalletDBUpdated) { nLastSeen = nWalletDBUpdated; nLastWalletUpdate = GetTime(); } if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) { TRY_LOCK(bitdb.cs_db, lockDb); if (lockDb) { // Don't do this if any databases are in use int nRefCount = 0; map::iterator mi = bitdb.mapFileUseCount.begin(); while (mi != bitdb.mapFileUseCount.end()) { nRefCount += (*mi).second; mi++; } if (nRefCount == 0) { boost::this_thread::interruption_point(); map::iterator mi = bitdb.mapFileUseCount.find(strFile); if (mi != bitdb.mapFileUseCount.end()) { LogPrint("db", "Flushing wallet.dat\n"); nLastFlushed = nWalletDBUpdated; int64_t nStart = GetTimeMillis(); // Flush wallet.dat so it's self contained bitdb.CloseDb(strFile); bitdb.CheckpointLSN(strFile); bitdb.mapFileUseCount.erase(mi++); LogPrint("db", "Flushed wallet.dat %dms\n", GetTimeMillis() - nStart); } } } } } } void NotifyBacked(const CWallet& wallet, bool fSuccess, string strMessage) { LogPrint(nullptr, strMessage.data()); wallet.NotifyWalletBacked(fSuccess, strMessage); } bool BackupWallet(const CWallet& wallet, const filesystem::path& strDest, bool fEnableCustom) { filesystem::path pathCustom; filesystem::path pathWithFile; if (!wallet.fFileBacked) { return false; } else if(fEnableCustom) { pathWithFile = GetArg("-backuppath", ""); if(!pathWithFile.empty()) { if(!pathWithFile.has_extension()) { pathCustom = pathWithFile; pathWithFile /= wallet.GetUniqueWalletBackupName(false); } else { pathCustom = pathWithFile.parent_path(); } try { filesystem::create_directories(pathCustom); } catch(const filesystem::filesystem_error& e) { NotifyBacked(wallet, false, strprintf("%s\n", e.what())); pathCustom = ""; } } } while (true) { { LOCK(bitdb.cs_db); if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0) { // Flush log data to the dat file bitdb.CloseDb(wallet.strWalletFile); bitdb.CheckpointLSN(wallet.strWalletFile); bitdb.mapFileUseCount.erase(wallet.strWalletFile); // Copy wallet.dat filesystem::path pathDest(strDest); filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; if (is_directory(pathDest)) { if(!exists(pathDest)) create_directory(pathDest); pathDest /= wallet.strWalletFile; } bool defaultPath = AttemptBackupWallet(wallet, pathSrc.string(), pathDest.string()); if(defaultPath && !pathCustom.empty()) { int nThreshold = GetArg("-custombackupthreshold", DEFAULT_CUSTOMBACKUPTHRESHOLD); if (nThreshold > 0) { typedef std::multimap folder_set_t; folder_set_t folderSet; filesystem::directory_iterator end_iter; pathCustom.make_preferred(); // Build map of backup files for current(!) wallet sorted by last write time filesystem::path currentFile; for (filesystem::directory_iterator dir_iter(pathCustom); dir_iter != end_iter; ++dir_iter) { // Only check regular files if (filesystem::is_regular_file(dir_iter->status())) { currentFile = dir_iter->path().filename(); // Only add the backups for the current wallet, e.g. wallet.dat.* if (dir_iter->path().stem().string() == wallet.strWalletFile) { folderSet.insert(folder_set_t::value_type(filesystem::last_write_time(dir_iter->path()), *dir_iter)); } } } int counter = 0; //TODO: add seconds to avoid naming conflicts for (auto entry : folderSet) { counter++; if(entry.second == pathWithFile) { pathWithFile += "(1)"; } } if (counter >= nThreshold) { std::time_t oldestBackup = 0; for(auto entry : folderSet) { if(oldestBackup == 0 || entry.first < oldestBackup) { oldestBackup = entry.first; } } try { auto entry = folderSet.find(oldestBackup); if (entry != folderSet.end()) { filesystem::remove(entry->second); LogPrintf("Old backup deleted: %s\n", (*entry).second); } } catch (filesystem::filesystem_error& error) { string strMessage = strprintf("Failed to delete backup %s\n", error.what()); LogPrint(nullptr, strMessage.data()); NotifyBacked(wallet, false, strMessage); } } } AttemptBackupWallet(wallet, pathSrc.string(), pathWithFile.string()); } return defaultPath; } } MilliSleep(100); } return false; } bool AttemptBackupWallet(const CWallet& wallet, const filesystem::path& pathSrc, const filesystem::path& pathDest) { bool retStatus; string strMessage; try { if (boost::filesystem::equivalent(pathSrc, pathDest)) { LogPrintf("cannot backup to wallet source file %s\n", pathDest.string()); return false; } #if BOOST_VERSION >= 107400 /* BOOST_LIB_VERSION 1_74 */ filesystem::copy_file(pathSrc.c_str(), pathDest, filesystem::copy_options::overwrite_existing); #elif BOOST_VERSION >= 105800 /* BOOST_LIB_VERSION 1_58 */ filesystem::copy_file(pathSrc.c_str(), pathDest, filesystem::copy_option::overwrite_if_exists); #else std::ifstream src(pathSrc.c_str(), std::ios::binary | std::ios::in); std::ofstream dst(pathDest.c_str(), std::ios::binary | std::ios::out | std::ios::trunc); dst << src.rdbuf(); dst.flush(); src.close(); dst.close(); #endif strMessage = strprintf("copied wallet.dat to %s\n", pathDest.string()); LogPrint(nullptr, strMessage.data()); retStatus = true; } catch (const filesystem::filesystem_error& e) { retStatus = false; strMessage = strprintf("%s\n", e.what()); LogPrint(nullptr, strMessage.data()); } NotifyBacked(wallet, retStatus, strMessage); return retStatus; } // // Try to (very carefully!) recover wallet.dat if there is a problem. // bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) { // Recovery procedure: // move wallet.dat to wallet.timestamp.bak // Call Salvage with fAggressive=true to // get as much data as possible. // Rewrite salvaged data to wallet.dat // Set -rescan so any missing transactions will be // found. int64_t now = GetTime(); std::string newFilename = strprintf("wallet.%d.bak", now); int result = dbenv.dbenv->dbrename(NULL, filename.c_str(), NULL, newFilename.c_str(), DB_AUTO_COMMIT); if (result == 0) LogPrintf("Renamed %s to %s\n", filename, newFilename); else { LogPrintf("Failed to rename %s to %s\n", filename, newFilename); return false; } std::vector salvagedData; bool allOK = dbenv.Salvage(newFilename, true, salvagedData); if (salvagedData.empty()) { LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename); return false; } LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); bool fSuccess = allOK; boost::scoped_ptr pdbCopy(new Db(dbenv.dbenv, 0)); int ret = pdbCopy->open(NULL, // Txn pointer filename.c_str(), // Filename "main", // Logical db name DB_BTREE, // Database type DB_CREATE, // Flags 0); if (ret > 0) { LogPrintf("Cannot create database file %s\n", filename); return false; } CWallet dummyWallet; CWalletScanState wss; DbTxn* ptxn = dbenv.TxnBegin(); for (CDBEnv::KeyValPair& row : salvagedData) { if (fOnlyKeys) { CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); string strType, strErr; bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, wss, strType, strErr); if (!IsKeyType(strType)) continue; if (!fReadOK) { LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr); continue; } } Dbt datKey(&row.first[0], row.first.size()); Dbt datValue(&row.second[0], row.second.size()); int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); if (ret2 > 0) fSuccess = false; } ptxn->commit(0); pdbCopy->close(0); return fSuccess; } bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) { return CWalletDB::Recover(dbenv, filename, false); } bool CWalletDB::WriteDestData(const std::string& address, const std::string& key, const std::string& value) { nWalletDBUpdated++; return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); } bool CWalletDB::EraseDestData(const std::string& address, const std::string& key) { nWalletDBUpdated++; return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } bool CWalletDB::WriteZerocoinSpendSerialEntry(const CZerocoinSpend& zerocoinSpend) { return Write(make_pair(string("zcserial"), zerocoinSpend.GetSerial()), zerocoinSpend, true); } bool CWalletDB::EraseZerocoinSpendSerialEntry(const CBigNum& serialEntry) { return Erase(make_pair(string("zcserial"), serialEntry)); } bool CWalletDB::ReadZerocoinSpendSerialEntry(const CBigNum& bnSerial) { CZerocoinSpend spend; return Read(make_pair(string("zcserial"), bnSerial), spend); } bool CWalletDB::WriteDeterministicMint(const CDeterministicMint& dMint) { uint256 hash = dMint.GetPubcoinHash(); return Write(make_pair(string("dzagr"), hash), dMint, true); } bool CWalletDB::ReadDeterministicMint(const uint256& hashPubcoin, CDeterministicMint& dMint) { return Read(make_pair(string("dzagr"), hashPubcoin), dMint); } bool CWalletDB::EraseDeterministicMint(const uint256& hashPubcoin) { return Erase(make_pair(string("dzagr"), hashPubcoin)); } bool CWalletDB::WriteZerocoinMint(const CZerocoinMint& zerocoinMint) { CDataStream ss(SER_GETHASH, 0); ss << zerocoinMint.GetValue(); uint256 hash = Hash(ss.begin(), ss.end()); Erase(make_pair(string("zerocoin"), hash)); return Write(make_pair(string("zerocoin"), hash), zerocoinMint, true); } bool CWalletDB::ReadZerocoinMint(const CBigNum &bnPubCoinValue, CZerocoinMint& zerocoinMint) { CDataStream ss(SER_GETHASH, 0); ss << bnPubCoinValue; uint256 hash = Hash(ss.begin(), ss.end()); return ReadZerocoinMint(hash, zerocoinMint); } bool CWalletDB::ReadZerocoinMint(const uint256& hashPubcoin, CZerocoinMint& mint) { return Read(make_pair(string("zerocoin"), hashPubcoin), mint); } bool CWalletDB::EraseZerocoinMint(const CZerocoinMint& zerocoinMint) { CDataStream ss(SER_GETHASH, 0); ss << zerocoinMint.GetValue(); uint256 hash = Hash(ss.begin(), ss.end()); return Erase(make_pair(string("zerocoin"), hash)); } bool CWalletDB::ArchiveMintOrphan(const CZerocoinMint& zerocoinMint) { CDataStream ss(SER_GETHASH, 0); ss << zerocoinMint.GetValue(); uint256 hash = Hash(ss.begin(), ss.end());; if (!Write(make_pair(string("zco"), hash), zerocoinMint)) { LogPrintf("%s : failed to database orphaned zerocoin mint\n", __func__); return false; } if (!Erase(make_pair(string("zerocoin"), hash))) { LogPrintf("%s : failed to erase orphaned zerocoin mint\n", __func__); return false; } return true; } bool CWalletDB::ArchiveDeterministicOrphan(const CDeterministicMint& dMint) { if (!Write(make_pair(string("dzco"), dMint.GetPubcoinHash()), dMint)) return error("%s: write failed", __func__); if (!Erase(make_pair(string("dzagr"), dMint.GetPubcoinHash()))) return error("%s: failed to erase", __func__); return true; } bool CWalletDB::UnarchiveDeterministicMint(const uint256& hashPubcoin, CDeterministicMint& dMint) { if (!Read(make_pair(string("dzco"), hashPubcoin), dMint)) return error("%s: failed to retrieve deterministic mint from archive", __func__); if (!WriteDeterministicMint(dMint)) return error("%s: failed to write deterministic mint", __func__); if (!Erase(make_pair(string("dzco"), dMint.GetPubcoinHash()))) return error("%s : failed to erase archived deterministic mint", __func__); return true; } bool CWalletDB::UnarchiveZerocoinMint(const uint256& hashPubcoin, CZerocoinMint& mint) { if (!Read(make_pair(string("zco"), hashPubcoin), mint)) return error("%s: failed to retrieve zerocoinmint from archive", __func__); if (!WriteZerocoinMint(mint)) return error("%s: failed to write zerocoinmint", __func__); uint256 hash = GetPubCoinHash(mint.GetValue()); if (!Erase(make_pair(string("zco"), hash))) return error("%s : failed to erase archived zerocoin mint", __func__); return true; } bool CWalletDB::WriteCurrentSeedHash(const uint256& hashSeed) { return Write(string("seedhash"), hashSeed); } bool CWalletDB::ReadCurrentSeedHash(uint256& hashSeed) { return Read(string("seedhash"), hashSeed); } bool CWalletDB::WriteZAGRSeed(const uint256& hashSeed, const vector& seed) { if (!WriteCurrentSeedHash(hashSeed)) return error("%s: failed to write current seed hash", __func__); return Write(make_pair(string("dzs"), hashSeed), seed); } bool CWalletDB::EraseZAGRSeed() { uint256 hash; if(!ReadCurrentSeedHash(hash)){ return error("Failed to read a current seed hash"); } if(!WriteZAGRSeed(hash, ToByteVector(base_uint<256>(0) << 256))) { return error("Failed to write empty seed to wallet"); } if(!WriteCurrentSeedHash(0)) { return error("Failed to write empty seedHash"); } return true; } bool CWalletDB::EraseZAGRSeed_deprecated() { return Erase(string("dzs")); } bool CWalletDB::ReadZAGRSeed(const uint256& hashSeed, vector& seed) { return Read(make_pair(string("dzs"), hashSeed), seed); } bool CWalletDB::ReadZAGRSeed_deprecated(uint256& seed) { return Read(string("dzs"), seed); } bool CWalletDB::WriteZAGRCount(const uint32_t& nCount) { return Write(string("dzc"), nCount); } bool CWalletDB::ReadZAGRCount(uint32_t& nCount) { return Read(string("dzc"), nCount); } bool CWalletDB::WriteMintPoolPair(const uint256& hashMasterSeed, const uint256& hashPubcoin, const uint32_t& nCount) { return Write(make_pair(string("mintpool"), hashPubcoin), make_pair(hashMasterSeed, nCount)); } void CWalletDB::LoadPrecomputes(std::list >& itemList, std::map >::iterator>& itemMap) { Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error(std::string(__func__)+" : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; for (;;) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("precompute"), uint256(0)); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error(std::string(__func__)+" : error scanning precompute DB"); } // Unserialize string strType; ssKey >> strType; if (strType != "precompute") break; uint256 hash; ssKey >> hash; CoinWitnessCacheData cacheData; ssValue >> cacheData; itemList.push_front(std::make_pair(hash, cacheData)); itemMap.insert(make_pair(hash, itemList.begin())); if (itemMap.size() == PRECOMPUTE_LRU_CACHE_SIZE) break; } pcursor->close(); } void CWalletDB::LoadPrecomputes(set setHashes) { Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error(std::string(__func__)+" : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; for (;;) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("precompute"), uint256(0)); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error(std::string(__func__)+" : error scanning precompute DB"); } // Unserialize string strType; ssKey >> strType; if (strType != "precompute") break; uint256 hash; ssKey >> hash; setHashes.insert(hash); } pcursor->close(); } void CWalletDB::EraseAllPrecomputes() { set setHashes; LoadPrecomputes(setHashes); for (auto hash : setHashes) ErasePrecompute(hash); } bool CWalletDB::WritePrecompute(const uint256& hash, const CoinWitnessCacheData& data) { return Write(make_pair(string("precompute"), hash), data); } bool CWalletDB::ReadPrecompute(const uint256& hash, CoinWitnessCacheData& data) { return Read(make_pair(string("precompute"), hash), data); } bool CWalletDB::ErasePrecompute(const uint256& hash) { return Erase(make_pair(string("precompute"), hash)); } //! map with hashMasterSeed as the key, paired with vector of hashPubcoins and their count std::map > > CWalletDB::MapMintPool() { std::map > > mapPool; Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error(std::string(__func__)+" : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; for (;;) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("mintpool"), uint256(0)); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error(std::string(__func__)+" : error scanning DB"); } // Unserialize string strType; ssKey >> strType; if (strType != "mintpool") break; uint256 hashPubcoin; ssKey >> hashPubcoin; uint256 hashMasterSeed; ssValue >> hashMasterSeed; uint32_t nCount; ssValue >> nCount; pair pMint; pMint.first = hashPubcoin; pMint.second = nCount; if (mapPool.count(hashMasterSeed)) { mapPool.at(hashMasterSeed).emplace_back(pMint); } else { vector > vPairs; vPairs.emplace_back(pMint); mapPool.insert(make_pair(hashMasterSeed, vPairs)); } } pcursor->close(); return mapPool; } std::list CWalletDB::ListDeterministicMints() { std::list listMints; Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error(std::string(__func__)+" : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; for (;;) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("dzagr"), uint256(0)); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error(std::string(__func__)+" : error scanning DB"); } // Unserialize string strType; ssKey >> strType; if (strType != "dzagr") break; uint256 hashPubcoin; ssKey >> hashPubcoin; CDeterministicMint mint; ssValue >> mint; listMints.emplace_back(mint); } pcursor->close(); return listMints; } std::list CWalletDB::ListMintedCoins() { std::list listPubCoin; Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error(std::string(__func__)+" : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; vector vOverWrite; vector vArchive; for (;;) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("zerocoin"), uint256(0)); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error(std::string(__func__)+" : error scanning DB"); } // Unserialize string strType; ssKey >> strType; if (strType != "zerocoin") break; uint256 hashPubcoin; ssKey >> hashPubcoin; CZerocoinMint mint; ssValue >> mint; listPubCoin.emplace_back(mint); } pcursor->close(); return listPubCoin; } std::list CWalletDB::ListSpentCoins() { std::list listCoinSpend; Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error(std::string(__func__)+" : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; for (;;) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("zcserial"), CBigNum(0)); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error(std::string(__func__)+" : error scanning DB"); } // Unserialize string strType; ssKey >> strType; if (strType != "zcserial") break; CBigNum value; ssKey >> value; CZerocoinSpend zerocoinSpendItem; ssValue >> zerocoinSpendItem; listCoinSpend.push_back(zerocoinSpendItem); } pcursor->close(); return listCoinSpend; } // Just get the Serial Numbers std::list CWalletDB::ListSpentCoinsSerial() { std::list listPubCoin; std::list listCoins = ListSpentCoins(); for ( auto& coin : listCoins) { listPubCoin.push_back(coin.GetSerial()); } return listPubCoin; } std::list CWalletDB::ListArchivedZerocoins() { std::list listMints; Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error(std::string(__func__)+" : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; for (;;) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("zco"), CBigNum(0)); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error(std::string(__func__)+" : error scanning DB"); } // Unserialize string strType; ssKey >> strType; if (strType != "zco") break; uint256 value; ssKey >> value; CZerocoinMint mint; ssValue >> mint; listMints.push_back(mint); } pcursor->close(); return listMints; } std::list CWalletDB::ListArchivedDeterministicMints() { std::list listMints; Dbc* pcursor = GetCursor(); if (!pcursor) throw runtime_error(std::string(__func__)+" : cannot create DB cursor"); unsigned int fFlags = DB_SET_RANGE; for (;;) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) ssKey << make_pair(string("dzco"), CBigNum(0)); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw runtime_error(std::string(__func__)+" : error scanning DB"); } // Unserialize string strType; ssKey >> strType; if (strType != "dzco") break; uint256 value; ssKey >> value; CDeterministicMint dMint; ssValue >> dMint; listMints.emplace_back(dMint); } pcursor->close(); return listMints; }