dash/src/wallet/walletdb.cpp

937 lines
28 KiB
C++
Raw Normal View History

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
2016-12-20 14:26:45 +01:00
// Copyright (c) 2014-2017 The Dash Core developers
2014-12-13 05:09:33 +01:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "wallet/walletdb.h"
#include "base58.h"
#include "consensus/validation.h"
#include "fs.h"
#include "validation.h" // For CheckTransaction
#include "protocol.h"
#include "serialize.h"
#include "sync.h"
#include "util.h"
#include "utiltime.h"
#include "wallet/wallet.h"
#include <atomic>
#include <boost/foreach.hpp>
#include <boost/thread.hpp>
static uint64_t nAccountingEntryNumber = 0;
static std::atomic<unsigned int> nWalletDBUpdateCounter;
//
// CWalletDB
//
bool CWalletDB::WriteName(const std::string& strAddress, const std::string& strName)
{
nWalletDBUpdateCounter++;
return Write(make_pair(std::string("name"), strAddress), strName);
}
bool CWalletDB::EraseName(const std::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.
nWalletDBUpdateCounter++;
return Erase(make_pair(std::string("name"), strAddress));
}
bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
{
nWalletDBUpdateCounter++;
return Write(make_pair(std::string("purpose"), strAddress), strPurpose);
}
bool CWalletDB::ErasePurpose(const std::string& strPurpose)
{
nWalletDBUpdateCounter++;
return Erase(make_pair(std::string("purpose"), strPurpose));
}
bool CWalletDB::WriteTx(const CWalletTx& wtx)
{
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
}
bool CWalletDB::EraseTx(uint256 hash)
{
nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("tx"), hash));
}
bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
{
nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta, false))
return false;
// 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());
return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
}
2013-11-15 12:24:34 +01:00
bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
const std::vector<unsigned char>& vchCryptedSecret,
const CKeyMetadata &keyMeta)
{
const bool fEraseUnencryptedKey = true;
nWalletDBUpdateCounter++;
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)
{
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
}
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
{
nWalletDBUpdateCounter++;
2015-10-29 07:11:24 +01:00
return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
}
bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
{
nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta))
return false;
2015-10-29 07:11:24 +01:00
return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
}
2014-07-26 21:05:11 +02:00
bool CWalletDB::EraseWatchOnly(const CScript &dest)
{
nWalletDBUpdateCounter++;
if (!Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest))))
return false;
2015-10-29 07:11:24 +01:00
return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
2014-07-26 21:05:11 +02:00
}
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdateCounter++;
Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
return Write(std::string("bestblock_nomerkle"), locator);
}
bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
{
if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
return Read(std::string("bestblock_nomerkle"), locator);
}
bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
{
nWalletDBUpdateCounter++;
return Write(std::string("orderposnext"), nOrderPosNext);
}
bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
{
nWalletDBUpdateCounter++;
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)
{
nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("pool"), nPool), keypool);
}
bool CWalletDB::ErasePool(int64_t nPool)
{
nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("pool"), nPool));
}
bool CWalletDB::WriteMinVersion(int nVersion)
{
return Write(std::string("minversion"), nVersion);
}
bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account)
{
account.SetNull();
return Read(make_pair(std::string("acc"), strAccount), account);
}
bool CWalletDB::WriteAccount(const std::string& strAccount, const CAccount& account)
{
return Write(make_pair(std::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 std::string& strAccount)
{
std::list<CAccountingEntry> entries;
ListAccountCreditDebit(strAccount, entries);
2014-04-23 00:46:19 +02:00
CAmount nCreditDebit = 0;
BOOST_FOREACH (const CAccountingEntry& entry, entries)
nCreditDebit += entry.nCreditDebit;
return nCreditDebit;
}
void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries)
{
bool fAllAccounts = (strAccount == "*");
Dbc* pcursor = GetCursor();
if (!pcursor)
throw std::runtime_error(std::string(__func__) + ": cannot create DB cursor");
bool setRange = true;
while (true)
{
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
if (setRange)
ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? std::string("") : strAccount), uint64_t(0)));
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
int ret = ReadAtCursor(pcursor, ssKey, ssValue, setRange);
setRange = false;
if (ret == DB_NOTFOUND)
break;
else if (ret != 0)
{
pcursor->close();
throw std::runtime_error(std::string(__func__) + ": error scanning DB");
}
// Unserialize
std::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();
}
class CWalletScanState {
public:
unsigned int nKeys;
unsigned int nCKeys;
unsigned int nWatchKeys;
unsigned int nKeyMeta;
bool fIsEncrypted;
bool fAnyUnordered;
int nFileVersion;
std::vector<uint256> vWalletUpgrade;
CWalletScanState() {
nKeys = nCKeys = nWatchKeys = nKeyMeta = 0;
fIsEncrypted = false;
fAnyUnordered = false;
nFileVersion = 0;
}
};
bool
ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CWalletScanState &wss, std::string& strType, std::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")
{
std::string strAddress;
ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name;
}
else if (strType == "purpose")
{
std::string strAddress;
ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose;
}
else if (strType == "tx")
{
uint256 hash;
ssKey >> hash;
CWalletTx wtx;
ssValue >> wtx;
2013-01-27 00:14:11 +01:00
CValidationState state;
if (!(CheckTransaction(wtx, 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->LoadToWallet(wtx);
}
else if (strType == "acentry")
{
std::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")
{
wss.nWatchKeys++;
CScript script;
2015-10-29 07:11:24 +01:00
ssKey >> *(CScriptBase*)(&script);
char fYes;
ssValue >> fYes;
if (fYes == '1')
pwallet->LoadWatchOnly(script);
}
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;
2013-11-15 12:24:34 +01:00
if (strType == "key")
{
wss.nKeys++;
ssValue >> pkey;
} else {
CWalletKey wkey;
ssValue >> wkey;
pkey = wkey.vchPrivKey;
}
2013-11-15 12:24:34 +01: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.
try
{
ssValue >> hash;
}
catch (...) {}
2013-11-15 12:24:34 +01:00
bool fSkipCheck = false;
2013-11-15 12:24:34 +01:00
if (!hash.IsNull())
{
// 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
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
fSkipCheck = true;
}
2013-11-15 12:24:34 +01:00
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;
if (!vchPubKey.IsValid())
{
strErr = "Error reading wallet database: CPubKey corrupt";
return false;
}
std::vector<unsigned char> 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" || strType == "watchmeta")
{
CTxDestination keyID;
if (strType == "keymeta")
{
CPubKey vchPubKey;
ssKey >> vchPubKey;
keyID = vchPubKey.GetID();
}
else if (strType == "watchmeta")
{
CScript script;
ssKey >> *(CScriptBase*)(&script);
keyID = CScriptID(script);
}
CKeyMetadata keyMeta;
ssValue >> keyMeta;
wss.nKeyMeta++;
pwallet->LoadKeyMetadata(keyID, keyMeta);
}
else if (strType == "defaultkey")
{
ssValue >> pwallet->vchDefaultKey;
}
else if (strType == "pool")
{
int64_t nIndex;
ssKey >> nIndex;
CKeyPool keypool;
ssValue >> keypool;
HD wallet (#1405) * HD wallet Minimal set of changes (no refactoring) backported from Bitcoin upstream to make HD wallets work in Dash 0.12.1.x+ * minimal bip44 (hardcoded account and change) * minimal bip39 Additional cmd-line options for new wallet: -mnemonic -mnemonicpassphrase * Do not recreate HD wallet on encryption Adjusted keypool.py test * Do not store any private keys for hd wallet besides the master one Derive all keys on the fly. Original idea/implementation - btc PR9298, backported and improved * actually use bip39 * pbkdf2 test * backport wallet-hd.py test * Allow specifying hd seed, add dumphdseed rpc, fix bugs - -hdseed cmd-line param to specify HD seed on wallet creation - dumphdseed rpc to dump HD seed - allow seed of any size - fix dumpwallet rpc bug (wasn't decrypting HD seed) - print HD seed and extended public masterkey on dumpwallet * top up keypool on HD wallet encryption * split HD chain: external/internal * add missing cs_wallet lock in init.cpp * fix `const char *` issues (use strings) * default mnemonic passphrase is an empty string in all cases * store mnemonic/mnemonicpassphrase replace dumphdseed with dumphdinfo * Add fCrypted flag to CHDChain * prepare internal structures for multiple HD accounts (plus some code cleanup) * use secure allocator for storing sensitive HD data * use secure strings for mnemonic(passphrase) * small fix in GenerateNewHDChain * use 24 words for mnemonic by default * make sure mnemonic passphrase provided by user does not exceed 256 symbols * more usage of secure allocators and memory_cleanse * code cleanup * rename: CSecureVector -> SecureVector * add missing include * fix warning in rpcdump.cpp * refactor mnemonic_check (also fix a bug) * move bip39 functions to CMnemonic * Few fixes for CMnemonic: - use `SecureVector` for data, bits, seed - `Check` should return bool * init vectors with desired size where possible
2017-05-29 13:51:40 +02:00
pwallet->LoadKeyPool(nIndex, keypool);
}
else if (strType == "version")
{
ssValue >> wss.nFileVersion;
if (wss.nFileVersion == 10300)
wss.nFileVersion = 300;
}
else if (strType == "cscript")
{
uint160 hash;
ssKey >> hash;
CScript script;
2015-10-29 07:11:24 +01:00
ssValue >> *(CScriptBase*)(&script);
if (!pwallet->LoadCScript(script))
{
strErr = "Error reading wallet database: LoadCScript failed";
return false;
}
}
else if (strType == "orderposnext")
{
ssValue >> pwallet->nOrderPosNext;
}
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;
}
}
HD wallet (#1405) * HD wallet Minimal set of changes (no refactoring) backported from Bitcoin upstream to make HD wallets work in Dash 0.12.1.x+ * minimal bip44 (hardcoded account and change) * minimal bip39 Additional cmd-line options for new wallet: -mnemonic -mnemonicpassphrase * Do not recreate HD wallet on encryption Adjusted keypool.py test * Do not store any private keys for hd wallet besides the master one Derive all keys on the fly. Original idea/implementation - btc PR9298, backported and improved * actually use bip39 * pbkdf2 test * backport wallet-hd.py test * Allow specifying hd seed, add dumphdseed rpc, fix bugs - -hdseed cmd-line param to specify HD seed on wallet creation - dumphdseed rpc to dump HD seed - allow seed of any size - fix dumpwallet rpc bug (wasn't decrypting HD seed) - print HD seed and extended public masterkey on dumpwallet * top up keypool on HD wallet encryption * split HD chain: external/internal * add missing cs_wallet lock in init.cpp * fix `const char *` issues (use strings) * default mnemonic passphrase is an empty string in all cases * store mnemonic/mnemonicpassphrase replace dumphdseed with dumphdinfo * Add fCrypted flag to CHDChain * prepare internal structures for multiple HD accounts (plus some code cleanup) * use secure allocator for storing sensitive HD data * use secure strings for mnemonic(passphrase) * small fix in GenerateNewHDChain * use 24 words for mnemonic by default * make sure mnemonic passphrase provided by user does not exceed 256 symbols * more usage of secure allocators and memory_cleanse * code cleanup * rename: CSecureVector -> SecureVector * add missing include * fix warning in rpcdump.cpp * refactor mnemonic_check (also fix a bug) * move bip39 functions to CMnemonic * Few fixes for CMnemonic: - use `SecureVector` for data, bits, seed - `Check` should return bool * init vectors with desired size where possible
2017-05-29 13:51:40 +02:00
else if (strType == "hdchain")
{
CHDChain chain;
ssValue >> chain;
if (!pwallet->SetHDChain(chain, true))
{
strErr = "Error reading wallet database: SetHDChain failed";
return false;
}
}
else if (strType == "chdchain")
{
CHDChain chain;
ssValue >> chain;
if (!pwallet->SetCryptedHDChain(chain, true))
{
strErr = "Error reading wallet database: SetHDCryptedChain failed";
return false;
}
}
else if (strType == "hdpubkey")
{
CPubKey vchPubKey;
ssKey >> vchPubKey;
CHDPubKey hdPubKey;
ssValue >> hdPubKey;
if(vchPubKey != hdPubKey.extPubKey.pubkey)
{
strErr = "Error reading wallet database: CHDPubKey corrupt";
return false;
}
if (!pwallet->LoadHDPubKey(hdPubKey))
{
strErr = "Error reading wallet database: LoadHDPubKey failed";
return false;
}
}
} catch (...)
{
return false;
}
return true;
}
bool CWalletDB::IsKeyType(const std::string& strType)
{
return (strType== "key" || strType == "wkey" ||
HD wallet (#1405) * HD wallet Minimal set of changes (no refactoring) backported from Bitcoin upstream to make HD wallets work in Dash 0.12.1.x+ * minimal bip44 (hardcoded account and change) * minimal bip39 Additional cmd-line options for new wallet: -mnemonic -mnemonicpassphrase * Do not recreate HD wallet on encryption Adjusted keypool.py test * Do not store any private keys for hd wallet besides the master one Derive all keys on the fly. Original idea/implementation - btc PR9298, backported and improved * actually use bip39 * pbkdf2 test * backport wallet-hd.py test * Allow specifying hd seed, add dumphdseed rpc, fix bugs - -hdseed cmd-line param to specify HD seed on wallet creation - dumphdseed rpc to dump HD seed - allow seed of any size - fix dumpwallet rpc bug (wasn't decrypting HD seed) - print HD seed and extended public masterkey on dumpwallet * top up keypool on HD wallet encryption * split HD chain: external/internal * add missing cs_wallet lock in init.cpp * fix `const char *` issues (use strings) * default mnemonic passphrase is an empty string in all cases * store mnemonic/mnemonicpassphrase replace dumphdseed with dumphdinfo * Add fCrypted flag to CHDChain * prepare internal structures for multiple HD accounts (plus some code cleanup) * use secure allocator for storing sensitive HD data * use secure strings for mnemonic(passphrase) * small fix in GenerateNewHDChain * use 24 words for mnemonic by default * make sure mnemonic passphrase provided by user does not exceed 256 symbols * more usage of secure allocators and memory_cleanse * code cleanup * rename: CSecureVector -> SecureVector * add missing include * fix warning in rpcdump.cpp * refactor mnemonic_check (also fix a bug) * move bip39 functions to CMnemonic * Few fixes for CMnemonic: - use `SecureVector` for data, bits, seed - `Check` should return bool * init vectors with desired size where possible
2017-05-29 13:51:40 +02:00
strType == "mkey" || strType == "ckey" ||
strType == "hdchain" || strType == "chdchain");
}
DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
{
2012-05-14 19:07:52 +02:00
pwallet->vchDefaultKey = CPubKey();
CWalletScanState wss;
bool fNoncriticalErrors = false;
DBErrors result = DB_LOAD_OK;
LOCK2(cs_main, pwallet->cs_wallet);
try {
int nMinVersion = 0;
if (Read((std::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:
std::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();
HD wallet (#1405) * HD wallet Minimal set of changes (no refactoring) backported from Bitcoin upstream to make HD wallets work in Dash 0.12.1.x+ * minimal bip44 (hardcoded account and change) * minimal bip39 Additional cmd-line options for new wallet: -mnemonic -mnemonicpassphrase * Do not recreate HD wallet on encryption Adjusted keypool.py test * Do not store any private keys for hd wallet besides the master one Derive all keys on the fly. Original idea/implementation - btc PR9298, backported and improved * actually use bip39 * pbkdf2 test * backport wallet-hd.py test * Allow specifying hd seed, add dumphdseed rpc, fix bugs - -hdseed cmd-line param to specify HD seed on wallet creation - dumphdseed rpc to dump HD seed - allow seed of any size - fix dumpwallet rpc bug (wasn't decrypting HD seed) - print HD seed and extended public masterkey on dumpwallet * top up keypool on HD wallet encryption * split HD chain: external/internal * add missing cs_wallet lock in init.cpp * fix `const char *` issues (use strings) * default mnemonic passphrase is an empty string in all cases * store mnemonic/mnemonicpassphrase replace dumphdseed with dumphdinfo * Add fCrypted flag to CHDChain * prepare internal structures for multiple HD accounts (plus some code cleanup) * use secure allocator for storing sensitive HD data * use secure strings for mnemonic(passphrase) * small fix in GenerateNewHDChain * use 24 words for mnemonic by default * make sure mnemonic passphrase provided by user does not exceed 256 symbols * more usage of secure allocators and memory_cleanse * code cleanup * rename: CSecureVector -> SecureVector * add missing include * fix warning in rpcdump.cpp * refactor mnemonic_check (also fix a bug) * move bip39 functions to CMnemonic * Few fixes for CMnemonic: - use `SecureVector` for data, bits, seed - `Check` should return bool * init vectors with desired size where possible
2017-05-29 13:51:40 +02:00
// Store initial external keypool size since we mostly use external keys in mixing
pwallet->nKeysLeftSinceAutoBackup = pwallet->KeypoolCountExternalKeys();
LogPrintf("nKeysLeftSinceAutoBackup: %d\n", pwallet->nKeysLeftSinceAutoBackup);
}
catch (const boost::thread_interrupted&) {
2013-03-09 18:02:57 +01:00
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.nWatchKeys) != wss.nKeyMeta)
pwallet->UpdateTimeFirstKey(1);
BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade)
WriteTx(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 = pwallet->ReorderTransactions();
pwallet->laccentries.clear();
ListAccountCreditDebit("*", pwallet->laccentries);
BOOST_FOREACH(CAccountingEntry& entry, pwallet->laccentries) {
pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair((CWalletTx*)0, &entry)));
}
return result;
}
2018-12-30 23:07:49 +01:00
DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx)
{
bool fNoncriticalErrors = false;
DBErrors result = DB_LOAD_OK;
try {
int nMinVersion = 0;
if (Read((std::string)"minversion", nMinVersion))
{
if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW;
}
// 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;
}
std::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 (const boost::thread_interrupted&) {
throw;
}
catch (...) {
result = DB_CORRUPT;
}
if (fNoncriticalErrors && result == DB_LOAD_OK)
result = DB_NONCRITICAL_ERROR;
return result;
}
2018-12-30 23:07:49 +01:00
DBErrors CWalletDB::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uint256>& vTxHashOut)
{
// build list of wallet TXs and hashes
2018-12-30 23:07:49 +01:00
std::vector<uint256> vTxHash;
std::vector<CWalletTx> vWtx;
DBErrors err = FindWalletTx(vTxHash, vWtx);
if (err != DB_LOAD_OK) {
return err;
}
std::sort(vTxHash.begin(), vTxHash.end());
std::sort(vTxHashIn.begin(), vTxHashIn.end());
// erase each matching wallet TX
bool delerror = false;
std::vector<uint256>::iterator it = vTxHashIn.begin();
BOOST_FOREACH (uint256 hash, vTxHash) {
while (it < vTxHashIn.end() && (*it) < hash) {
it++;
}
if (it == vTxHashIn.end()) {
break;
}
else if ((*it) == hash) {
if(!EraseTx(hash)) {
Backport Bitcoin#9424, Bitcoin#10123 and Bitcoin#10153 (#2918) * Contains dashification. disables `-debug dash` Merge #9424: Change LogAcceptCategory to use uint32_t rather than sets of strings. 6b3bb3d Change LogAcceptCategory to use uint32_t rather than sets of strings. (Gregory Maxwell) Tree-SHA512: ebb5bcf9a7d00a32dd1390b727ff4d29330a038423611da01268d8e1d2c0229e52a1098e751d4e6db73ef4ae862e1e96d38249883fcaf12b68f55ebb01035b34 Signed-off-by: Pasta <Pasta@dash.org> 31 -> 32 Signed-off-by: Pasta <Pasta@dash.org> * Merge #10123: Allow debug logs to be excluded from specified component 3bde556 Add -debugexclude option to switch off logging for specified components (John Newbery) Tree-SHA512: 30202e3f2085fc2fc5dd4bedb92988f4cb162c612a42cf8f6395a7da326f34975ddc347f82bc4ddca6c84c438dc0cc6e87869f90c7ff88105dbeaa52a947fa43 * bump to uint64_t due to added Dash codes Signed-off-by: Pasta <Pasta@dash.org> * bump to uint64_t due to added Dash codes cont. Signed-off-by: Pasta <Pasta@dash.org> * string -> BCLog format Signed-off-by: Pasta <Pasta@dash.org> * uint32_t -> uint64_t Signed-off-by: Pasta <Pasta@dash.org> * Fix CBatchedLogger * Fix most fDebug-s * Fix `debug` rpc * Fix BENCH and RAND conflicts * Add ALERT and use it * Update LogPrint-s in dash-specific code * Tweak few log categories Specifically: - use PRIVATESEND in `CPrivateSendClientManager::GetRandomNotUsedMasternode()` - use ZMQ in `CZMQPublishRawGovernanceVoteNotifier::NotifyGovernanceVote()` and `CZMQPublishRawGovernanceObjectNotifier::NotifyGovernanceObject()` * Drop no longer used MASTERNODE category * Merge #10153: logging: Fix off-by-one for shrinkdebugfile default faab624 logging: Fix off-by-one for shrinkdebugfile (MarcoFalke) Tree-SHA512: d6153e06067906172ff0611af9e585a3ecf0a7d56925b6ad7c12e75aa802441047059b9b6f6c78e79916c3f2abc8f1998bfd2d5b84201ec6421f727c08da3c21 * Shift dash-specific log categories to start from `1ul << 32` to avoid potential future conflicts with bitcoin ones * Fix `dash` category * remove debugCategories Signed-off-by: Pasta <Pasta@dash.org> * Prepend "std::" to find call * Check for BCLog::PRIVATESEND instead of logCategories != BCLog::NONE * Use BCLog::MNPAYMENTS category instead of checking for logCategories != BCLog::NONE * Move "End Dash" comment below "ALERT" When adding new entries here, we'll otherwise get confused with ordering and might end up forgetting that adding something Dash specific must continue with the bit after 43.
2019-05-22 23:51:39 +02:00
LogPrint(BCLog::DB, "Transaction was found for deletion but returned database error: %s\n", hash.GetHex());
delerror = true;
}
vTxHashOut.push_back(hash);
}
}
if (delerror) {
return DB_CORRUPT;
}
return DB_LOAD_OK;
}
2018-12-30 23:07:49 +01:00
DBErrors CWalletDB::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{
// build list of wallet TXs
2018-12-30 23:07:49 +01:00
std::vector<uint256> vTxHash;
DBErrors err = FindWalletTx(vTxHash, vWtx);
if (err != DB_LOAD_OK)
return err;
// erase each wallet TX
BOOST_FOREACH (uint256& hash, vTxHash) {
if (!EraseTx(hash))
return DB_CORRUPT;
}
return DB_LOAD_OK;
}
void MaybeCompactWalletDB()
{
static std::atomic<bool> fOneThread;
if (fOneThread.exchange(true)) {
return;
}
if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
return;
}
static unsigned int nLastSeen = CWalletDB::GetUpdateCounter();
static unsigned int nLastFlushed = CWalletDB::GetUpdateCounter();
static int64_t nLastWalletUpdate = GetTime();
if (nLastSeen != CWalletDB::GetUpdateCounter())
{
nLastSeen = CWalletDB::GetUpdateCounter();
nLastWalletUpdate = GetTime();
}
if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2)
{
const std::string& strFile = pwalletMain->strWalletFile;
if (CDB::PeriodicFlush(strFile))
nLastFlushed = CWalletDB::GetUpdateCounter();
}
fOneThread = false;
}
//
// Try to (very carefully!) recover wallet file if there is a problem.
//
bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue))
{
return CDB::Recover(filename, callbackDataIn, recoverKVcallback);
}
bool CWalletDB::Recover(const std::string& filename)
{
// recover without a key filter callback
// results in recovering all record types
return CWalletDB::Recover(filename, NULL, NULL);
}
bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
{
CWallet *dummyWallet = reinterpret_cast<CWallet*>(callbackData);
CWalletScanState dummyWss;
std::string strType, strErr;
bool fReadOK;
{
// Required in LoadKeyMetadata():
LOCK(dummyWallet->cs_wallet);
fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue,
dummyWss, strType, strErr);
}
2019-02-16 22:09:51 +01:00
if (!IsKeyType(strType) && strType != "hdpubkey")
return false;
if (!fReadOK)
{
LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr);
return false;
}
return true;
}
bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
{
return CDB::VerifyEnvironment(walletFile, dataDir, errorStr);
}
bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr)
{
return CDB::VerifyDatabaseFile(walletFile, dataDir, errorStr, warningStr, CWalletDB::Recover);
}
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
{
nWalletDBUpdateCounter++;
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)
{
nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
}
HD wallet (#1405) * HD wallet Minimal set of changes (no refactoring) backported from Bitcoin upstream to make HD wallets work in Dash 0.12.1.x+ * minimal bip44 (hardcoded account and change) * minimal bip39 Additional cmd-line options for new wallet: -mnemonic -mnemonicpassphrase * Do not recreate HD wallet on encryption Adjusted keypool.py test * Do not store any private keys for hd wallet besides the master one Derive all keys on the fly. Original idea/implementation - btc PR9298, backported and improved * actually use bip39 * pbkdf2 test * backport wallet-hd.py test * Allow specifying hd seed, add dumphdseed rpc, fix bugs - -hdseed cmd-line param to specify HD seed on wallet creation - dumphdseed rpc to dump HD seed - allow seed of any size - fix dumpwallet rpc bug (wasn't decrypting HD seed) - print HD seed and extended public masterkey on dumpwallet * top up keypool on HD wallet encryption * split HD chain: external/internal * add missing cs_wallet lock in init.cpp * fix `const char *` issues (use strings) * default mnemonic passphrase is an empty string in all cases * store mnemonic/mnemonicpassphrase replace dumphdseed with dumphdinfo * Add fCrypted flag to CHDChain * prepare internal structures for multiple HD accounts (plus some code cleanup) * use secure allocator for storing sensitive HD data * use secure strings for mnemonic(passphrase) * small fix in GenerateNewHDChain * use 24 words for mnemonic by default * make sure mnemonic passphrase provided by user does not exceed 256 symbols * more usage of secure allocators and memory_cleanse * code cleanup * rename: CSecureVector -> SecureVector * add missing include * fix warning in rpcdump.cpp * refactor mnemonic_check (also fix a bug) * move bip39 functions to CMnemonic * Few fixes for CMnemonic: - use `SecureVector` for data, bits, seed - `Check` should return bool * init vectors with desired size where possible
2017-05-29 13:51:40 +02:00
bool CWalletDB::WriteHDChain(const CHDChain& chain)
{
nWalletDBUpdateCounter++;
HD wallet (#1405) * HD wallet Minimal set of changes (no refactoring) backported from Bitcoin upstream to make HD wallets work in Dash 0.12.1.x+ * minimal bip44 (hardcoded account and change) * minimal bip39 Additional cmd-line options for new wallet: -mnemonic -mnemonicpassphrase * Do not recreate HD wallet on encryption Adjusted keypool.py test * Do not store any private keys for hd wallet besides the master one Derive all keys on the fly. Original idea/implementation - btc PR9298, backported and improved * actually use bip39 * pbkdf2 test * backport wallet-hd.py test * Allow specifying hd seed, add dumphdseed rpc, fix bugs - -hdseed cmd-line param to specify HD seed on wallet creation - dumphdseed rpc to dump HD seed - allow seed of any size - fix dumpwallet rpc bug (wasn't decrypting HD seed) - print HD seed and extended public masterkey on dumpwallet * top up keypool on HD wallet encryption * split HD chain: external/internal * add missing cs_wallet lock in init.cpp * fix `const char *` issues (use strings) * default mnemonic passphrase is an empty string in all cases * store mnemonic/mnemonicpassphrase replace dumphdseed with dumphdinfo * Add fCrypted flag to CHDChain * prepare internal structures for multiple HD accounts (plus some code cleanup) * use secure allocator for storing sensitive HD data * use secure strings for mnemonic(passphrase) * small fix in GenerateNewHDChain * use 24 words for mnemonic by default * make sure mnemonic passphrase provided by user does not exceed 256 symbols * more usage of secure allocators and memory_cleanse * code cleanup * rename: CSecureVector -> SecureVector * add missing include * fix warning in rpcdump.cpp * refactor mnemonic_check (also fix a bug) * move bip39 functions to CMnemonic * Few fixes for CMnemonic: - use `SecureVector` for data, bits, seed - `Check` should return bool * init vectors with desired size where possible
2017-05-29 13:51:40 +02:00
return Write(std::string("hdchain"), chain);
}
bool CWalletDB::WriteCryptedHDChain(const CHDChain& chain)
{
nWalletDBUpdateCounter++;
HD wallet (#1405) * HD wallet Minimal set of changes (no refactoring) backported from Bitcoin upstream to make HD wallets work in Dash 0.12.1.x+ * minimal bip44 (hardcoded account and change) * minimal bip39 Additional cmd-line options for new wallet: -mnemonic -mnemonicpassphrase * Do not recreate HD wallet on encryption Adjusted keypool.py test * Do not store any private keys for hd wallet besides the master one Derive all keys on the fly. Original idea/implementation - btc PR9298, backported and improved * actually use bip39 * pbkdf2 test * backport wallet-hd.py test * Allow specifying hd seed, add dumphdseed rpc, fix bugs - -hdseed cmd-line param to specify HD seed on wallet creation - dumphdseed rpc to dump HD seed - allow seed of any size - fix dumpwallet rpc bug (wasn't decrypting HD seed) - print HD seed and extended public masterkey on dumpwallet * top up keypool on HD wallet encryption * split HD chain: external/internal * add missing cs_wallet lock in init.cpp * fix `const char *` issues (use strings) * default mnemonic passphrase is an empty string in all cases * store mnemonic/mnemonicpassphrase replace dumphdseed with dumphdinfo * Add fCrypted flag to CHDChain * prepare internal structures for multiple HD accounts (plus some code cleanup) * use secure allocator for storing sensitive HD data * use secure strings for mnemonic(passphrase) * small fix in GenerateNewHDChain * use 24 words for mnemonic by default * make sure mnemonic passphrase provided by user does not exceed 256 symbols * more usage of secure allocators and memory_cleanse * code cleanup * rename: CSecureVector -> SecureVector * add missing include * fix warning in rpcdump.cpp * refactor mnemonic_check (also fix a bug) * move bip39 functions to CMnemonic * Few fixes for CMnemonic: - use `SecureVector` for data, bits, seed - `Check` should return bool * init vectors with desired size where possible
2017-05-29 13:51:40 +02:00
if (!Write(std::string("chdchain"), chain))
return false;
Erase(std::string("hdchain"));
return true;
}
bool CWalletDB::WriteHDPubKey(const CHDPubKey& hdPubKey, const CKeyMetadata& keyMeta)
{
nWalletDBUpdateCounter++;
HD wallet (#1405) * HD wallet Minimal set of changes (no refactoring) backported from Bitcoin upstream to make HD wallets work in Dash 0.12.1.x+ * minimal bip44 (hardcoded account and change) * minimal bip39 Additional cmd-line options for new wallet: -mnemonic -mnemonicpassphrase * Do not recreate HD wallet on encryption Adjusted keypool.py test * Do not store any private keys for hd wallet besides the master one Derive all keys on the fly. Original idea/implementation - btc PR9298, backported and improved * actually use bip39 * pbkdf2 test * backport wallet-hd.py test * Allow specifying hd seed, add dumphdseed rpc, fix bugs - -hdseed cmd-line param to specify HD seed on wallet creation - dumphdseed rpc to dump HD seed - allow seed of any size - fix dumpwallet rpc bug (wasn't decrypting HD seed) - print HD seed and extended public masterkey on dumpwallet * top up keypool on HD wallet encryption * split HD chain: external/internal * add missing cs_wallet lock in init.cpp * fix `const char *` issues (use strings) * default mnemonic passphrase is an empty string in all cases * store mnemonic/mnemonicpassphrase replace dumphdseed with dumphdinfo * Add fCrypted flag to CHDChain * prepare internal structures for multiple HD accounts (plus some code cleanup) * use secure allocator for storing sensitive HD data * use secure strings for mnemonic(passphrase) * small fix in GenerateNewHDChain * use 24 words for mnemonic by default * make sure mnemonic passphrase provided by user does not exceed 256 symbols * more usage of secure allocators and memory_cleanse * code cleanup * rename: CSecureVector -> SecureVector * add missing include * fix warning in rpcdump.cpp * refactor mnemonic_check (also fix a bug) * move bip39 functions to CMnemonic * Few fixes for CMnemonic: - use `SecureVector` for data, bits, seed - `Check` should return bool * init vectors with desired size where possible
2017-05-29 13:51:40 +02:00
if (!Write(std::make_pair(std::string("keymeta"), hdPubKey.extPubKey.pubkey), keyMeta, false))
return false;
return Write(std::make_pair(std::string("hdpubkey"), hdPubKey.extPubKey.pubkey), hdPubKey, false);
}
void CWalletDB::IncrementUpdateCounter()
{
nWalletDBUpdateCounter++;
}
unsigned int CWalletDB::GetUpdateCounter()
{
return nWalletDBUpdateCounter;
}