2012-04-15 23:39:49 +02:00
|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
2018-01-02 18:12:05 +01:00
|
|
|
// Copyright (c) 2009-2017 The Bitcoin Core developers
|
2014-12-13 05:09:33 +01:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2012-05-18 16:02:28 +02:00
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2017-11-10 01:57:53 +01:00
|
|
|
#include <wallet/walletdb.h>
|
|
|
|
|
|
|
|
#include <consensus/tx_verify.h>
|
|
|
|
#include <consensus/validation.h>
|
|
|
|
#include <fs.h>
|
2017-09-20 03:12:25 +02:00
|
|
|
#include <key_io.h>
|
2017-11-10 01:57:53 +01:00
|
|
|
#include <protocol.h>
|
|
|
|
#include <serialize.h>
|
|
|
|
#include <sync.h>
|
|
|
|
#include <util.h>
|
|
|
|
#include <utiltime.h>
|
|
|
|
#include <wallet/wallet.h>
|
2013-04-13 07:13:08 +02:00
|
|
|
|
2016-11-27 04:32:30 +01:00
|
|
|
#include <atomic>
|
|
|
|
|
Split up util.cpp/h
Split up util.cpp/h into:
- string utilities (hex, base32, base64): no internal dependencies, no dependency on boost (apart from foreach)
- money utilities (parsesmoney, formatmoney)
- time utilities (gettime*, sleep, format date):
- and the rest (logging, argument parsing, config file parsing)
The latter is basically the environment and OS handling,
and is stripped of all utility functions, so we may want to
rename it to something else than util.cpp/h for clarity (Matt suggested
osinterface).
Breaks dependency of sha256.cpp on all the things pulled in by util.
2014-08-21 16:11:09 +02:00
|
|
|
#include <boost/thread.hpp>
|
2012-04-15 23:39:49 +02:00
|
|
|
|
|
|
|
//
|
2017-12-08 12:39:22 +01:00
|
|
|
// WalletBatch
|
2012-04-15 23:39:49 +02:00
|
|
|
//
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName)
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::make_pair(std::string("name"), strAddress), strName);
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::EraseName(const std::string& strAddress)
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
|
|
|
// 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.
|
2017-03-09 21:29:01 +01:00
|
|
|
return EraseIC(std::make_pair(std::string("name"), strAddress));
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
|
2013-07-22 08:50:39 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose);
|
2013-07-22 08:50:39 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::ErasePurpose(const std::string& strAddress)
|
2013-07-22 08:50:39 +02:00
|
|
|
{
|
2017-09-27 00:02:09 +02:00
|
|
|
return EraseIC(std::make_pair(std::string("purpose"), strAddress));
|
2013-07-22 08:50:39 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteTx(const CWalletTx& wtx)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::EraseTx(uint256 hash)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return EraseIC(std::make_pair(std::string("tx"), hash));
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-06-06 00:23:20 +02:00
|
|
|
if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) {
|
2013-04-13 07:13:08 +02:00
|
|
|
return false;
|
2017-06-06 00:23:20 +02:00
|
|
|
}
|
2013-04-13 07:13:08 +02:00
|
|
|
|
|
|
|
// hash pubkey/privkey to accelerate wallet load
|
|
|
|
std::vector<unsigned char> vchKey;
|
|
|
|
vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
|
|
|
|
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
|
|
|
|
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
|
|
|
|
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
|
2013-11-15 12:24:34 +01:00
|
|
|
const std::vector<unsigned char>& vchCryptedSecret,
|
2013-04-13 07:13:08 +02:00
|
|
|
const CKeyMetadata &keyMeta)
|
|
|
|
{
|
2017-06-06 00:23:20 +02:00
|
|
|
if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) {
|
2013-04-13 07:13:08 +02:00
|
|
|
return false;
|
2017-06-06 00:23:20 +02:00
|
|
|
}
|
2013-04-13 07:13:08 +02:00
|
|
|
|
2017-06-06 00:23:20 +02:00
|
|
|
if (!WriteIC(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) {
|
2013-04-13 07:13:08 +02:00
|
|
|
return false;
|
2017-06-06 00:23:20 +02:00
|
|
|
}
|
2017-06-12 15:01:46 +02:00
|
|
|
EraseIC(std::make_pair(std::string("key"), vchPubKey));
|
|
|
|
EraseIC(std::make_pair(std::string("wkey"), vchPubKey));
|
2013-04-13 07:13:08 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-07-07 09:54:42 +02:00
|
|
|
return WriteIC(std::make_pair(std::string("cscript"), hash), redeemScript, false);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
|
2013-07-26 01:06:01 +02:00
|
|
|
{
|
2017-07-07 09:54:42 +02:00
|
|
|
if (!WriteIC(std::make_pair(std::string("watchmeta"), dest), keyMeta)) {
|
2016-11-08 22:55:02 +01:00
|
|
|
return false;
|
2017-06-06 00:23:20 +02:00
|
|
|
}
|
2017-07-07 09:54:42 +02:00
|
|
|
return WriteIC(std::make_pair(std::string("watchs"), dest), '1');
|
2013-07-26 01:06:01 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::EraseWatchOnly(const CScript &dest)
|
2014-07-26 21:05:11 +02:00
|
|
|
{
|
2017-07-07 09:54:42 +02:00
|
|
|
if (!EraseIC(std::make_pair(std::string("watchmeta"), dest))) {
|
2016-11-08 22:55:02 +01:00
|
|
|
return false;
|
2017-06-06 00:23:20 +02:00
|
|
|
}
|
2017-07-07 09:54:42 +02:00
|
|
|
return EraseIC(std::make_pair(std::string("watchs"), dest));
|
2014-07-26 21:05:11 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
WriteIC(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
|
|
|
|
return WriteIC(std::string("bestblock_nomerkle"), locator);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2018-03-20 19:48:33 +01:00
|
|
|
if (m_batch.Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
|
|
|
|
return m_batch.Read(std::string("bestblock_nomerkle"), locator);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::string("orderposnext"), nOrderPosNext);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2018-03-20 19:48:33 +01:00
|
|
|
return m_batch.Read(std::make_pair(std::string("pool"), nPool), keypool);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::make_pair(std::string("pool"), nPool), keypool);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::ErasePool(int64_t nPool)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return EraseIC(std::make_pair(std::string("pool"), nPool));
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteMinVersion(int nVersion)
|
2013-04-13 07:13:08 +02:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::string("minversion"), nVersion);
|
2013-04-13 07:13:08 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::ReadAccount(const std::string& strAccount, CAccount& account)
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
|
|
|
account.SetNull();
|
2018-03-20 19:48:33 +01:00
|
|
|
return m_batch.Read(std::make_pair(std::string("acc"), strAccount), account);
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteAccount(const std::string& strAccount, const CAccount& account)
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
2017-03-09 21:30:31 +01:00
|
|
|
return WriteIC(std::make_pair(std::string("acc"), strAccount), account);
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
2016-03-21 16:55:02 +01:00
|
|
|
bool WalletBatch::EraseAccount(const std::string& strAccount)
|
|
|
|
{
|
|
|
|
return EraseIC(std::make_pair(std::string("acc"), strAccount));
|
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry)
|
2012-05-28 01:06:09 +02:00
|
|
|
{
|
2017-03-09 21:30:31 +01:00
|
|
|
return WriteIC(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
CAmount WalletBatch::GetAccountCreditDebit(const std::string& strAccount)
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
2017-01-27 02:33:45 +01:00
|
|
|
std::list<CAccountingEntry> entries;
|
2012-04-15 23:39:49 +02:00
|
|
|
ListAccountCreditDebit(strAccount, entries);
|
|
|
|
|
2014-04-23 00:46:19 +02:00
|
|
|
CAmount nCreditDebit = 0;
|
2017-06-02 03:18:57 +02:00
|
|
|
for (const CAccountingEntry& entry : entries)
|
2012-04-15 23:39:49 +02:00
|
|
|
nCreditDebit += entry.nCreditDebit;
|
|
|
|
|
|
|
|
return nCreditDebit;
|
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
void WalletBatch::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries)
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
|
|
|
bool fAllAccounts = (strAccount == "*");
|
|
|
|
|
2018-03-20 19:48:33 +01:00
|
|
|
Dbc* pcursor = m_batch.GetCursor();
|
2012-04-15 23:39:49 +02:00
|
|
|
if (!pcursor)
|
2017-01-27 02:33:45 +01:00
|
|
|
throw std::runtime_error(std::string(__func__) + ": cannot create DB cursor");
|
2016-08-23 15:36:23 +02:00
|
|
|
bool setRange = true;
|
2013-07-31 06:06:44 +02:00
|
|
|
while (true)
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
|
|
|
// Read next record
|
2012-04-16 14:56:45 +02:00
|
|
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
2016-08-23 15:36:23 +02:00
|
|
|
if (setRange)
|
2017-01-27 02:33:45 +01:00
|
|
|
ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? std::string("") : strAccount), uint64_t(0)));
|
2012-04-16 14:56:45 +02:00
|
|
|
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
2018-03-20 19:48:33 +01:00
|
|
|
int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue, setRange);
|
2016-08-23 15:36:23 +02:00
|
|
|
setRange = false;
|
2012-04-15 23:39:49 +02:00
|
|
|
if (ret == DB_NOTFOUND)
|
|
|
|
break;
|
|
|
|
else if (ret != 0)
|
|
|
|
{
|
|
|
|
pcursor->close();
|
2017-01-27 02:33:45 +01:00
|
|
|
throw std::runtime_error(std::string(__func__) + ": error scanning DB");
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Unserialize
|
2017-01-27 02:33:45 +01:00
|
|
|
std::string strType;
|
2012-04-15 23:39:49 +02:00
|
|
|
ssKey >> strType;
|
|
|
|
if (strType != "acentry")
|
|
|
|
break;
|
|
|
|
CAccountingEntry acentry;
|
|
|
|
ssKey >> acentry.strAccount;
|
|
|
|
if (!fAllAccounts && acentry.strAccount != strAccount)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssValue >> acentry;
|
2012-05-28 01:06:09 +02:00
|
|
|
ssKey >> acentry.nEntryNo;
|
2012-04-15 23:39:49 +02:00
|
|
|
entries.push_back(acentry);
|
|
|
|
}
|
|
|
|
|
|
|
|
pcursor->close();
|
|
|
|
}
|
|
|
|
|
2013-06-10 15:36:29 +02:00
|
|
|
class CWalletScanState {
|
|
|
|
public:
|
|
|
|
unsigned int nKeys;
|
|
|
|
unsigned int nCKeys;
|
2016-11-08 22:55:02 +01:00
|
|
|
unsigned int nWatchKeys;
|
2013-06-10 15:36:29 +02:00
|
|
|
unsigned int nKeyMeta;
|
2018-04-05 02:46:40 +02:00
|
|
|
unsigned int m_unknown_records;
|
2013-06-10 15:36:29 +02:00
|
|
|
bool fIsEncrypted;
|
|
|
|
bool fAnyUnordered;
|
|
|
|
int nFileVersion;
|
2017-01-27 02:33:45 +01:00
|
|
|
std::vector<uint256> vWalletUpgrade;
|
2013-06-10 15:36:29 +02:00
|
|
|
|
|
|
|
CWalletScanState() {
|
2018-04-05 02:46:40 +02:00
|
|
|
nKeys = nCKeys = nWatchKeys = nKeyMeta = m_unknown_records = 0;
|
2013-06-10 15:36:29 +02:00
|
|
|
fIsEncrypted = false;
|
|
|
|
fAnyUnordered = false;
|
|
|
|
nFileVersion = 0;
|
|
|
|
}
|
|
|
|
};
|
2012-05-28 01:06:09 +02:00
|
|
|
|
2018-05-02 17:14:48 +02:00
|
|
|
static bool
|
2012-09-18 20:30:47 +02:00
|
|
|
ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
2017-01-27 02:33:45 +01:00
|
|
|
CWalletScanState &wss, std::string& strType, std::string& strErr)
|
2012-09-18 20:30:47 +02:00
|
|
|
{
|
|
|
|
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")
|
|
|
|
{
|
2017-01-27 02:33:45 +01:00
|
|
|
std::string strAddress;
|
2012-09-18 20:30:47 +02:00
|
|
|
ssKey >> strAddress;
|
2017-08-23 03:02:33 +02:00
|
|
|
ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name;
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
2013-07-22 08:50:39 +02:00
|
|
|
else if (strType == "purpose")
|
|
|
|
{
|
2017-01-27 02:33:45 +01:00
|
|
|
std::string strAddress;
|
2013-07-22 08:50:39 +02:00
|
|
|
ssKey >> strAddress;
|
2017-08-23 03:02:33 +02:00
|
|
|
ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose;
|
2013-07-22 08:50:39 +02:00
|
|
|
}
|
2012-09-18 20:30:47 +02:00
|
|
|
else if (strType == "tx")
|
|
|
|
{
|
|
|
|
uint256 hash;
|
|
|
|
ssKey >> hash;
|
2017-01-19 22:08:03 +01:00
|
|
|
CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef());
|
2012-09-18 20:30:47 +02:00
|
|
|
ssValue >> wtx;
|
2013-01-27 00:14:11 +01:00
|
|
|
CValidationState state;
|
2017-05-09 08:46:26 +02:00
|
|
|
if (!(CheckTransaction(*wtx.tx, state) && (wtx.GetHash() == hash) && state.IsValid()))
|
2012-09-18 20:30:47 +02:00
|
|
|
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",
|
2014-01-16 16:15:27 +01:00
|
|
|
wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString());
|
2012-09-18 20:30:47 +02:00
|
|
|
wtx.fTimeReceivedIsTxTime = fTmp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-01-16 16:15:27 +01:00
|
|
|
strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
|
2012-09-18 20:30:47 +02:00
|
|
|
wtx.fTimeReceivedIsTxTime = 0;
|
|
|
|
}
|
2013-06-10 15:36:29 +02:00
|
|
|
wss.vWalletUpgrade.push_back(hash);
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (wtx.nOrderPos == -1)
|
2013-06-10 15:36:29 +02:00
|
|
|
wss.fAnyUnordered = true;
|
2012-09-18 20:30:47 +02:00
|
|
|
|
2016-06-08 06:25:31 +02:00
|
|
|
pwallet->LoadToWallet(wtx);
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
|
|
|
else if (strType == "acentry")
|
|
|
|
{
|
2017-01-27 02:33:45 +01:00
|
|
|
std::string strAccount;
|
2012-09-18 20:30:47 +02:00
|
|
|
ssKey >> strAccount;
|
2013-04-13 07:13:08 +02:00
|
|
|
uint64_t nNumber;
|
2012-09-18 20:30:47 +02:00
|
|
|
ssKey >> nNumber;
|
2017-03-09 21:11:36 +01:00
|
|
|
if (nNumber > pwallet->nAccountingEntryNumber) {
|
|
|
|
pwallet->nAccountingEntryNumber = nNumber;
|
|
|
|
}
|
2012-09-18 20:30:47 +02:00
|
|
|
|
2013-06-10 15:36:29 +02:00
|
|
|
if (!wss.fAnyUnordered)
|
2012-09-18 20:30:47 +02:00
|
|
|
{
|
|
|
|
CAccountingEntry acentry;
|
|
|
|
ssValue >> acentry;
|
|
|
|
if (acentry.nOrderPos == -1)
|
2013-06-10 15:36:29 +02:00
|
|
|
wss.fAnyUnordered = true;
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-09 21:11:59 +02:00
|
|
|
else if (strType == "watchs")
|
2013-07-26 01:06:01 +02:00
|
|
|
{
|
2016-11-08 22:55:02 +01:00
|
|
|
wss.nWatchKeys++;
|
2014-06-09 21:11:59 +02:00
|
|
|
CScript script;
|
2017-07-07 09:54:42 +02:00
|
|
|
ssKey >> script;
|
2013-07-26 01:06:01 +02:00
|
|
|
char fYes;
|
|
|
|
ssValue >> fYes;
|
|
|
|
if (fYes == '1')
|
2014-06-09 21:11:59 +02:00
|
|
|
pwallet->LoadWatchOnly(script);
|
2013-07-26 01:06:01 +02:00
|
|
|
}
|
2012-09-18 20:30:47 +02:00
|
|
|
else if (strType == "key" || strType == "wkey")
|
|
|
|
{
|
2013-05-01 06:52:05 +02:00
|
|
|
CPubKey vchPubKey;
|
2012-09-18 20:30:47 +02:00
|
|
|
ssKey >> vchPubKey;
|
2013-05-01 06:52:05 +02:00
|
|
|
if (!vchPubKey.IsValid())
|
|
|
|
{
|
|
|
|
strErr = "Error reading wallet database: CPubKey corrupt";
|
|
|
|
return false;
|
|
|
|
}
|
2012-09-18 20:30:47 +02:00
|
|
|
CKey key;
|
2013-05-01 06:52:05 +02:00
|
|
|
CPrivKey pkey;
|
2014-12-15 09:11:16 +01:00
|
|
|
uint256 hash;
|
2013-11-15 12:24:34 +01:00
|
|
|
|
2012-09-18 20:30:47 +02:00
|
|
|
if (strType == "key")
|
2013-06-10 15:36:29 +02:00
|
|
|
{
|
|
|
|
wss.nKeys++;
|
2012-09-18 20:30:47 +02:00
|
|
|
ssValue >> pkey;
|
2013-06-10 15:36:29 +02:00
|
|
|
} else {
|
2012-09-18 20:30:47 +02:00
|
|
|
CWalletKey wkey;
|
|
|
|
ssValue >> wkey;
|
2013-05-01 06:52:05 +02:00
|
|
|
pkey = wkey.vchPrivKey;
|
|
|
|
}
|
2013-11-15 12:24:34 +01:00
|
|
|
|
2013-10-13 08:44:28 +02:00
|
|
|
// 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.
|
2013-08-29 08:53:26 +02:00
|
|
|
try
|
2013-05-01 06:52:05 +02:00
|
|
|
{
|
2013-08-29 08:53:26 +02:00
|
|
|
ssValue >> hash;
|
2013-05-01 06:52:05 +02:00
|
|
|
}
|
2014-12-07 13:29:06 +01:00
|
|
|
catch (...) {}
|
2013-11-15 12:24:34 +01:00
|
|
|
|
2013-08-29 08:53:26 +02:00
|
|
|
bool fSkipCheck = false;
|
2013-11-15 12:24:34 +01:00
|
|
|
|
2014-12-15 09:11:16 +01:00
|
|
|
if (!hash.IsNull())
|
2013-05-01 06:52:05 +02:00
|
|
|
{
|
2013-08-29 08:53:26 +02:00
|
|
|
// hash pubkey/privkey to accelerate wallet load
|
|
|
|
std::vector<unsigned char> vchKey;
|
|
|
|
vchKey.reserve(vchPubKey.size() + pkey.size());
|
|
|
|
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
|
|
|
|
vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
|
2013-11-15 12:24:34 +01:00
|
|
|
|
2013-08-29 08:53:26 +02:00
|
|
|
if (Hash(vchKey.begin(), vchKey.end()) != hash)
|
|
|
|
{
|
|
|
|
strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
|
|
|
|
return false;
|
|
|
|
}
|
2013-11-15 12:24:34 +01:00
|
|
|
|
2013-08-29 08:53:26 +02:00
|
|
|
fSkipCheck = true;
|
|
|
|
}
|
2013-11-15 12:24:34 +01:00
|
|
|
|
2013-08-29 08:53:26 +02:00
|
|
|
if (!key.Load(pkey, vchPubKey, fSkipCheck))
|
|
|
|
{
|
|
|
|
strErr = "Error reading wallet database: CPrivKey corrupt";
|
2013-05-01 06:52:05 +02:00
|
|
|
return false;
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
2013-05-01 06:52:05 +02:00
|
|
|
if (!pwallet->LoadKey(key, vchPubKey))
|
2012-09-18 20:30:47 +02:00
|
|
|
{
|
|
|
|
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")
|
|
|
|
{
|
2015-10-29 19:24:49 +01:00
|
|
|
CPubKey vchPubKey;
|
2012-09-18 20:30:47 +02:00
|
|
|
ssKey >> vchPubKey;
|
2015-10-29 19:24:49 +01:00
|
|
|
if (!vchPubKey.IsValid())
|
|
|
|
{
|
|
|
|
strErr = "Error reading wallet database: CPubKey corrupt";
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-27 02:33:45 +01:00
|
|
|
std::vector<unsigned char> vchPrivKey;
|
2012-09-18 20:30:47 +02:00
|
|
|
ssValue >> vchPrivKey;
|
2013-06-10 15:36:29 +02:00
|
|
|
wss.nCKeys++;
|
|
|
|
|
2012-09-18 20:30:47 +02:00
|
|
|
if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
|
|
|
|
{
|
|
|
|
strErr = "Error reading wallet database: LoadCryptedKey failed";
|
|
|
|
return false;
|
|
|
|
}
|
2013-06-10 15:36:29 +02:00
|
|
|
wss.fIsEncrypted = true;
|
|
|
|
}
|
2017-12-08 18:50:46 +01:00
|
|
|
else if (strType == "keymeta")
|
2013-06-10 15:36:29 +02:00
|
|
|
{
|
2017-12-08 18:50:46 +01:00
|
|
|
CPubKey vchPubKey;
|
|
|
|
ssKey >> vchPubKey;
|
2013-06-10 15:36:29 +02:00
|
|
|
CKeyMetadata keyMeta;
|
|
|
|
ssValue >> keyMeta;
|
|
|
|
wss.nKeyMeta++;
|
2017-12-08 18:50:46 +01:00
|
|
|
pwallet->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
|
|
|
|
}
|
|
|
|
else if (strType == "watchmeta")
|
|
|
|
{
|
|
|
|
CScript script;
|
|
|
|
ssKey >> script;
|
|
|
|
CKeyMetadata keyMeta;
|
|
|
|
ssValue >> keyMeta;
|
|
|
|
wss.nKeyMeta++;
|
|
|
|
pwallet->LoadScriptMetadata(CScriptID(script), keyMeta);
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
|
|
|
else if (strType == "defaultkey")
|
|
|
|
{
|
2017-07-29 02:00:49 +02:00
|
|
|
// We don't want or need the default key, but if there is one set,
|
|
|
|
// we want to make sure that it is valid so that we can detect corruption
|
|
|
|
CPubKey vchPubKey;
|
|
|
|
ssValue >> vchPubKey;
|
|
|
|
if (!vchPubKey.IsValid()) {
|
|
|
|
strErr = "Error reading wallet database: Default Key corrupt";
|
|
|
|
return false;
|
|
|
|
}
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
|
|
|
else if (strType == "pool")
|
|
|
|
{
|
2013-04-13 07:13:08 +02:00
|
|
|
int64_t nIndex;
|
2012-09-18 20:30:47 +02:00
|
|
|
ssKey >> nIndex;
|
2013-04-29 19:50:40 +02:00
|
|
|
CKeyPool keypool;
|
|
|
|
ssValue >> keypool;
|
2016-08-02 02:14:40 +02:00
|
|
|
|
|
|
|
pwallet->LoadKeyPool(nIndex, keypool);
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
|
|
|
else if (strType == "version")
|
|
|
|
{
|
2013-06-10 15:36:29 +02:00
|
|
|
ssValue >> wss.nFileVersion;
|
|
|
|
if (wss.nFileVersion == 10300)
|
|
|
|
wss.nFileVersion = 300;
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
|
|
|
else if (strType == "cscript")
|
|
|
|
{
|
|
|
|
uint160 hash;
|
|
|
|
ssKey >> hash;
|
|
|
|
CScript script;
|
2017-07-07 09:54:42 +02:00
|
|
|
ssValue >> script;
|
2012-09-18 20:30:47 +02:00
|
|
|
if (!pwallet->LoadCScript(script))
|
|
|
|
{
|
|
|
|
strErr = "Error reading wallet database: LoadCScript failed";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strType == "orderposnext")
|
|
|
|
{
|
|
|
|
ssValue >> pwallet->nOrderPosNext;
|
|
|
|
}
|
2013-11-18 16:55:54 +01:00
|
|
|
else if (strType == "destdata")
|
|
|
|
{
|
|
|
|
std::string strAddress, strKey, strValue;
|
|
|
|
ssKey >> strAddress;
|
|
|
|
ssKey >> strKey;
|
|
|
|
ssValue >> strValue;
|
2017-08-23 03:02:33 +02:00
|
|
|
if (!pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue))
|
2013-11-18 16:55:54 +01:00
|
|
|
{
|
|
|
|
strErr = "Error reading wallet database: LoadDestData failed";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-01-02 12:34:08 +01:00
|
|
|
else if (strType == "hdchain")
|
|
|
|
{
|
|
|
|
CHDChain chain;
|
|
|
|
ssValue >> chain;
|
|
|
|
if (!pwallet->SetHDChain(chain, true))
|
|
|
|
{
|
|
|
|
strErr = "Error reading wallet database: SetHDChain failed";
|
|
|
|
return false;
|
|
|
|
}
|
2018-04-05 02:46:40 +02:00
|
|
|
} else if (strType != "bestblock" && strType != "bestblock_nomerkle"){
|
|
|
|
wss.m_unknown_records++;
|
2016-01-02 12:34:08 +01:00
|
|
|
}
|
2012-09-18 20:30:47 +02:00
|
|
|
} catch (...)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::IsKeyType(const std::string& strType)
|
2012-09-18 20:30:47 +02:00
|
|
|
{
|
|
|
|
return (strType== "key" || strType == "wkey" ||
|
|
|
|
strType == "mkey" || strType == "ckey");
|
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
2013-06-10 15:36:29 +02:00
|
|
|
CWalletScanState wss;
|
2012-09-18 20:30:47 +02:00
|
|
|
bool fNoncriticalErrors = false;
|
2018-03-09 15:03:40 +01:00
|
|
|
DBErrors result = DBErrors::LOAD_OK;
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2017-02-15 23:01:30 +01:00
|
|
|
LOCK(pwallet->cs_wallet);
|
2012-09-18 20:30:47 +02:00
|
|
|
try {
|
2012-04-15 23:39:49 +02:00
|
|
|
int nMinVersion = 0;
|
2018-03-20 19:48:33 +01:00
|
|
|
if (m_batch.Read((std::string)"minversion", nMinVersion))
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
|
|
|
if (nMinVersion > CLIENT_VERSION)
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::TOO_NEW;
|
2012-04-15 23:39:49 +02:00
|
|
|
pwallet->LoadMinVersion(nMinVersion);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get cursor
|
2018-03-20 19:48:33 +01:00
|
|
|
Dbc* pcursor = m_batch.GetCursor();
|
2012-04-15 23:39:49 +02:00
|
|
|
if (!pcursor)
|
|
|
|
{
|
2013-09-18 12:38:08 +02:00
|
|
|
LogPrintf("Error getting wallet database cursor\n");
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::CORRUPT;
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
2013-07-31 06:06:44 +02:00
|
|
|
while (true)
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
|
|
|
// Read next record
|
2012-04-16 14:56:45 +02:00
|
|
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
|
|
|
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
2018-03-20 19:48:33 +01:00
|
|
|
int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue);
|
2012-04-15 23:39:49 +02:00
|
|
|
if (ret == DB_NOTFOUND)
|
|
|
|
break;
|
|
|
|
else if (ret != 0)
|
|
|
|
{
|
2013-09-18 12:38:08 +02:00
|
|
|
LogPrintf("Error reading next record from wallet database\n");
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::CORRUPT;
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
2012-09-18 20:30:47 +02:00
|
|
|
// Try to be tolerant of single corrupt records:
|
2017-01-27 02:33:45 +01:00
|
|
|
std::string strType, strErr;
|
2013-06-10 15:36:29 +02:00
|
|
|
if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
2012-09-18 20:30:47 +02:00
|
|
|
// losing keys is considered a catastrophic error, anything else
|
|
|
|
// we assume the user can live with:
|
2017-07-29 02:00:49 +02:00
|
|
|
if (IsKeyType(strType) || strType == "defaultkey")
|
2018-03-09 15:03:40 +01:00
|
|
|
result = DBErrors::CORRUPT;
|
2012-04-15 23:39:49 +02:00
|
|
|
else
|
|
|
|
{
|
2012-09-18 20:30:47 +02:00
|
|
|
// 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:
|
2017-08-01 21:17:40 +02:00
|
|
|
gArgs.SoftSetBoolArg("-rescan", true);
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
2012-09-08 06:55:36 +02:00
|
|
|
}
|
2012-09-18 20:30:47 +02:00
|
|
|
if (!strErr.empty())
|
2014-01-16 16:15:27 +01:00
|
|
|
LogPrintf("%s\n", strErr);
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
pcursor->close();
|
|
|
|
}
|
2014-12-07 13:29:06 +01:00
|
|
|
catch (const boost::thread_interrupted&) {
|
2013-03-09 18:02:57 +01:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
catch (...) {
|
2018-03-09 15:03:40 +01:00
|
|
|
result = DBErrors::CORRUPT;
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2018-03-09 15:03:40 +01:00
|
|
|
if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
|
|
|
|
result = DBErrors::NONCRITICAL_ERROR;
|
2012-09-18 20:30:47 +02:00
|
|
|
|
|
|
|
// Any wallet corruption at all: skip any rewriting or
|
|
|
|
// upgrading, we don't want to make it worse.
|
2018-03-09 15:03:40 +01:00
|
|
|
if (result != DBErrors::LOAD_OK)
|
2012-09-18 20:30:47 +02:00
|
|
|
return result;
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2013-09-18 12:38:08 +02:00
|
|
|
LogPrintf("nFileVersion = %d\n", wss.nFileVersion);
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2018-04-05 02:46:40 +02:00
|
|
|
LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n",
|
|
|
|
wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records);
|
2013-06-10 15:36:29 +02:00
|
|
|
|
|
|
|
// nTimeFirstKey is only reliable if all keys have metadata
|
2016-11-08 22:55:02 +01:00
|
|
|
if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta)
|
2016-11-08 22:28:20 +01:00
|
|
|
pwallet->UpdateTimeFirstKey(1);
|
2013-06-10 15:36:29 +02:00
|
|
|
|
2017-06-02 03:18:57 +02:00
|
|
|
for (uint256 hash : wss.vWalletUpgrade)
|
2017-01-19 22:08:03 +01:00
|
|
|
WriteTx(pwallet->mapWallet.at(hash));
|
2012-04-15 23:39:49 +02:00
|
|
|
|
|
|
|
// Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
|
2013-06-10 15:36:29 +02:00
|
|
|
if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000))
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::NEED_REWRITE;
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2013-06-10 15:36:29 +02:00
|
|
|
if (wss.nFileVersion < CLIENT_VERSION) // Update
|
2012-04-15 23:39:49 +02:00
|
|
|
WriteVersion(CLIENT_VERSION);
|
|
|
|
|
2013-06-10 15:36:29 +02:00
|
|
|
if (wss.fAnyUnordered)
|
2016-09-28 17:57:25 +02:00
|
|
|
result = pwallet->ReorderTransactions();
|
2012-05-28 01:06:09 +02:00
|
|
|
|
2015-10-19 11:19:38 +02:00
|
|
|
pwallet->laccentries.clear();
|
|
|
|
ListAccountCreditDebit("*", pwallet->laccentries);
|
2017-06-02 03:18:57 +02:00
|
|
|
for (CAccountingEntry& entry : pwallet->laccentries) {
|
2017-06-21 21:10:00 +02:00
|
|
|
pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair(nullptr, &entry)));
|
2015-10-19 11:19:38 +02:00
|
|
|
}
|
|
|
|
|
2012-09-18 20:30:47 +02:00
|
|
|
return result;
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx)
|
2014-02-14 17:33:07 +01:00
|
|
|
{
|
2018-03-09 15:03:40 +01:00
|
|
|
DBErrors result = DBErrors::LOAD_OK;
|
2014-02-14 17:33:07 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
int nMinVersion = 0;
|
2018-03-20 19:48:33 +01:00
|
|
|
if (m_batch.Read((std::string)"minversion", nMinVersion))
|
2014-02-14 17:33:07 +01:00
|
|
|
{
|
|
|
|
if (nMinVersion > CLIENT_VERSION)
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::TOO_NEW;
|
2014-02-14 17:33:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get cursor
|
2018-03-20 19:48:33 +01:00
|
|
|
Dbc* pcursor = m_batch.GetCursor();
|
2014-02-14 17:33:07 +01:00
|
|
|
if (!pcursor)
|
|
|
|
{
|
|
|
|
LogPrintf("Error getting wallet database cursor\n");
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::CORRUPT;
|
2014-02-14 17:33:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
// Read next record
|
|
|
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
|
|
|
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
2018-03-20 19:48:33 +01:00
|
|
|
int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue);
|
2014-02-14 17:33:07 +01:00
|
|
|
if (ret == DB_NOTFOUND)
|
|
|
|
break;
|
|
|
|
else if (ret != 0)
|
|
|
|
{
|
|
|
|
LogPrintf("Error reading next record from wallet database\n");
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::CORRUPT;
|
2014-02-14 17:33:07 +01:00
|
|
|
}
|
|
|
|
|
2017-01-27 02:33:45 +01:00
|
|
|
std::string strType;
|
2014-02-14 17:33:07 +01:00
|
|
|
ssKey >> strType;
|
|
|
|
if (strType == "tx") {
|
|
|
|
uint256 hash;
|
|
|
|
ssKey >> hash;
|
|
|
|
|
2017-01-19 22:08:03 +01:00
|
|
|
CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef());
|
2014-02-14 18:27:15 +01:00
|
|
|
ssValue >> wtx;
|
|
|
|
|
2014-02-14 17:33:07 +01:00
|
|
|
vTxHash.push_back(hash);
|
2014-02-14 18:27:15 +01:00
|
|
|
vWtx.push_back(wtx);
|
2014-02-14 17:33:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pcursor->close();
|
|
|
|
}
|
2014-12-07 13:29:06 +01:00
|
|
|
catch (const boost::thread_interrupted&) {
|
2014-02-14 17:33:07 +01:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
catch (...) {
|
2018-03-09 15:03:40 +01:00
|
|
|
result = DBErrors::CORRUPT;
|
2014-02-14 17:33:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uint256>& vTxHashOut)
|
2016-03-07 14:51:06 +01:00
|
|
|
{
|
|
|
|
// build list of wallet TXs and hashes
|
2017-01-27 02:33:45 +01:00
|
|
|
std::vector<uint256> vTxHash;
|
|
|
|
std::vector<CWalletTx> vWtx;
|
2016-11-12 10:53:18 +01:00
|
|
|
DBErrors err = FindWalletTx(vTxHash, vWtx);
|
2018-03-09 15:03:40 +01:00
|
|
|
if (err != DBErrors::LOAD_OK) {
|
2016-03-07 14:51:06 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::sort(vTxHash.begin(), vTxHash.end());
|
|
|
|
std::sort(vTxHashIn.begin(), vTxHashIn.end());
|
|
|
|
|
|
|
|
// erase each matching wallet TX
|
|
|
|
bool delerror = false;
|
2017-01-27 02:33:45 +01:00
|
|
|
std::vector<uint256>::iterator it = vTxHashIn.begin();
|
2017-06-02 03:18:57 +02:00
|
|
|
for (uint256 hash : vTxHash) {
|
2016-03-07 14:51:06 +01:00
|
|
|
while (it < vTxHashIn.end() && (*it) < hash) {
|
|
|
|
it++;
|
|
|
|
}
|
|
|
|
if (it == vTxHashIn.end()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if ((*it) == hash) {
|
|
|
|
if(!EraseTx(hash)) {
|
2016-12-25 21:19:40 +01:00
|
|
|
LogPrint(BCLog::DB, "Transaction was found for deletion but returned database error: %s\n", hash.GetHex());
|
2016-03-07 14:51:06 +01:00
|
|
|
delerror = true;
|
|
|
|
}
|
|
|
|
vTxHashOut.push_back(hash);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (delerror) {
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::CORRUPT;
|
2016-03-07 14:51:06 +01:00
|
|
|
}
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::LOAD_OK;
|
2016-03-07 14:51:06 +01:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
DBErrors WalletBatch::ZapWalletTx(std::vector<CWalletTx>& vWtx)
|
2014-02-14 17:33:07 +01:00
|
|
|
{
|
|
|
|
// build list of wallet TXs
|
2017-01-27 02:33:45 +01:00
|
|
|
std::vector<uint256> vTxHash;
|
2016-11-12 10:53:18 +01:00
|
|
|
DBErrors err = FindWalletTx(vTxHash, vWtx);
|
2018-03-09 15:03:40 +01:00
|
|
|
if (err != DBErrors::LOAD_OK)
|
2014-02-14 17:33:07 +01:00
|
|
|
return err;
|
|
|
|
|
|
|
|
// erase each wallet TX
|
2017-06-02 03:18:57 +02:00
|
|
|
for (uint256& hash : vTxHash) {
|
2014-02-14 17:33:07 +01:00
|
|
|
if (!EraseTx(hash))
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::CORRUPT;
|
2014-02-14 17:33:07 +01:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:03:40 +01:00
|
|
|
return DBErrors::LOAD_OK;
|
2014-02-14 17:33:07 +01:00
|
|
|
}
|
|
|
|
|
2017-01-23 15:27:59 +01:00
|
|
|
void MaybeCompactWalletDB()
|
2012-04-15 23:39:49 +02:00
|
|
|
{
|
2017-07-14 00:25:56 +02:00
|
|
|
static std::atomic<bool> fOneThread(false);
|
2017-02-08 19:19:18 +01:00
|
|
|
if (fOneThread.exchange(true)) {
|
2012-04-15 23:39:49 +02:00
|
|
|
return;
|
2017-02-08 19:19:18 +01:00
|
|
|
}
|
2017-08-01 21:17:40 +02:00
|
|
|
if (!gArgs.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
|
2012-04-15 23:39:49 +02:00
|
|
|
return;
|
2017-02-08 19:19:18 +01:00
|
|
|
}
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2018-04-17 19:22:23 +02:00
|
|
|
for (CWallet* pwallet : GetWallets()) {
|
2017-12-08 12:39:22 +01:00
|
|
|
WalletDatabase& dbh = pwallet->GetDBHandle();
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2016-09-09 10:42:30 +02:00
|
|
|
unsigned int nUpdateCounter = dbh.nUpdateCounter;
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2016-09-09 10:42:30 +02:00
|
|
|
if (dbh.nLastSeen != nUpdateCounter) {
|
|
|
|
dbh.nLastSeen = nUpdateCounter;
|
|
|
|
dbh.nLastWalletUpdate = GetTime();
|
|
|
|
}
|
2012-04-15 23:39:49 +02:00
|
|
|
|
2016-09-09 10:42:30 +02:00
|
|
|
if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) {
|
2017-12-08 12:39:22 +01:00
|
|
|
if (BerkeleyBatch::PeriodicFlush(dbh)) {
|
2016-09-09 10:42:30 +02:00
|
|
|
dbh.nLastFlushed = nUpdateCounter;
|
|
|
|
}
|
2017-03-08 11:48:58 +01:00
|
|
|
}
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
2016-09-09 10:42:30 +02:00
|
|
|
|
2017-02-08 19:19:18 +01:00
|
|
|
fOneThread = false;
|
2012-04-15 23:39:49 +02:00
|
|
|
}
|
|
|
|
|
2012-09-18 20:30:47 +02:00
|
|
|
//
|
2016-02-26 12:48:53 +01:00
|
|
|
// Try to (very carefully!) recover wallet file if there is a problem.
|
2012-09-18 20:30:47 +02:00
|
|
|
//
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
|
2016-08-24 09:57:23 +02:00
|
|
|
{
|
2017-12-08 12:39:22 +01:00
|
|
|
return BerkeleyBatch::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename);
|
2016-08-24 09:57:23 +02:00
|
|
|
}
|
2012-09-18 20:30:47 +02:00
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::Recover(const fs::path& wallet_path, std::string& out_backup_filename)
|
2016-08-24 09:57:23 +02:00
|
|
|
{
|
|
|
|
// recover without a key filter callback
|
|
|
|
// results in recovering all record types
|
2017-12-08 12:39:22 +01:00
|
|
|
return WalletBatch::Recover(wallet_path, nullptr, nullptr, out_backup_filename);
|
2016-08-24 09:57:23 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
|
2016-08-24 09:57:23 +02:00
|
|
|
{
|
|
|
|
CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData);
|
|
|
|
CWalletScanState dummyWss;
|
|
|
|
std::string strType, strErr;
|
|
|
|
bool fReadOK;
|
2012-09-18 20:30:47 +02:00
|
|
|
{
|
2016-08-24 09:57:23 +02:00
|
|
|
// Required in LoadKeyMetadata():
|
|
|
|
LOCK(dummyWallet->cs_wallet);
|
|
|
|
fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue,
|
|
|
|
dummyWss, strType, strErr);
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
2016-08-24 09:57:23 +02:00
|
|
|
if (!IsKeyType(strType) && strType != "hdchain")
|
|
|
|
return false;
|
|
|
|
if (!fReadOK)
|
2012-09-18 20:30:47 +02:00
|
|
|
{
|
2017-12-08 12:39:22 +01:00
|
|
|
LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, strErr);
|
2012-09-18 20:30:47 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-24 09:57:23 +02:00
|
|
|
return true;
|
|
|
|
}
|
2012-09-18 20:30:47 +02:00
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr)
|
2016-08-24 09:57:23 +02:00
|
|
|
{
|
2017-12-08 12:39:22 +01:00
|
|
|
return BerkeleyBatch::VerifyEnvironment(wallet_path, errorStr);
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr)
|
2012-09-18 20:30:47 +02:00
|
|
|
{
|
2017-12-08 12:39:22 +01:00
|
|
|
return BerkeleyBatch::VerifyDatabaseFile(wallet_path, warningStr, errorStr, WalletBatch::Recover);
|
2012-09-18 20:30:47 +02:00
|
|
|
}
|
2013-11-18 16:55:54 +01:00
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
|
2013-11-18 16:55:54 +01:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
|
2013-11-18 16:55:54 +01:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::EraseDestData(const std::string &address, const std::string &key)
|
2013-11-18 16:55:54 +01:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return EraseIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
|
2013-11-18 16:55:54 +01:00
|
|
|
}
|
2016-01-02 12:34:08 +01:00
|
|
|
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteHDChain(const CHDChain& chain)
|
2016-01-02 12:34:08 +01:00
|
|
|
{
|
2017-03-09 21:29:01 +01:00
|
|
|
return WriteIC(std::string("hdchain"), chain);
|
2016-11-27 04:32:30 +01:00
|
|
|
}
|
2017-03-08 11:48:58 +01:00
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::TxnBegin()
|
2017-03-08 17:20:08 +01:00
|
|
|
{
|
2018-03-20 19:48:33 +01:00
|
|
|
return m_batch.TxnBegin();
|
2017-03-08 17:20:08 +01:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::TxnCommit()
|
2017-03-08 17:20:08 +01:00
|
|
|
{
|
2018-03-20 19:48:33 +01:00
|
|
|
return m_batch.TxnCommit();
|
2017-03-08 17:20:08 +01:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::TxnAbort()
|
2017-03-08 17:20:08 +01:00
|
|
|
{
|
2018-03-20 19:48:33 +01:00
|
|
|
return m_batch.TxnAbort();
|
2017-03-08 17:20:08 +01:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::ReadVersion(int& nVersion)
|
2017-03-08 17:20:08 +01:00
|
|
|
{
|
2018-03-20 19:48:33 +01:00
|
|
|
return m_batch.ReadVersion(nVersion);
|
2017-03-08 17:20:08 +01:00
|
|
|
}
|
|
|
|
|
2017-12-08 12:39:22 +01:00
|
|
|
bool WalletBatch::WriteVersion(int nVersion)
|
2017-03-08 17:20:08 +01:00
|
|
|
{
|
2018-03-20 19:48:33 +01:00
|
|
|
return m_batch.WriteVersion(nVersion);
|
2017-03-08 17:20:08 +01:00
|
|
|
}
|