Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/walletdb.cpp
Show All 14 Lines | |||||
#include <serialize.h> | #include <serialize.h> | ||||
#include <sync.h> | #include <sync.h> | ||||
#include <util/system.h> | #include <util/system.h> | ||||
#include <util/time.h> | #include <util/time.h> | ||||
#include <wallet/wallet.h> | #include <wallet/wallet.h> | ||||
#include <atomic> | #include <atomic> | ||||
namespace DBKeys { | |||||
const std::string ACENTRY{"acentry"}; | |||||
const std::string BESTBLOCK_NOMERKLE{"bestblock_nomerkle"}; | |||||
const std::string BESTBLOCK{"bestblock"}; | |||||
const std::string CRYPTED_KEY{"ckey"}; | |||||
const std::string CSCRIPT{"cscript"}; | |||||
const std::string DEFAULTKEY{"defaultkey"}; | |||||
const std::string DESTDATA{"destdata"}; | |||||
const std::string FLAGS{"flags"}; | |||||
const std::string HDCHAIN{"hdchain"}; | |||||
const std::string KEYMETA{"keymeta"}; | |||||
const std::string KEY{"key"}; | |||||
const std::string MASTER_KEY{"mkey"}; | |||||
const std::string MINVERSION{"minversion"}; | |||||
const std::string NAME{"name"}; | |||||
const std::string OLD_KEY{"wkey"}; | |||||
const std::string ORDERPOSNEXT{"orderposnext"}; | |||||
const std::string POOL{"pool"}; | |||||
const std::string PURPOSE{"purpose"}; | |||||
const std::string TX{"tx"}; | |||||
const std::string VERSION{"version"}; | |||||
const std::string WATCHMETA{"watchmeta"}; | |||||
const std::string WATCHS{"watchs"}; | |||||
} // namespace DBKeys | |||||
// | // | ||||
// WalletBatch | // WalletBatch | ||||
// | // | ||||
bool WalletBatch::WriteName(const CTxDestination &address, | bool WalletBatch::WriteName(const CTxDestination &address, | ||||
const std::string &strName) { | const std::string &strName) { | ||||
if (!IsValidDestination(address)) { | if (!IsValidDestination(address)) { | ||||
return false; | return false; | ||||
} | } | ||||
return WriteIC(std::make_pair(std::string("name"), | return WriteIC( | ||||
EncodeLegacyAddr(address, Params())), | std::make_pair(DBKeys::NAME, EncodeLegacyAddr(address, Params())), | ||||
strName); | strName); | ||||
} | } | ||||
bool WalletBatch::EraseName(const CTxDestination &address) { | bool WalletBatch::EraseName(const CTxDestination &address) { | ||||
// This should only be used for sending addresses, never for receiving | // This should only be used for sending addresses, never for receiving | ||||
// addresses, receiving addresses must always have an address book entry if | // addresses, receiving addresses must always have an address book entry if | ||||
// they're not change return. | // they're not change return. | ||||
if (!IsValidDestination(address)) { | if (!IsValidDestination(address)) { | ||||
return false; | return false; | ||||
} | } | ||||
return EraseIC(std::make_pair(std::string("name"), | return EraseIC( | ||||
EncodeLegacyAddr(address, Params()))); | std::make_pair(DBKeys::NAME, EncodeLegacyAddr(address, Params()))); | ||||
} | } | ||||
bool WalletBatch::WritePurpose(const CTxDestination &address, | bool WalletBatch::WritePurpose(const CTxDestination &address, | ||||
const std::string &strPurpose) { | const std::string &strPurpose) { | ||||
if (!IsValidDestination(address)) { | if (!IsValidDestination(address)) { | ||||
return false; | return false; | ||||
} | } | ||||
return WriteIC(std::make_pair(std::string("purpose"), | return WriteIC( | ||||
EncodeLegacyAddr(address, Params())), | std::make_pair(DBKeys::PURPOSE, EncodeLegacyAddr(address, Params())), | ||||
strPurpose); | strPurpose); | ||||
} | } | ||||
bool WalletBatch::ErasePurpose(const CTxDestination &address) { | bool WalletBatch::ErasePurpose(const CTxDestination &address) { | ||||
if (!IsValidDestination(address)) { | if (!IsValidDestination(address)) { | ||||
return false; | return false; | ||||
} | } | ||||
return EraseIC(std::make_pair(std::string("purpose"), | return EraseIC( | ||||
EncodeLegacyAddr(address, Params()))); | std::make_pair(DBKeys::PURPOSE, EncodeLegacyAddr(address, Params()))); | ||||
} | } | ||||
bool WalletBatch::WriteTx(const CWalletTx &wtx) { | bool WalletBatch::WriteTx(const CWalletTx &wtx) { | ||||
return WriteIC(std::make_pair(std::string("tx"), wtx.GetId()), wtx); | return WriteIC(std::make_pair(DBKeys::TX, wtx.GetId()), wtx); | ||||
} | } | ||||
bool WalletBatch::EraseTx(uint256 hash) { | bool WalletBatch::EraseTx(uint256 hash) { | ||||
return EraseIC(std::make_pair(std::string("tx"), hash)); | return EraseIC(std::make_pair(DBKeys::TX, hash)); | ||||
} | } | ||||
bool WalletBatch::WriteKeyMetadata(const CKeyMetadata &meta, | bool WalletBatch::WriteKeyMetadata(const CKeyMetadata &meta, | ||||
const CPubKey &pubkey, | const CPubKey &pubkey, | ||||
const bool overwrite) { | const bool overwrite) { | ||||
return WriteIC(std::make_pair(std::string("keymeta"), pubkey), meta, | return WriteIC(std::make_pair(DBKeys::KEYMETA, pubkey), meta, overwrite); | ||||
overwrite); | |||||
} | } | ||||
bool WalletBatch::WriteKey(const CPubKey &vchPubKey, const CPrivKey &vchPrivKey, | bool WalletBatch::WriteKey(const CPubKey &vchPubKey, const CPrivKey &vchPrivKey, | ||||
const CKeyMetadata &keyMeta) { | const CKeyMetadata &keyMeta) { | ||||
if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) { | if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) { | ||||
return false; | return false; | ||||
} | } | ||||
// hash pubkey/privkey to accelerate wallet load | // hash pubkey/privkey to accelerate wallet load | ||||
std::vector<uint8_t> vchKey; | std::vector<uint8_t> vchKey; | ||||
vchKey.reserve(vchPubKey.size() + vchPrivKey.size()); | vchKey.reserve(vchPubKey.size() + vchPrivKey.size()); | ||||
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); | vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); | ||||
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); | vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); | ||||
return WriteIC( | return WriteIC( | ||||
std::make_pair(std::string("key"), vchPubKey), | std::make_pair(DBKeys::KEY, vchPubKey), | ||||
std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); | std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); | ||||
} | } | ||||
bool WalletBatch::WriteCryptedKey(const CPubKey &vchPubKey, | bool WalletBatch::WriteCryptedKey(const CPubKey &vchPubKey, | ||||
const std::vector<uint8_t> &vchCryptedSecret, | const std::vector<uint8_t> &vchCryptedSecret, | ||||
const CKeyMetadata &keyMeta) { | const CKeyMetadata &keyMeta) { | ||||
if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) { | if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) { | ||||
return false; | return false; | ||||
} | } | ||||
if (!WriteIC(std::make_pair(std::string("ckey"), vchPubKey), | if (!WriteIC(std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey), | ||||
vchCryptedSecret, false)) { | vchCryptedSecret, false)) { | ||||
return false; | return false; | ||||
} | } | ||||
EraseIC(std::make_pair(std::string("key"), vchPubKey)); | EraseIC(std::make_pair(DBKeys::KEY, vchPubKey)); | ||||
EraseIC(std::make_pair(std::string("wkey"), vchPubKey)); | EraseIC(std::make_pair(DBKeys::OLD_KEY, vchPubKey)); | ||||
return true; | return true; | ||||
} | } | ||||
bool WalletBatch::WriteMasterKey(unsigned int nID, | bool WalletBatch::WriteMasterKey(unsigned int nID, | ||||
const CMasterKey &kMasterKey) { | const CMasterKey &kMasterKey) { | ||||
return WriteIC(std::make_pair(std::string("mkey"), nID), kMasterKey, true); | return WriteIC(std::make_pair(DBKeys::MASTER_KEY, nID), kMasterKey, true); | ||||
} | } | ||||
bool WalletBatch::WriteCScript(const uint160 &hash, | bool WalletBatch::WriteCScript(const uint160 &hash, | ||||
const CScript &redeemScript) { | const CScript &redeemScript) { | ||||
return WriteIC(std::make_pair(std::string("cscript"), hash), redeemScript, | return WriteIC(std::make_pair(DBKeys::CSCRIPT, hash), redeemScript, false); | ||||
false); | |||||
} | } | ||||
bool WalletBatch::WriteWatchOnly(const CScript &dest, | bool WalletBatch::WriteWatchOnly(const CScript &dest, | ||||
const CKeyMetadata &keyMeta) { | const CKeyMetadata &keyMeta) { | ||||
if (!WriteIC(std::make_pair(std::string("watchmeta"), dest), keyMeta)) { | if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) { | ||||
return false; | return false; | ||||
} | } | ||||
return WriteIC(std::make_pair(std::string("watchs"), dest), '1'); | return WriteIC(std::make_pair(DBKeys::WATCHS, dest), '1'); | ||||
} | } | ||||
bool WalletBatch::EraseWatchOnly(const CScript &dest) { | bool WalletBatch::EraseWatchOnly(const CScript &dest) { | ||||
if (!EraseIC(std::make_pair(std::string("watchmeta"), dest))) { | if (!EraseIC(std::make_pair(DBKeys::WATCHMETA, dest))) { | ||||
return false; | return false; | ||||
} | } | ||||
return EraseIC(std::make_pair(std::string("watchs"), dest)); | return EraseIC(std::make_pair(DBKeys::WATCHS, dest)); | ||||
} | } | ||||
bool WalletBatch::WriteBestBlock(const CBlockLocator &locator) { | bool WalletBatch::WriteBestBlock(const CBlockLocator &locator) { | ||||
// Write empty block locator so versions that require a merkle branch | // Write empty block locator so versions that require a merkle branch | ||||
// automatically rescan | // automatically rescan | ||||
WriteIC(std::string("bestblock"), CBlockLocator()); | WriteIC(DBKeys::BESTBLOCK, CBlockLocator()); | ||||
return WriteIC(std::string("bestblock_nomerkle"), locator); | return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, locator); | ||||
} | } | ||||
bool WalletBatch::ReadBestBlock(CBlockLocator &locator) { | bool WalletBatch::ReadBestBlock(CBlockLocator &locator) { | ||||
if (m_batch.Read(std::string("bestblock"), locator) && | if (m_batch.Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) { | ||||
!locator.vHave.empty()) { | |||||
return true; | return true; | ||||
} | } | ||||
return m_batch.Read(std::string("bestblock_nomerkle"), locator); | return m_batch.Read(DBKeys::BESTBLOCK_NOMERKLE, locator); | ||||
} | } | ||||
bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext) { | bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext) { | ||||
return WriteIC(std::string("orderposnext"), nOrderPosNext); | return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext); | ||||
} | } | ||||
bool WalletBatch::ReadPool(int64_t nPool, CKeyPool &keypool) { | bool WalletBatch::ReadPool(int64_t nPool, CKeyPool &keypool) { | ||||
return m_batch.Read(std::make_pair(std::string("pool"), nPool), keypool); | return m_batch.Read(std::make_pair(DBKeys::POOL, nPool), keypool); | ||||
} | } | ||||
bool WalletBatch::WritePool(int64_t nPool, const CKeyPool &keypool) { | bool WalletBatch::WritePool(int64_t nPool, const CKeyPool &keypool) { | ||||
return WriteIC(std::make_pair(std::string("pool"), nPool), keypool); | return WriteIC(std::make_pair(DBKeys::POOL, nPool), keypool); | ||||
} | } | ||||
bool WalletBatch::ErasePool(int64_t nPool) { | bool WalletBatch::ErasePool(int64_t nPool) { | ||||
return EraseIC(std::make_pair(std::string("pool"), nPool)); | return EraseIC(std::make_pair(DBKeys::POOL, nPool)); | ||||
} | } | ||||
bool WalletBatch::WriteMinVersion(int nVersion) { | bool WalletBatch::WriteMinVersion(int nVersion) { | ||||
return WriteIC(std::string("minversion"), nVersion); | return WriteIC(DBKeys::MINVERSION, nVersion); | ||||
} | } | ||||
class CWalletScanState { | class CWalletScanState { | ||||
public: | public: | ||||
unsigned int nKeys{0}; | unsigned int nKeys{0}; | ||||
unsigned int nCKeys{0}; | unsigned int nCKeys{0}; | ||||
unsigned int nWatchKeys{0}; | unsigned int nWatchKeys{0}; | ||||
unsigned int nKeyMeta{0}; | unsigned int nKeyMeta{0}; | ||||
Show All 9 Lines | static bool ReadKeyValue(CWallet *pwallet, CDataStream &ssKey, | ||||
CDataStream &ssValue, CWalletScanState &wss, | CDataStream &ssValue, CWalletScanState &wss, | ||||
std::string &strType, std::string &strErr) | std::string &strType, std::string &strErr) | ||||
EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { | EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { | ||||
try { | try { | ||||
// Unserialize | // Unserialize | ||||
// Taking advantage of the fact that pair serialization is just the two | // Taking advantage of the fact that pair serialization is just the two | ||||
// items serialized one after the other. | // items serialized one after the other. | ||||
ssKey >> strType; | ssKey >> strType; | ||||
if (strType == "name") { | if (strType == DBKeys::NAME) { | ||||
std::string strAddress; | std::string strAddress; | ||||
ssKey >> strAddress; | ssKey >> strAddress; | ||||
ssValue >> pwallet | ssValue >> pwallet | ||||
->mapAddressBook[DecodeDestination( | ->mapAddressBook[DecodeDestination( | ||||
strAddress, pwallet->chainParams)] | strAddress, pwallet->chainParams)] | ||||
.name; | .name; | ||||
} else if (strType == "purpose") { | } else if (strType == DBKeys::PURPOSE) { | ||||
std::string strAddress; | std::string strAddress; | ||||
ssKey >> strAddress; | ssKey >> strAddress; | ||||
ssValue >> pwallet | ssValue >> pwallet | ||||
->mapAddressBook[DecodeDestination( | ->mapAddressBook[DecodeDestination( | ||||
strAddress, pwallet->chainParams)] | strAddress, pwallet->chainParams)] | ||||
.purpose; | .purpose; | ||||
} else if (strType == "tx") { | } else if (strType == DBKeys::TX) { | ||||
TxId txid; | TxId txid; | ||||
ssKey >> txid; | ssKey >> txid; | ||||
CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); | CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); | ||||
ssValue >> wtx; | ssValue >> wtx; | ||||
TxValidationState state; | TxValidationState state; | ||||
bool isValid = wtx.IsCoinBase() | bool isValid = wtx.IsCoinBase() | ||||
? CheckCoinbase(*wtx.tx, state) | ? CheckCoinbase(*wtx.tx, state) | ||||
: CheckRegularTransaction(*wtx.tx, state); | : CheckRegularTransaction(*wtx.tx, state); | ||||
Show All 22 Lines | try { | ||||
wss.vWalletUpgrade.push_back(txid); | wss.vWalletUpgrade.push_back(txid); | ||||
} | } | ||||
if (wtx.nOrderPos == -1) { | if (wtx.nOrderPos == -1) { | ||||
wss.fAnyUnordered = true; | wss.fAnyUnordered = true; | ||||
} | } | ||||
pwallet->LoadToWallet(wtx); | pwallet->LoadToWallet(wtx); | ||||
} else if (strType == "watchs") { | } else if (strType == DBKeys::WATCHS) { | ||||
wss.nWatchKeys++; | wss.nWatchKeys++; | ||||
CScript script; | CScript script; | ||||
ssKey >> script; | ssKey >> script; | ||||
char fYes; | char fYes; | ||||
ssValue >> fYes; | ssValue >> fYes; | ||||
if (fYes == '1') { | if (fYes == '1') { | ||||
pwallet->LoadWatchOnly(script); | pwallet->LoadWatchOnly(script); | ||||
} | } | ||||
} else if (strType == "key" || strType == "wkey") { | } else if (strType == DBKeys::KEY || strType == DBKeys::OLD_KEY) { | ||||
CPubKey vchPubKey; | CPubKey vchPubKey; | ||||
ssKey >> vchPubKey; | ssKey >> vchPubKey; | ||||
if (!vchPubKey.IsValid()) { | if (!vchPubKey.IsValid()) { | ||||
strErr = "Error reading wallet database: CPubKey corrupt"; | strErr = "Error reading wallet database: CPubKey corrupt"; | ||||
return false; | return false; | ||||
} | } | ||||
CKey key; | CKey key; | ||||
CPrivKey pkey; | CPrivKey pkey; | ||||
uint256 hash; | uint256 hash; | ||||
if (strType == "key") { | if (strType == DBKeys::KEY) { | ||||
wss.nKeys++; | wss.nKeys++; | ||||
ssValue >> pkey; | ssValue >> pkey; | ||||
} else { | } else { | ||||
CWalletKey wkey; | CWalletKey wkey; | ||||
ssValue >> wkey; | ssValue >> wkey; | ||||
pkey = wkey.vchPrivKey; | pkey = wkey.vchPrivKey; | ||||
} | } | ||||
// Old wallets store keys as "key" [pubkey] => [privkey] | // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey] ... | ||||
// ... which was slow for wallets with lots of keys, because the | // which was slow for wallets with lots of keys, because the public | ||||
// public key is re-derived from the private key using EC operations | // key is re-derived from the private key using EC operations as a | ||||
// as a checksum. Newer wallets store keys as "key"[pubkey] => | // checksum. Newer wallets store keys as DBKeys::KEY [pubkey] => | ||||
// [privkey][hash(pubkey,privkey)], which is much faster while | // [privkey][hash(pubkey,privkey)], which is much faster while | ||||
// remaining backwards-compatible. | // remaining backwards-compatible. | ||||
try { | try { | ||||
ssValue >> hash; | ssValue >> hash; | ||||
} catch (...) { | } catch (...) { | ||||
} | } | ||||
bool fSkipCheck = false; | bool fSkipCheck = false; | ||||
Show All 17 Lines | try { | ||||
if (!key.Load(pkey, vchPubKey, fSkipCheck)) { | if (!key.Load(pkey, vchPubKey, fSkipCheck)) { | ||||
strErr = "Error reading wallet database: CPrivKey corrupt"; | strErr = "Error reading wallet database: CPrivKey corrupt"; | ||||
return false; | return false; | ||||
} | } | ||||
if (!pwallet->LoadKey(key, vchPubKey)) { | if (!pwallet->LoadKey(key, vchPubKey)) { | ||||
strErr = "Error reading wallet database: LoadKey failed"; | strErr = "Error reading wallet database: LoadKey failed"; | ||||
return false; | return false; | ||||
} | } | ||||
} else if (strType == "mkey") { | } else if (strType == DBKeys::MASTER_KEY) { | ||||
unsigned int nID; | unsigned int nID; | ||||
ssKey >> nID; | ssKey >> nID; | ||||
CMasterKey kMasterKey; | CMasterKey kMasterKey; | ||||
ssValue >> kMasterKey; | ssValue >> kMasterKey; | ||||
if (pwallet->mapMasterKeys.count(nID) != 0) { | if (pwallet->mapMasterKeys.count(nID) != 0) { | ||||
strErr = strprintf( | strErr = strprintf( | ||||
"Error reading wallet database: duplicate CMasterKey id %u", | "Error reading wallet database: duplicate CMasterKey id %u", | ||||
nID); | nID); | ||||
return false; | return false; | ||||
} | } | ||||
pwallet->mapMasterKeys[nID] = kMasterKey; | pwallet->mapMasterKeys[nID] = kMasterKey; | ||||
if (pwallet->nMasterKeyMaxID < nID) { | if (pwallet->nMasterKeyMaxID < nID) { | ||||
pwallet->nMasterKeyMaxID = nID; | pwallet->nMasterKeyMaxID = nID; | ||||
} | } | ||||
} else if (strType == "ckey") { | } else if (strType == DBKeys::CRYPTED_KEY) { | ||||
CPubKey vchPubKey; | CPubKey vchPubKey; | ||||
ssKey >> vchPubKey; | ssKey >> vchPubKey; | ||||
if (!vchPubKey.IsValid()) { | if (!vchPubKey.IsValid()) { | ||||
strErr = "Error reading wallet database: CPubKey corrupt"; | strErr = "Error reading wallet database: CPubKey corrupt"; | ||||
return false; | return false; | ||||
} | } | ||||
std::vector<uint8_t> vchPrivKey; | std::vector<uint8_t> vchPrivKey; | ||||
ssValue >> vchPrivKey; | ssValue >> vchPrivKey; | ||||
wss.nCKeys++; | wss.nCKeys++; | ||||
if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) { | if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) { | ||||
strErr = "Error reading wallet database: LoadCryptedKey failed"; | strErr = "Error reading wallet database: LoadCryptedKey failed"; | ||||
return false; | return false; | ||||
} | } | ||||
wss.fIsEncrypted = true; | wss.fIsEncrypted = true; | ||||
} else if (strType == "keymeta") { | } else if (strType == DBKeys::KEYMETA) { | ||||
CPubKey vchPubKey; | CPubKey vchPubKey; | ||||
ssKey >> vchPubKey; | ssKey >> vchPubKey; | ||||
CKeyMetadata keyMeta; | CKeyMetadata keyMeta; | ||||
ssValue >> keyMeta; | ssValue >> keyMeta; | ||||
wss.nKeyMeta++; | wss.nKeyMeta++; | ||||
pwallet->LoadKeyMetadata(vchPubKey.GetID(), keyMeta); | pwallet->LoadKeyMetadata(vchPubKey.GetID(), keyMeta); | ||||
} else if (strType == "watchmeta") { | } else if (strType == DBKeys::WATCHMETA) { | ||||
CScript script; | CScript script; | ||||
ssKey >> script; | ssKey >> script; | ||||
CKeyMetadata keyMeta; | CKeyMetadata keyMeta; | ||||
ssValue >> keyMeta; | ssValue >> keyMeta; | ||||
wss.nKeyMeta++; | wss.nKeyMeta++; | ||||
pwallet->LoadScriptMetadata(CScriptID(script), keyMeta); | pwallet->LoadScriptMetadata(CScriptID(script), keyMeta); | ||||
} else if (strType == "defaultkey") { | } else if (strType == DBKeys::DEFAULTKEY) { | ||||
// We don't want or need the default key, but if there is one set, | // 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 | // we want to make sure that it is valid so that we can detect | ||||
// corruption | // corruption | ||||
CPubKey vchPubKey; | CPubKey vchPubKey; | ||||
ssValue >> vchPubKey; | ssValue >> vchPubKey; | ||||
if (!vchPubKey.IsValid()) { | if (!vchPubKey.IsValid()) { | ||||
strErr = "Error reading wallet database: Default Key corrupt"; | strErr = "Error reading wallet database: Default Key corrupt"; | ||||
return false; | return false; | ||||
} | } | ||||
} else if (strType == "pool") { | } else if (strType == DBKeys::POOL) { | ||||
int64_t nIndex; | int64_t nIndex; | ||||
ssKey >> nIndex; | ssKey >> nIndex; | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
ssValue >> keypool; | ssValue >> keypool; | ||||
pwallet->LoadKeyPool(nIndex, keypool); | pwallet->LoadKeyPool(nIndex, keypool); | ||||
} else if (strType == "cscript") { | } else if (strType == DBKeys::CSCRIPT) { | ||||
uint160 hash; | uint160 hash; | ||||
ssKey >> hash; | ssKey >> hash; | ||||
CScript script; | CScript script; | ||||
ssValue >> script; | ssValue >> script; | ||||
if (!pwallet->LoadCScript(script)) { | if (!pwallet->LoadCScript(script)) { | ||||
strErr = "Error reading wallet database: LoadCScript failed"; | strErr = "Error reading wallet database: LoadCScript failed"; | ||||
return false; | return false; | ||||
} | } | ||||
} else if (strType == "orderposnext") { | } else if (strType == DBKeys::ORDERPOSNEXT) { | ||||
ssValue >> pwallet->nOrderPosNext; | ssValue >> pwallet->nOrderPosNext; | ||||
} else if (strType == "destdata") { | } else if (strType == DBKeys::DESTDATA) { | ||||
std::string strAddress, strKey, strValue; | std::string strAddress, strKey, strValue; | ||||
ssKey >> strAddress; | ssKey >> strAddress; | ||||
ssKey >> strKey; | ssKey >> strKey; | ||||
ssValue >> strValue; | ssValue >> strValue; | ||||
pwallet->LoadDestData( | pwallet->LoadDestData( | ||||
DecodeDestination(strAddress, pwallet->chainParams), strKey, | DecodeDestination(strAddress, pwallet->chainParams), strKey, | ||||
strValue); | strValue); | ||||
} else if (strType == "hdchain") { | } else if (strType == DBKeys::HDCHAIN) { | ||||
CHDChain chain; | CHDChain chain; | ||||
ssValue >> chain; | ssValue >> chain; | ||||
pwallet->SetHDChain(chain, true); | pwallet->SetHDChain(chain, true); | ||||
} else if (strType == "flags") { | } else if (strType == DBKeys::FLAGS) { | ||||
uint64_t flags; | uint64_t flags; | ||||
ssValue >> flags; | ssValue >> flags; | ||||
if (!pwallet->SetWalletFlags(flags, true)) { | if (!pwallet->SetWalletFlags(flags, true)) { | ||||
strErr = "Error reading wallet database: Unknown non-tolerable " | strErr = "Error reading wallet database: Unknown non-tolerable " | ||||
"wallet flags found"; | "wallet flags found"; | ||||
return false; | return false; | ||||
} | } | ||||
} else if (strType != "bestblock" && strType != "bestblock_nomerkle" && | } else if (strType != DBKeys::BESTBLOCK && | ||||
strType != "minversion" && strType != "acentry" && | strType != DBKeys::BESTBLOCK_NOMERKLE && | ||||
strType != "version") { | strType != DBKeys::MINVERSION && | ||||
strType != DBKeys::ACENTRY && strType != DBKeys::VERSION) { | |||||
wss.m_unknown_records++; | wss.m_unknown_records++; | ||||
} | } | ||||
} catch (...) { | } catch (...) { | ||||
return false; | return false; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool WalletBatch::IsKeyType(const std::string &strType) { | bool WalletBatch::IsKeyType(const std::string &strType) { | ||||
return (strType == "key" || strType == "wkey" || strType == "mkey" || | return (strType == DBKeys::KEY || strType == DBKeys::OLD_KEY || | ||||
strType == "ckey"); | strType == DBKeys::MASTER_KEY || strType == DBKeys::CRYPTED_KEY); | ||||
} | } | ||||
DBErrors WalletBatch::LoadWallet(CWallet *pwallet) { | DBErrors WalletBatch::LoadWallet(CWallet *pwallet) { | ||||
CWalletScanState wss; | CWalletScanState wss; | ||||
bool fNoncriticalErrors = false; | bool fNoncriticalErrors = false; | ||||
DBErrors result = DBErrors::LOAD_OK; | DBErrors result = DBErrors::LOAD_OK; | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
try { | try { | ||||
int nMinVersion = 0; | int nMinVersion = 0; | ||||
if (m_batch.Read((std::string) "minversion", nMinVersion)) { | if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) { | ||||
if (nMinVersion > FEATURE_LATEST) { | if (nMinVersion > FEATURE_LATEST) { | ||||
return DBErrors::TOO_NEW; | return DBErrors::TOO_NEW; | ||||
} | } | ||||
pwallet->LoadMinVersion(nMinVersion); | pwallet->LoadMinVersion(nMinVersion); | ||||
} | } | ||||
// Get cursor | // Get cursor | ||||
Dbc *pcursor = m_batch.GetCursor(); | Dbc *pcursor = m_batch.GetCursor(); | ||||
Show All 17 Lines | try { | ||||
return DBErrors::CORRUPT; | return DBErrors::CORRUPT; | ||||
} | } | ||||
// Try to be tolerant of single corrupt records: | // Try to be tolerant of single corrupt records: | ||||
std::string strType, strErr; | std::string strType, strErr; | ||||
if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { | if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { | ||||
// losing keys is considered a catastrophic error, anything else | // losing keys is considered a catastrophic error, anything else | ||||
// we assume the user can live with: | // we assume the user can live with: | ||||
if (IsKeyType(strType) || strType == "defaultkey") { | if (IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) { | ||||
result = DBErrors::CORRUPT; | result = DBErrors::CORRUPT; | ||||
} else if (strType == "flags") { | } else if (strType == DBKeys::FLAGS) { | ||||
// Reading the wallet flags can only fail if unknown flags | // Reading the wallet flags can only fail if unknown flags | ||||
// are present. | // are present. | ||||
result = DBErrors::TOO_NEW; | result = DBErrors::TOO_NEW; | ||||
} else { | } else { | ||||
// Leave other errors alone, if we try to fix them we might | // Leave other errors alone, if we try to fix them we might | ||||
// make things worse. But do warn the user there is | // make things worse. But do warn the user there is | ||||
// something wrong. | // something wrong. | ||||
fNoncriticalErrors = true; | fNoncriticalErrors = true; | ||||
if (strType == "tx") { | if (strType == DBKeys::TX) { | ||||
// Rescan if there is a bad transaction record: | // Rescan if there is a bad transaction record: | ||||
gArgs.SoftSetBoolArg("-rescan", true); | gArgs.SoftSetBoolArg("-rescan", true); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!strErr.empty()) { | if (!strErr.empty()) { | ||||
pwallet->WalletLogPrintf("%s\n", strErr); | pwallet->WalletLogPrintf("%s\n", strErr); | ||||
} | } | ||||
Show All 13 Lines | DBErrors WalletBatch::LoadWallet(CWallet *pwallet) { | ||||
// want to make it worse. | // want to make it worse. | ||||
if (result != DBErrors::LOAD_OK) { | if (result != DBErrors::LOAD_OK) { | ||||
return result; | return result; | ||||
} | } | ||||
// Last client version to open this wallet, was previously the file version | // Last client version to open this wallet, was previously the file version | ||||
// number | // number | ||||
int last_client = CLIENT_VERSION; | int last_client = CLIENT_VERSION; | ||||
m_batch.Read(std::string("version"), last_client); | m_batch.Read(DBKeys::VERSION, last_client); | ||||
int wallet_version = pwallet->GetVersion(); | int wallet_version = pwallet->GetVersion(); | ||||
pwallet->WalletLogPrintf("Wallet File Version = %d\n", | pwallet->WalletLogPrintf("Wallet File Version = %d\n", | ||||
wallet_version > 0 ? wallet_version : last_client); | wallet_version > 0 ? wallet_version : last_client); | ||||
pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ " | pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ " | ||||
"metadata, %u total. Unknown wallet records: %u\n", | "metadata, %u total. Unknown wallet records: %u\n", | ||||
wss.nKeys, wss.nCKeys, wss.nKeyMeta, | wss.nKeys, wss.nCKeys, wss.nKeyMeta, | ||||
Show All 10 Lines | DBErrors WalletBatch::LoadWallet(CWallet *pwallet) { | ||||
// Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: | // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: | ||||
if (wss.fIsEncrypted && (last_client == 40000 || last_client == 50000)) { | if (wss.fIsEncrypted && (last_client == 40000 || last_client == 50000)) { | ||||
return DBErrors::NEED_REWRITE; | return DBErrors::NEED_REWRITE; | ||||
} | } | ||||
if (last_client < CLIENT_VERSION) { | if (last_client < CLIENT_VERSION) { | ||||
// Update | // Update | ||||
m_batch.Write(std::string("version"), CLIENT_VERSION); | m_batch.Write(DBKeys::VERSION, CLIENT_VERSION); | ||||
} | } | ||||
if (wss.fAnyUnordered) { | if (wss.fAnyUnordered) { | ||||
result = pwallet->ReorderTransactions(); | result = pwallet->ReorderTransactions(); | ||||
} | } | ||||
// Upgrade all of the wallet keymetadata to have the hd master key id | // Upgrade all of the wallet keymetadata to have the hd master key id | ||||
// This operation is not atomic, but if it fails, updated entries are still | // This operation is not atomic, but if it fails, updated entries are still | ||||
// backwards compatible with older software | // backwards compatible with older software | ||||
try { | try { | ||||
pwallet->UpgradeKeyMetadata(); | pwallet->UpgradeKeyMetadata(); | ||||
} catch (...) { | } catch (...) { | ||||
result = DBErrors::CORRUPT; | result = DBErrors::CORRUPT; | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
DBErrors WalletBatch::FindWalletTx(std::vector<TxId> &txIds, | DBErrors WalletBatch::FindWalletTx(std::vector<TxId> &txIds, | ||||
std::vector<CWalletTx> &vWtx) { | std::vector<CWalletTx> &vWtx) { | ||||
DBErrors result = DBErrors::LOAD_OK; | DBErrors result = DBErrors::LOAD_OK; | ||||
try { | try { | ||||
int nMinVersion = 0; | int nMinVersion = 0; | ||||
if (m_batch.Read((std::string) "minversion", nMinVersion)) { | if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) { | ||||
if (nMinVersion > FEATURE_LATEST) { | if (nMinVersion > FEATURE_LATEST) { | ||||
return DBErrors::TOO_NEW; | return DBErrors::TOO_NEW; | ||||
} | } | ||||
} | } | ||||
// Get cursor | // Get cursor | ||||
Dbc *pcursor = m_batch.GetCursor(); | Dbc *pcursor = m_batch.GetCursor(); | ||||
if (!pcursor) { | if (!pcursor) { | ||||
Show All 12 Lines | try { | ||||
if (ret != 0) { | if (ret != 0) { | ||||
LogPrintf("Error reading next record from wallet database\n"); | LogPrintf("Error reading next record from wallet database\n"); | ||||
return DBErrors::CORRUPT; | return DBErrors::CORRUPT; | ||||
} | } | ||||
std::string strType; | std::string strType; | ||||
ssKey >> strType; | ssKey >> strType; | ||||
if (strType == "tx") { | if (strType == DBKeys::TX) { | ||||
TxId txid; | TxId txid; | ||||
ssKey >> txid; | ssKey >> txid; | ||||
CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); | CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); | ||||
ssValue >> wtx; | ssValue >> wtx; | ||||
txIds.push_back(txid); | txIds.push_back(txid); | ||||
vWtx.push_back(wtx); | vWtx.push_back(wtx); | ||||
▲ Show 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, | ||||
std::string strType, strErr; | std::string strType, strErr; | ||||
bool fReadOK; | bool fReadOK; | ||||
{ | { | ||||
// Required in LoadKeyMetadata(): | // Required in LoadKeyMetadata(): | ||||
LOCK(dummyWallet->cs_wallet); | LOCK(dummyWallet->cs_wallet); | ||||
fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue, dummyWss, strType, | fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue, dummyWss, strType, | ||||
strErr); | strErr); | ||||
} | } | ||||
if (!IsKeyType(strType) && strType != "hdchain") { | if (!IsKeyType(strType) && strType != DBKeys::HDCHAIN) { | ||||
return false; | return false; | ||||
} | } | ||||
if (!fReadOK) { | if (!fReadOK) { | ||||
LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, | LogPrintf("WARNING: WalletBatch::Recover skipping %s: %s\n", strType, | ||||
strErr); | strErr); | ||||
return false; | return false; | ||||
} | } | ||||
Show All 15 Lines | |||||
bool WalletBatch::WriteDestData(const CTxDestination &address, | bool WalletBatch::WriteDestData(const CTxDestination &address, | ||||
const std::string &key, | const std::string &key, | ||||
const std::string &value) { | const std::string &value) { | ||||
if (!IsValidDestination(address)) { | if (!IsValidDestination(address)) { | ||||
return false; | return false; | ||||
} | } | ||||
return WriteIC( | return WriteIC( | ||||
std::make_pair( | std::make_pair( | ||||
std::string("destdata"), | DBKeys::DESTDATA, | ||||
std::make_pair(EncodeLegacyAddr(address, Params()), key)), | std::make_pair(EncodeLegacyAddr(address, Params()), key)), | ||||
value); | value); | ||||
} | } | ||||
bool WalletBatch::EraseDestData(const CTxDestination &address, | bool WalletBatch::EraseDestData(const CTxDestination &address, | ||||
const std::string &key) { | const std::string &key) { | ||||
if (!IsValidDestination(address)) { | if (!IsValidDestination(address)) { | ||||
return false; | return false; | ||||
} | } | ||||
return EraseIC(std::make_pair( | return EraseIC(std::make_pair( | ||||
std::string("destdata"), | DBKeys::DESTDATA, | ||||
std::make_pair(EncodeLegacyAddr(address, Params()), key))); | std::make_pair(EncodeLegacyAddr(address, Params()), key))); | ||||
} | } | ||||
bool WalletBatch::WriteHDChain(const CHDChain &chain) { | bool WalletBatch::WriteHDChain(const CHDChain &chain) { | ||||
return WriteIC(std::string("hdchain"), chain); | return WriteIC(DBKeys::HDCHAIN, chain); | ||||
} | } | ||||
bool WalletBatch::WriteWalletFlags(const uint64_t flags) { | bool WalletBatch::WriteWalletFlags(const uint64_t flags) { | ||||
return WriteIC(std::string("flags"), flags); | return WriteIC(DBKeys::FLAGS, flags); | ||||
} | } | ||||
bool WalletBatch::TxnBegin() { | bool WalletBatch::TxnBegin() { | ||||
return m_batch.TxnBegin(); | return m_batch.TxnBegin(); | ||||
} | } | ||||
bool WalletBatch::TxnCommit() { | bool WalletBatch::TxnCommit() { | ||||
return m_batch.TxnCommit(); | return m_batch.TxnCommit(); | ||||
} | } | ||||
bool WalletBatch::TxnAbort() { | bool WalletBatch::TxnAbort() { | ||||
return m_batch.TxnAbort(); | return m_batch.TxnAbort(); | ||||
} | } |