Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 150 Lines • ▼ Show 20 Lines | const CWalletTx *CWallet::GetWalletTx(const TxId &txid) const { | ||||
std::map<TxId, CWalletTx>::const_iterator it = mapWallet.find(txid); | std::map<TxId, CWalletTx>::const_iterator it = mapWallet.find(txid); | ||||
if (it == mapWallet.end()) { | if (it == mapWallet.end()) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return &(it->second); | return &(it->second); | ||||
} | } | ||||
CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal) { | CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal) { | ||||
// mapKeyMetadata | // mapKeyMetadata | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
// default to compressed public keys if we want 0.6.0 wallets | // default to compressed public keys if we want 0.6.0 wallets | ||||
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); | bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); | ||||
CKey secret; | CKey secret; | ||||
// Create new metadata | // Create new metadata | ||||
int64_t nCreationTime = GetTime(); | int64_t nCreationTime = GetTime(); | ||||
CKeyMetadata metadata(nCreationTime); | CKeyMetadata metadata(nCreationTime); | ||||
// use HD key derivation if HD was enabled during wallet creation | // use HD key derivation if HD was enabled during wallet creation | ||||
if (IsHDEnabled()) { | if (IsHDEnabled()) { | ||||
DeriveNewChildKey( | DeriveNewChildKey( | ||||
walletdb, metadata, secret, | batch, metadata, secret, | ||||
(CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); | (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); | ||||
} else { | } else { | ||||
secret.MakeNewKey(fCompressed); | secret.MakeNewKey(fCompressed); | ||||
} | } | ||||
// Compressed public keys were introduced in version 0.6.0 | // Compressed public keys were introduced in version 0.6.0 | ||||
if (fCompressed) { | if (fCompressed) { | ||||
SetMinVersion(FEATURE_COMPRPUBKEY); | SetMinVersion(FEATURE_COMPRPUBKEY); | ||||
} | } | ||||
CPubKey pubkey = secret.GetPubKey(); | CPubKey pubkey = secret.GetPubKey(); | ||||
assert(secret.VerifyPubKey(pubkey)); | assert(secret.VerifyPubKey(pubkey)); | ||||
mapKeyMetadata[pubkey.GetID()] = metadata; | mapKeyMetadata[pubkey.GetID()] = metadata; | ||||
UpdateTimeFirstKey(nCreationTime); | UpdateTimeFirstKey(nCreationTime); | ||||
if (!AddKeyPubKeyWithDB(walletdb, secret, pubkey)) { | if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) { | ||||
throw std::runtime_error(std::string(__func__) + ": AddKey failed"); | throw std::runtime_error(std::string(__func__) + ": AddKey failed"); | ||||
} | } | ||||
return pubkey; | return pubkey; | ||||
} | } | ||||
void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata &metadata, | void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata &metadata, | ||||
CKey &secret, bool internal) { | CKey &secret, bool internal) { | ||||
// for now we use a fixed keypath scheme of m/0'/0'/k | // for now we use a fixed keypath scheme of m/0'/0'/k | ||||
// master key seed (256bit) | // master key seed (256bit) | ||||
CKey key; | CKey key; | ||||
// hd master key | // hd master key | ||||
CExtKey masterKey; | CExtKey masterKey; | ||||
// key at m/0' | // key at m/0' | ||||
CExtKey accountKey; | CExtKey accountKey; | ||||
Show All 40 Lines | do { | ||||
std::to_string(hdChain.nExternalChainCounter) + | std::to_string(hdChain.nExternalChainCounter) + | ||||
"'"; | "'"; | ||||
hdChain.nExternalChainCounter++; | hdChain.nExternalChainCounter++; | ||||
} | } | ||||
} while (HaveKey(childKey.key.GetPubKey().GetID())); | } while (HaveKey(childKey.key.GetPubKey().GetID())); | ||||
secret = childKey.key; | secret = childKey.key; | ||||
metadata.hdMasterKeyID = hdChain.masterKeyID; | metadata.hdMasterKeyID = hdChain.masterKeyID; | ||||
// update the chain model in the database | // update the chain model in the database | ||||
if (!walletdb.WriteHDChain(hdChain)) { | if (!batch.WriteHDChain(hdChain)) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": Writing HD chain model failed"); | ": Writing HD chain model failed"); | ||||
} | } | ||||
} | } | ||||
bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey &secret, | bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey &secret, | ||||
const CPubKey &pubkey) { | const CPubKey &pubkey) { | ||||
// mapKeyMetadata | // mapKeyMetadata | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
// CCryptoKeyStore has no concept of wallet databases, but calls | // CCryptoKeyStore has no concept of wallet databases, but calls | ||||
// AddCryptedKey | // AddCryptedKey | ||||
// which is overridden below. To avoid flushes, the database handle is | // which is overridden below. To avoid flushes, the database handle is | ||||
// tunneled through to it. | // tunneled through to it. | ||||
bool needsDB = !pwalletdbEncryption; | bool needsDB = !encrypted_batch; | ||||
if (needsDB) { | if (needsDB) { | ||||
pwalletdbEncryption = &walletdb; | encrypted_batch = &batch; | ||||
} | } | ||||
if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) { | if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) { | ||||
if (needsDB) { | if (needsDB) { | ||||
pwalletdbEncryption = nullptr; | encrypted_batch = nullptr; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
if (needsDB) { | if (needsDB) { | ||||
pwalletdbEncryption = nullptr; | encrypted_batch = nullptr; | ||||
} | } | ||||
// Check if we need to remove from watch-only. | // Check if we need to remove from watch-only. | ||||
CScript script; | CScript script; | ||||
script = GetScriptForDestination(pubkey.GetID()); | script = GetScriptForDestination(pubkey.GetID()); | ||||
if (HaveWatchOnly(script)) { | if (HaveWatchOnly(script)) { | ||||
RemoveWatchOnly(script); | RemoveWatchOnly(script); | ||||
} | } | ||||
script = GetScriptForRawPubKey(pubkey); | script = GetScriptForRawPubKey(pubkey); | ||||
if (HaveWatchOnly(script)) { | if (HaveWatchOnly(script)) { | ||||
RemoveWatchOnly(script); | RemoveWatchOnly(script); | ||||
} | } | ||||
if (IsCrypted()) { | if (IsCrypted()) { | ||||
return true; | return true; | ||||
} | } | ||||
return walletdb.WriteKey(pubkey, secret.GetPrivKey(), | return batch.WriteKey(pubkey, secret.GetPrivKey(), | ||||
mapKeyMetadata[pubkey.GetID()]); | mapKeyMetadata[pubkey.GetID()]); | ||||
} | } | ||||
bool CWallet::AddKeyPubKey(const CKey &secret, const CPubKey &pubkey) { | bool CWallet::AddKeyPubKey(const CKey &secret, const CPubKey &pubkey) { | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
return CWallet::AddKeyPubKeyWithDB(walletdb, secret, pubkey); | return CWallet::AddKeyPubKeyWithDB(batch, secret, pubkey); | ||||
} | } | ||||
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, | bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, | ||||
const std::vector<uint8_t> &vchCryptedSecret) { | const std::vector<uint8_t> &vchCryptedSecret) { | ||||
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) { | if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) { | ||||
return false; | return false; | ||||
} | } | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (pwalletdbEncryption) { | if (encrypted_batch) { | ||||
return pwalletdbEncryption->WriteCryptedKey( | return encrypted_batch->WriteCryptedKey( | ||||
vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); | vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); | ||||
} | } | ||||
return CWalletDB(*dbw).WriteCryptedKey(vchPubKey, vchCryptedSecret, | return WalletBatch(*database).WriteCryptedKey( | ||||
mapKeyMetadata[vchPubKey.GetID()]); | vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); | ||||
} | } | ||||
bool CWallet::LoadKeyMetadata(const CKeyID &keyID, const CKeyMetadata &meta) { | bool CWallet::LoadKeyMetadata(const CKeyID &keyID, const CKeyMetadata &meta) { | ||||
// mapKeyMetadata | // mapKeyMetadata | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
UpdateTimeFirstKey(meta.nCreateTime); | UpdateTimeFirstKey(meta.nCreateTime); | ||||
mapKeyMetadata[keyID] = meta; | mapKeyMetadata[keyID] = meta; | ||||
return true; | return true; | ||||
Show All 28 Lines | void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) { | ||||
} | } | ||||
} | } | ||||
bool CWallet::AddCScript(const CScript &redeemScript) { | bool CWallet::AddCScript(const CScript &redeemScript) { | ||||
if (!CCryptoKeyStore::AddCScript(redeemScript)) { | if (!CCryptoKeyStore::AddCScript(redeemScript)) { | ||||
return false; | return false; | ||||
} | } | ||||
return CWalletDB(*dbw).WriteCScript(Hash160(redeemScript), redeemScript); | return WalletBatch(*database).WriteCScript(Hash160(redeemScript), | ||||
redeemScript); | |||||
} | } | ||||
bool CWallet::LoadCScript(const CScript &redeemScript) { | bool CWallet::LoadCScript(const CScript &redeemScript) { | ||||
/** | /** | ||||
* A sanity check was added in pull #3843 to avoid adding redeemScripts that | * A sanity check was added in pull #3843 to avoid adding redeemScripts that | ||||
* never can be redeemed. However, old wallets may still contain these. Do | * never can be redeemed. However, old wallets may still contain these. Do | ||||
* not add them to the wallet and warn. | * not add them to the wallet and warn. | ||||
*/ | */ | ||||
Show All 14 Lines | |||||
bool CWallet::AddWatchOnly(const CScript &dest) { | bool CWallet::AddWatchOnly(const CScript &dest) { | ||||
if (!CCryptoKeyStore::AddWatchOnly(dest)) { | if (!CCryptoKeyStore::AddWatchOnly(dest)) { | ||||
return false; | return false; | ||||
} | } | ||||
const CKeyMetadata &meta = m_script_metadata[CScriptID(dest)]; | const CKeyMetadata &meta = m_script_metadata[CScriptID(dest)]; | ||||
UpdateTimeFirstKey(meta.nCreateTime); | UpdateTimeFirstKey(meta.nCreateTime); | ||||
NotifyWatchonlyChanged(true); | NotifyWatchonlyChanged(true); | ||||
return CWalletDB(*dbw).WriteWatchOnly(dest, meta); | return WalletBatch(*database).WriteWatchOnly(dest, meta); | ||||
} | } | ||||
bool CWallet::AddWatchOnly(const CScript &dest, int64_t nCreateTime) { | bool CWallet::AddWatchOnly(const CScript &dest, int64_t nCreateTime) { | ||||
m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime; | m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime; | ||||
return AddWatchOnly(dest); | return AddWatchOnly(dest); | ||||
} | } | ||||
bool CWallet::RemoveWatchOnly(const CScript &dest) { | bool CWallet::RemoveWatchOnly(const CScript &dest) { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
if (!CCryptoKeyStore::RemoveWatchOnly(dest)) { | if (!CCryptoKeyStore::RemoveWatchOnly(dest)) { | ||||
return false; | return false; | ||||
} | } | ||||
if (!HaveWatchOnly()) { | if (!HaveWatchOnly()) { | ||||
NotifyWatchonlyChanged(false); | NotifyWatchonlyChanged(false); | ||||
} | } | ||||
return CWalletDB(*dbw).EraseWatchOnly(dest); | return WalletBatch(*database).EraseWatchOnly(dest); | ||||
} | } | ||||
bool CWallet::LoadWatchOnly(const CScript &dest) { | bool CWallet::LoadWatchOnly(const CScript &dest) { | ||||
return CCryptoKeyStore::AddWatchOnly(dest); | return CCryptoKeyStore::AddWatchOnly(dest); | ||||
} | } | ||||
bool CWallet::Unlock(const SecureString &strWalletPassphrase) { | bool CWallet::Unlock(const SecureString &strWalletPassphrase) { | ||||
CCrypter crypter; | CCrypter crypter; | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | for (MasterKeyMap::value_type &pMasterKey : mapMasterKeys) { | ||||
return false; | return false; | ||||
} | } | ||||
if (!crypter.Encrypt(_vMasterKey, | if (!crypter.Encrypt(_vMasterKey, | ||||
pMasterKey.second.vchCryptedKey)) { | pMasterKey.second.vchCryptedKey)) { | ||||
return false; | return false; | ||||
} | } | ||||
CWalletDB(*dbw).WriteMasterKey(pMasterKey.first, pMasterKey.second); | WalletBatch(*database).WriteMasterKey(pMasterKey.first, | ||||
pMasterKey.second); | |||||
if (fWasLocked) { | if (fWasLocked) { | ||||
Lock(); | Lock(); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
void CWallet::ChainStateFlushed(const CBlockLocator &loc) { | void CWallet::ChainStateFlushed(const CBlockLocator &loc) { | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
walletdb.WriteBestBlock(loc); | batch.WriteBestBlock(loc); | ||||
} | } | ||||
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB *pwalletdbIn, | bool CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch *batch_in, | ||||
bool fExplicit) { | bool fExplicit) { | ||||
// nWalletVersion | // nWalletVersion | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (nWalletVersion >= nVersion) { | if (nWalletVersion >= nVersion) { | ||||
return true; | return true; | ||||
} | } | ||||
// When doing an explicit upgrade, if we pass the max version permitted, | // When doing an explicit upgrade, if we pass the max version permitted, | ||||
// upgrade all the way. | // upgrade all the way. | ||||
if (fExplicit && nVersion > nWalletMaxVersion) { | if (fExplicit && nVersion > nWalletMaxVersion) { | ||||
nVersion = FEATURE_LATEST; | nVersion = FEATURE_LATEST; | ||||
} | } | ||||
nWalletVersion = nVersion; | nWalletVersion = nVersion; | ||||
if (nVersion > nWalletMaxVersion) { | if (nVersion > nWalletMaxVersion) { | ||||
nWalletMaxVersion = nVersion; | nWalletMaxVersion = nVersion; | ||||
} | } | ||||
CWalletDB *pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(*dbw); | WalletBatch *batch = batch_in ? batch_in : new WalletBatch(*database); | ||||
if (nWalletVersion > 40000) { | if (nWalletVersion > 40000) { | ||||
pwalletdb->WriteMinVersion(nWalletVersion); | batch->WriteMinVersion(nWalletVersion); | ||||
} | } | ||||
if (!batch_in) { | |||||
if (!pwalletdbIn) { | delete batch; | ||||
delete pwalletdb; | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CWallet::SetMaxVersion(int nVersion) { | bool CWallet::SetMaxVersion(int nVersion) { | ||||
// nWalletVersion, nWalletMaxVersion | // nWalletVersion, nWalletMaxVersion | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
Show All 39 Lines | |||||
bool CWallet::HasWalletSpend(const TxId &txid) const { | bool CWallet::HasWalletSpend(const TxId &txid) const { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); | auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); | ||||
return (iter != mapTxSpends.end() && iter->first.GetTxId() == txid); | return (iter != mapTxSpends.end() && iter->first.GetTxId() == txid); | ||||
} | } | ||||
void CWallet::Flush(bool shutdown) { | void CWallet::Flush(bool shutdown) { | ||||
dbw->Flush(shutdown); | database->Flush(shutdown); | ||||
} | } | ||||
void CWallet::SyncMetaData( | void CWallet::SyncMetaData( | ||||
std::pair<TxSpends::iterator, TxSpends::iterator> range) { | std::pair<TxSpends::iterator, TxSpends::iterator> range) { | ||||
// We want all the wallet transactions in range to have the same metadata as | // We want all the wallet transactions in range to have the same metadata as | ||||
// the oldest (smallest nOrderPos). | // the oldest (smallest nOrderPos). | ||||
// So: find smallest nOrderPos: | // So: find smallest nOrderPos: | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | bool CWallet::EncryptWallet(const SecureString &strWalletPassphrase) { | ||||
if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey)) { | if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey)) { | ||||
return false; | return false; | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; | mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; | ||||
assert(!pwalletdbEncryption); | assert(!encrypted_batch); | ||||
pwalletdbEncryption = new CWalletDB(*dbw); | encrypted_batch = new WalletBatch(*database); | ||||
if (!pwalletdbEncryption->TxnBegin()) { | if (!encrypted_batch->TxnBegin()) { | ||||
delete pwalletdbEncryption; | delete encrypted_batch; | ||||
pwalletdbEncryption = nullptr; | encrypted_batch = nullptr; | ||||
return false; | return false; | ||||
} | } | ||||
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); | encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey); | ||||
if (!EncryptKeys(_vMasterKey)) { | if (!EncryptKeys(_vMasterKey)) { | ||||
pwalletdbEncryption->TxnAbort(); | encrypted_batch->TxnAbort(); | ||||
delete pwalletdbEncryption; | delete encrypted_batch; | ||||
// We now probably have half of our keys encrypted in memory, and | // We now probably have half of our keys encrypted in memory, and | ||||
// half not... die and let the user reload the unencrypted wallet. | // half not... die and let the user reload the unencrypted wallet. | ||||
assert(false); | assert(false); | ||||
} | } | ||||
// Encryption was introduced in version 0.4.0 | // Encryption was introduced in version 0.4.0 | ||||
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); | SetMinVersion(FEATURE_WALLETCRYPT, encrypted_batch, true); | ||||
if (!pwalletdbEncryption->TxnCommit()) { | if (!encrypted_batch->TxnCommit()) { | ||||
delete pwalletdbEncryption; | delete encrypted_batch; | ||||
// We now have keys encrypted in memory, but not on disk... die to | // We now have keys encrypted in memory, but not on disk... | ||||
// avoid confusion and let the user reload the unencrypted wallet. | // die to avoid confusion and let the user reload the unencrypted | ||||
// wallet. | |||||
assert(false); | assert(false); | ||||
} | } | ||||
delete pwalletdbEncryption; | delete encrypted_batch; | ||||
pwalletdbEncryption = nullptr; | encrypted_batch = nullptr; | ||||
Lock(); | Lock(); | ||||
Unlock(strWalletPassphrase); | Unlock(strWalletPassphrase); | ||||
// If we are using HD, replace the HD master key (seed) with a new one. | // If we are using HD, replace the HD master key (seed) with a new one. | ||||
if (IsHDEnabled()) { | if (IsHDEnabled()) { | ||||
if (!SetHDMasterKey(GenerateNewHDMasterKey())) { | if (!SetHDMasterKey(GenerateNewHDMasterKey())) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
NewKeyPool(); | NewKeyPool(); | ||||
Lock(); | Lock(); | ||||
// Need to completely rewrite the wallet file; if we don't, bdb might | // Need to completely rewrite the wallet file; if we don't, bdb might | ||||
// keep bits of the unencrypted private key in slack space in the | // keep bits of the unencrypted private key in slack space in the | ||||
// database file. | // database file. | ||||
dbw->Rewrite(); | database->Rewrite(); | ||||
} | } | ||||
NotifyStatusChanged(this); | NotifyStatusChanged(this); | ||||
return true; | return true; | ||||
} | } | ||||
DBErrors CWallet::ReorderTransactions() { | DBErrors CWallet::ReorderTransactions() { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
// Old wallets didn't have any defined order for transactions. Probably a | // Old wallets didn't have any defined order for transactions. Probably a | ||||
// bad idea to change the output of this. | // bad idea to change the output of this. | ||||
// First: get all CWalletTx and CAccountingEntry into a sorted-by-time | // First: get all CWalletTx and CAccountingEntry into a sorted-by-time | ||||
// multimap. | // multimap. | ||||
TxItems txByTime; | TxItems txByTime; | ||||
for (auto &entry : mapWallet) { | for (auto &entry : mapWallet) { | ||||
CWalletTx *wtx = &entry.second; | CWalletTx *wtx = &entry.second; | ||||
txByTime.insert( | txByTime.insert( | ||||
std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); | std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); | ||||
} | } | ||||
std::list<CAccountingEntry> acentries; | std::list<CAccountingEntry> acentries; | ||||
walletdb.ListAccountCreditDebit("", acentries); | batch.ListAccountCreditDebit("", acentries); | ||||
for (CAccountingEntry &entry : acentries) { | for (CAccountingEntry &entry : acentries) { | ||||
txByTime.insert(std::make_pair(entry.nTime, TxPair(nullptr, &entry))); | txByTime.insert(std::make_pair(entry.nTime, TxPair(nullptr, &entry))); | ||||
} | } | ||||
nOrderPosNext = 0; | nOrderPosNext = 0; | ||||
std::vector<int64_t> nOrderPosOffsets; | std::vector<int64_t> nOrderPosOffsets; | ||||
for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) { | for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) { | ||||
CWalletTx *const pwtx = (*it).second.first; | CWalletTx *const pwtx = (*it).second.first; | ||||
CAccountingEntry *const pacentry = (*it).second.second; | CAccountingEntry *const pacentry = (*it).second.second; | ||||
int64_t &nOrderPos = | int64_t &nOrderPos = | ||||
(pwtx != nullptr) ? pwtx->nOrderPos : pacentry->nOrderPos; | (pwtx != nullptr) ? pwtx->nOrderPos : pacentry->nOrderPos; | ||||
if (nOrderPos == -1) { | if (nOrderPos == -1) { | ||||
nOrderPos = nOrderPosNext++; | nOrderPos = nOrderPosNext++; | ||||
nOrderPosOffsets.push_back(nOrderPos); | nOrderPosOffsets.push_back(nOrderPos); | ||||
if (pwtx) { | if (pwtx) { | ||||
if (!walletdb.WriteTx(*pwtx)) { | if (!batch.WriteTx(*pwtx)) { | ||||
return DBErrors::LOAD_FAIL; | return DBErrors::LOAD_FAIL; | ||||
} | } | ||||
} else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, | } else if (!batch.WriteAccountingEntry(pacentry->nEntryNo, | ||||
*pacentry)) { | *pacentry)) { | ||||
return DBErrors::LOAD_FAIL; | return DBErrors::LOAD_FAIL; | ||||
} | } | ||||
} else { | } else { | ||||
int64_t nOrderPosOff = 0; | int64_t nOrderPosOff = 0; | ||||
for (const int64_t &nOffsetStart : nOrderPosOffsets) { | for (const int64_t &nOffsetStart : nOrderPosOffsets) { | ||||
if (nOrderPos >= nOffsetStart) { | if (nOrderPos >= nOffsetStart) { | ||||
++nOrderPosOff; | ++nOrderPosOff; | ||||
} | } | ||||
} | } | ||||
nOrderPos += nOrderPosOff; | nOrderPos += nOrderPosOff; | ||||
nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); | nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); | ||||
if (!nOrderPosOff) { | if (!nOrderPosOff) { | ||||
continue; | continue; | ||||
} | } | ||||
// Since we're changing the order, write it back. | // Since we're changing the order, write it back. | ||||
if (pwtx) { | if (pwtx) { | ||||
if (!walletdb.WriteTx(*pwtx)) { | if (!batch.WriteTx(*pwtx)) { | ||||
return DBErrors::LOAD_FAIL; | return DBErrors::LOAD_FAIL; | ||||
} | } | ||||
} else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, | } else if (!batch.WriteAccountingEntry(pacentry->nEntryNo, | ||||
*pacentry)) { | *pacentry)) { | ||||
return DBErrors::LOAD_FAIL; | return DBErrors::LOAD_FAIL; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
walletdb.WriteOrderPosNext(nOrderPosNext); | batch.WriteOrderPosNext(nOrderPosNext); | ||||
return DBErrors::LOAD_OK; | return DBErrors::LOAD_OK; | ||||
} | } | ||||
int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { | int64_t CWallet::IncOrderPosNext(WalletBatch *batch) { | ||||
// nOrderPosNext | // nOrderPosNext | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
int64_t nRet = nOrderPosNext++; | int64_t nRet = nOrderPosNext++; | ||||
if (pwalletdb) { | if (batch) { | ||||
pwalletdb->WriteOrderPosNext(nOrderPosNext); | batch->WriteOrderPosNext(nOrderPosNext); | ||||
} else { | } else { | ||||
CWalletDB(*dbw).WriteOrderPosNext(nOrderPosNext); | WalletBatch(*database).WriteOrderPosNext(nOrderPosNext); | ||||
} | } | ||||
return nRet; | return nRet; | ||||
} | } | ||||
bool CWallet::AccountMove(std::string strFrom, std::string strTo, | bool CWallet::AccountMove(std::string strFrom, std::string strTo, | ||||
const Amount nAmount, std::string strComment) { | const Amount nAmount, std::string strComment) { | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
if (!walletdb.TxnBegin()) { | if (!batch.TxnBegin()) { | ||||
return false; | return false; | ||||
} | } | ||||
int64_t nNow = GetAdjustedTime(); | int64_t nNow = GetAdjustedTime(); | ||||
// Debit | // Debit | ||||
CAccountingEntry debit; | CAccountingEntry debit; | ||||
debit.nOrderPos = IncOrderPosNext(&walletdb); | debit.nOrderPos = IncOrderPosNext(&batch); | ||||
debit.strAccount = strFrom; | debit.strAccount = strFrom; | ||||
debit.nCreditDebit = -nAmount; | debit.nCreditDebit = -nAmount; | ||||
debit.nTime = nNow; | debit.nTime = nNow; | ||||
debit.strOtherAccount = strTo; | debit.strOtherAccount = strTo; | ||||
debit.strComment = strComment; | debit.strComment = strComment; | ||||
AddAccountingEntry(debit, &walletdb); | AddAccountingEntry(debit, &batch); | ||||
// Credit | // Credit | ||||
CAccountingEntry credit; | CAccountingEntry credit; | ||||
credit.nOrderPos = IncOrderPosNext(&walletdb); | credit.nOrderPos = IncOrderPosNext(&batch); | ||||
credit.strAccount = strTo; | credit.strAccount = strTo; | ||||
credit.nCreditDebit = nAmount; | credit.nCreditDebit = nAmount; | ||||
credit.nTime = nNow; | credit.nTime = nNow; | ||||
credit.strOtherAccount = strFrom; | credit.strOtherAccount = strFrom; | ||||
credit.strComment = strComment; | credit.strComment = strComment; | ||||
AddAccountingEntry(credit, &walletdb); | AddAccountingEntry(credit, &batch); | ||||
return walletdb.TxnCommit(); | return batch.TxnCommit(); | ||||
} | } | ||||
bool CWallet::GetLabelDestination(CTxDestination &dest, | bool CWallet::GetLabelDestination(CTxDestination &dest, | ||||
const std::string &label, bool bForceNew) { | const std::string &label, bool bForceNew) { | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
CAccount account; | CAccount account; | ||||
walletdb.ReadAccount(label, account); | batch.ReadAccount(label, account); | ||||
if (!bForceNew) { | if (!bForceNew) { | ||||
if (!account.vchPubKey.IsValid()) { | if (!account.vchPubKey.IsValid()) { | ||||
bForceNew = true; | bForceNew = true; | ||||
} else { | } else { | ||||
// Check if the current key has been used (TODO: check other | // Check if the current key has been used (TODO: check other | ||||
// addresses with the same key) | // addresses with the same key) | ||||
CScript scriptPubKey = GetScriptForDestination( | CScript scriptPubKey = GetScriptForDestination( | ||||
Show All 14 Lines | bool CWallet::GetLabelDestination(CTxDestination &dest, | ||||
if (bForceNew) { | if (bForceNew) { | ||||
if (!GetKeyFromPool(account.vchPubKey, false)) { | if (!GetKeyFromPool(account.vchPubKey, false)) { | ||||
return false; | return false; | ||||
} | } | ||||
LearnRelatedScripts(account.vchPubKey, g_address_type); | LearnRelatedScripts(account.vchPubKey, g_address_type); | ||||
dest = GetDestinationForKey(account.vchPubKey, g_address_type); | dest = GetDestinationForKey(account.vchPubKey, g_address_type); | ||||
SetAddressBook(dest, label, "receive"); | SetAddressBook(dest, label, "receive"); | ||||
walletdb.WriteAccount(label, account); | batch.WriteAccount(label, account); | ||||
} else { | } else { | ||||
dest = GetDestinationForKey(account.vchPubKey, g_address_type); | dest = GetDestinationForKey(account.vchPubKey, g_address_type); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CWallet::MarkDirty() { | void CWallet::MarkDirty() { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
for (std::pair<const TxId, CWalletTx> &item : mapWallet) { | for (std::pair<const TxId, CWalletTx> &item : mapWallet) { | ||||
item.second.MarkDirty(); | item.second.MarkDirty(); | ||||
} | } | ||||
} | } | ||||
bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) { | bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CWalletDB walletdb(*dbw, "r+", fFlushOnClose); | WalletBatch batch(*database, "r+", fFlushOnClose); | ||||
const TxId &txid = wtxIn.GetId(); | const TxId &txid = wtxIn.GetId(); | ||||
// Inserts only if not already there, returns tx inserted or tx found. | // Inserts only if not already there, returns tx inserted or tx found. | ||||
std::pair<std::map<TxId, CWalletTx>::iterator, bool> ret = | std::pair<std::map<TxId, CWalletTx>::iterator, bool> ret = | ||||
mapWallet.insert(std::make_pair(txid, wtxIn)); | mapWallet.insert(std::make_pair(txid, wtxIn)); | ||||
CWalletTx &wtx = (*ret.first).second; | CWalletTx &wtx = (*ret.first).second; | ||||
wtx.BindWallet(this); | wtx.BindWallet(this); | ||||
bool fInsertedNew = ret.second; | bool fInsertedNew = ret.second; | ||||
if (fInsertedNew) { | if (fInsertedNew) { | ||||
wtx.nTimeReceived = GetAdjustedTime(); | wtx.nTimeReceived = GetAdjustedTime(); | ||||
wtx.nOrderPos = IncOrderPosNext(&walletdb); | wtx.nOrderPos = IncOrderPosNext(&batch); | ||||
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); | wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); | ||||
wtx.nTimeSmart = ComputeTimeSmart(wtx); | wtx.nTimeSmart = ComputeTimeSmart(wtx); | ||||
AddToSpends(txid); | AddToSpends(txid); | ||||
} | } | ||||
bool fUpdated = false; | bool fUpdated = false; | ||||
if (!fInsertedNew) { | if (!fInsertedNew) { | ||||
// Merge | // Merge | ||||
Show All 19 Lines | if (!fInsertedNew) { | ||||
} | } | ||||
} | } | ||||
//// debug print | //// debug print | ||||
LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetId().ToString(), | LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetId().ToString(), | ||||
(fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); | (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); | ||||
// Write to disk | // Write to disk | ||||
if ((fInsertedNew || fUpdated) && !walletdb.WriteTx(wtx)) { | if ((fInsertedNew || fUpdated) && !batch.WriteTx(wtx)) { | ||||
return false; | return false; | ||||
} | } | ||||
// Break debit/credit balance caches: | // Break debit/credit balance caches: | ||||
wtx.MarkDirty(); | wtx.MarkDirty(); | ||||
// Notify UI of new or updated transaction. | // Notify UI of new or updated transaction. | ||||
NotifyTransactionChanged(this, txid, fInsertedNew ? CT_NEW : CT_UPDATED); | NotifyTransactionChanged(this, txid, fInsertedNew ? CT_NEW : CT_UPDATED); | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | bool CWallet::TransactionCanBeAbandoned(const TxId &txid) const { | ||||
const CWalletTx *wtx = GetWalletTx(txid); | const CWalletTx *wtx = GetWalletTx(txid); | ||||
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && | return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && | ||||
!wtx->InMempool(); | !wtx->InMempool(); | ||||
} | } | ||||
bool CWallet::AbandonTransaction(const TxId &txid) { | bool CWallet::AbandonTransaction(const TxId &txid) { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
CWalletDB walletdb(*dbw, "r+"); | WalletBatch batch(*database, "r+"); | ||||
std::set<TxId> todo; | std::set<TxId> todo; | ||||
std::set<TxId> done; | std::set<TxId> done; | ||||
// Can't mark abandoned if confirmed or in mempool | // Can't mark abandoned if confirmed or in mempool | ||||
auto it = mapWallet.find(txid); | auto it = mapWallet.find(txid); | ||||
assert(it != mapWallet.end()); | assert(it != mapWallet.end()); | ||||
CWalletTx &origtx = it->second; | CWalletTx &origtx = it->second; | ||||
Show All 17 Lines | while (!todo.empty()) { | ||||
// need to abandon} | // need to abandon} | ||||
if (currentconfirm == 0 && !wtx.isAbandoned()) { | if (currentconfirm == 0 && !wtx.isAbandoned()) { | ||||
// If the orig tx was not in block/mempool, none of its spends can | // If the orig tx was not in block/mempool, none of its spends can | ||||
// be in mempool. | // be in mempool. | ||||
assert(!wtx.InMempool()); | assert(!wtx.InMempool()); | ||||
wtx.nIndex = -1; | wtx.nIndex = -1; | ||||
wtx.setAbandoned(); | wtx.setAbandoned(); | ||||
wtx.MarkDirty(); | wtx.MarkDirty(); | ||||
walletdb.WriteTx(wtx); | batch.WriteTx(wtx); | ||||
NotifyTransactionChanged(this, wtx.GetId(), CT_UPDATED); | NotifyTransactionChanged(this, wtx.GetId(), CT_UPDATED); | ||||
// Iterate over all its outputs, and mark transactions in the wallet | // Iterate over all its outputs, and mark transactions in the wallet | ||||
// that spend them abandoned too. | // that spend them abandoned too. | ||||
TxSpends::const_iterator iter = | TxSpends::const_iterator iter = | ||||
mapTxSpends.lower_bound(COutPoint(now, 0)); | mapTxSpends.lower_bound(COutPoint(now, 0)); | ||||
while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) { | while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) { | ||||
if (!done.count(iter->second)) { | if (!done.count(iter->second)) { | ||||
todo.insert(iter->second); | todo.insert(iter->second); | ||||
Show All 28 Lines | void CWallet::MarkConflicted(const uint256 &hashBlock, const TxId &txid) { | ||||
// If number of conflict confirms cannot be determined, this means that the | // If number of conflict confirms cannot be determined, this means that the | ||||
// block is still unknown or not yet part of the main chain, for example | // block is still unknown or not yet part of the main chain, for example | ||||
// when loading the wallet during a reindex. Do nothing in that case. | // when loading the wallet during a reindex. Do nothing in that case. | ||||
if (conflictconfirms >= 0) { | if (conflictconfirms >= 0) { | ||||
return; | return; | ||||
} | } | ||||
// Do not flush the wallet here for performance reasons. | // Do not flush the wallet here for performance reasons. | ||||
CWalletDB walletdb(*dbw, "r+", false); | WalletBatch batch(*database, "r+", false); | ||||
std::set<TxId> todo; | std::set<TxId> todo; | ||||
std::set<TxId> done; | std::set<TxId> done; | ||||
todo.insert(txid); | todo.insert(txid); | ||||
while (!todo.empty()) { | while (!todo.empty()) { | ||||
const TxId now = *todo.begin(); | const TxId now = *todo.begin(); | ||||
todo.erase(now); | todo.erase(now); | ||||
done.insert(now); | done.insert(now); | ||||
auto it = mapWallet.find(now); | auto it = mapWallet.find(now); | ||||
assert(it != mapWallet.end()); | assert(it != mapWallet.end()); | ||||
CWalletTx &wtx = it->second; | CWalletTx &wtx = it->second; | ||||
int currentconfirm = wtx.GetDepthInMainChain(); | int currentconfirm = wtx.GetDepthInMainChain(); | ||||
if (conflictconfirms < currentconfirm) { | if (conflictconfirms < currentconfirm) { | ||||
// Block is 'more conflicted' than current confirm; update. | // Block is 'more conflicted' than current confirm; update. | ||||
// Mark transaction as conflicted with this block. | // Mark transaction as conflicted with this block. | ||||
wtx.nIndex = -1; | wtx.nIndex = -1; | ||||
wtx.hashBlock = hashBlock; | wtx.hashBlock = hashBlock; | ||||
wtx.MarkDirty(); | wtx.MarkDirty(); | ||||
walletdb.WriteTx(wtx); | batch.WriteTx(wtx); | ||||
// Iterate over all its outputs, and mark transactions in the wallet | // Iterate over all its outputs, and mark transactions in the wallet | ||||
// that spend them conflicted too. | // that spend them conflicted too. | ||||
TxSpends::const_iterator iter = | TxSpends::const_iterator iter = | ||||
mapTxSpends.lower_bound(COutPoint(now, 0)); | mapTxSpends.lower_bound(COutPoint(now, 0)); | ||||
while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) { | while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) { | ||||
if (!done.count(iter->second)) { | if (!done.count(iter->second)) { | ||||
todo.insert(iter->second); | todo.insert(iter->second); | ||||
} | } | ||||
iter++; | iter++; | ||||
} | } | ||||
// If a transaction changes 'conflicted' state, that changes the | // If a transaction changes 'conflicted' state, that changes the | ||||
// balance available of the outputs it spends. So force those to be | // balance available of the outputs it spends. So force those to be | ||||
// recomputed. | // recomputed. | ||||
for (const CTxIn &txin : wtx.tx->vin) { | for (const CTxIn &txin : wtx.tx->vin) { | ||||
auto it2 = mapWallet.find(txin.prevout.GetTxId()); | auto it2 = mapWallet.find(txin.prevout.GetTxId()); | ||||
if (it2 != mapWallet.end()) { | if (it2 != mapWallet.end()) { | ||||
it2->second.MarkDirty(); | it2->second.MarkDirty(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 299 Lines • ▼ Show 20 Lines | bool CWallet::SetHDMasterKey(const CPubKey &pubkey) { | ||||
newHdChain.masterKeyID = pubkey.GetID(); | newHdChain.masterKeyID = pubkey.GetID(); | ||||
SetHDChain(newHdChain, false); | SetHDChain(newHdChain, false); | ||||
return true; | return true; | ||||
} | } | ||||
bool CWallet::SetHDChain(const CHDChain &chain, bool memonly) { | bool CWallet::SetHDChain(const CHDChain &chain, bool memonly) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (!memonly && !CWalletDB(*dbw).WriteHDChain(chain)) { | if (!memonly && !WalletBatch(*database).WriteHDChain(chain)) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": writing chain failed"); | ": writing chain failed"); | ||||
} | } | ||||
hdChain = chain; | hdChain = chain; | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 749 Lines • ▼ Show 20 Lines | for (const auto &entry : mapWallet) { | ||||
// For outgoing txs, subtract amount debited. | // For outgoing txs, subtract amount debited. | ||||
if (outgoing && (!account || *account == wtx.strFromAccount)) { | if (outgoing && (!account || *account == wtx.strFromAccount)) { | ||||
balance -= debit; | balance -= debit; | ||||
} | } | ||||
} | } | ||||
if (account) { | if (account) { | ||||
balance += CWalletDB(*dbw).GetAccountCreditDebit(*account); | balance += WalletBatch(*database).GetAccountCreditDebit(*account); | ||||
} | } | ||||
return balance; | return balance; | ||||
} | } | ||||
Amount CWallet::GetAvailableBalance(const CCoinControl *coinControl) const { | Amount CWallet::GetAvailableBalance(const CCoinControl *coinControl) const { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
▲ Show 20 Lines • Show All 979 Lines • ▼ Show 20 Lines | if (fBroadcastTransactions) { | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CWallet::ListAccountCreditDebit(const std::string &strAccount, | void CWallet::ListAccountCreditDebit(const std::string &strAccount, | ||||
std::list<CAccountingEntry> &entries) { | std::list<CAccountingEntry> &entries) { | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
return walletdb.ListAccountCreditDebit(strAccount, entries); | return batch.ListAccountCreditDebit(strAccount, entries); | ||||
} | } | ||||
bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry) { | bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry) { | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
return AddAccountingEntry(acentry, &walletdb); | |||||
return AddAccountingEntry(acentry, &batch); | |||||
} | } | ||||
bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry, | bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry, | ||||
CWalletDB *pwalletdb) { | WalletBatch *batch) { | ||||
if (!pwalletdb->WriteAccountingEntry(++nAccountingEntryNumber, acentry)) { | if (!batch->WriteAccountingEntry(++nAccountingEntryNumber, acentry)) { | ||||
return false; | return false; | ||||
} | } | ||||
laccentries.push_back(acentry); | laccentries.push_back(acentry); | ||||
CAccountingEntry &entry = laccentries.back(); | CAccountingEntry &entry = laccentries.back(); | ||||
wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry))); | wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry))); | ||||
return true; | return true; | ||||
} | } | ||||
DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
fFirstRunRet = false; | fFirstRunRet = false; | ||||
DBErrors nLoadWalletRet = CWalletDB(*dbw, "cr+").LoadWallet(this); | DBErrors nLoadWalletRet = WalletBatch(*database, "cr+").LoadWallet(this); | ||||
if (nLoadWalletRet == DBErrors::NEED_REWRITE) { | if (nLoadWalletRet == DBErrors::NEED_REWRITE) { | ||||
if (dbw->Rewrite("\x04pool")) { | if (database->Rewrite("\x04pool")) { | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
m_pool_key_to_index.clear(); | m_pool_key_to_index.clear(); | ||||
// Note: can't top-up keypool here, because wallet is locked. | // Note: can't top-up keypool here, because wallet is locked. | ||||
// User will be prompted to unlock wallet the next operation | // User will be prompted to unlock wallet the next operation | ||||
// that requires a new key. | // that requires a new key. | ||||
} | } | ||||
} | } | ||||
Show All 9 Lines | DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | ||||
uiInterface.LoadWallet(this); | uiInterface.LoadWallet(this); | ||||
return DBErrors::LOAD_OK; | return DBErrors::LOAD_OK; | ||||
} | } | ||||
DBErrors CWallet::ZapSelectTx(std::vector<TxId> &txIdsIn, | DBErrors CWallet::ZapSelectTx(std::vector<TxId> &txIdsIn, | ||||
std::vector<TxId> &txIdsOut) { | std::vector<TxId> &txIdsOut) { | ||||
AssertLockHeld(cs_wallet); // mapWallet | // mapWallet | ||||
AssertLockHeld(cs_wallet); | |||||
DBErrors nZapSelectTxRet = | DBErrors nZapSelectTxRet = | ||||
CWalletDB(*dbw, "cr+").ZapSelectTx(txIdsIn, txIdsOut); | WalletBatch(*database, "cr+").ZapSelectTx(txIdsIn, txIdsOut); | ||||
for (const TxId &txid : txIdsOut) { | for (const TxId &txid : txIdsOut) { | ||||
mapWallet.erase(txid); | mapWallet.erase(txid); | ||||
} | } | ||||
if (nZapSelectTxRet == DBErrors::NEED_REWRITE) { | if (nZapSelectTxRet == DBErrors::NEED_REWRITE) { | ||||
if (dbw->Rewrite("\x04pool")) { | if (database->Rewrite("\x04pool")) { | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
m_pool_key_to_index.clear(); | m_pool_key_to_index.clear(); | ||||
// Note: can't top-up keypool here, because wallet is locked. | // Note: can't top-up keypool here, because wallet is locked. | ||||
// User will be prompted to unlock wallet the next operation | // User will be prompted to unlock wallet the next operation | ||||
// that requires a new key. | // that requires a new key. | ||||
} | } | ||||
} | } | ||||
if (nZapSelectTxRet != DBErrors::LOAD_OK) { | if (nZapSelectTxRet != DBErrors::LOAD_OK) { | ||||
return nZapSelectTxRet; | return nZapSelectTxRet; | ||||
} | } | ||||
MarkDirty(); | MarkDirty(); | ||||
return DBErrors::LOAD_OK; | return DBErrors::LOAD_OK; | ||||
} | } | ||||
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx> &vWtx) { | DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx> &vWtx) { | ||||
DBErrors nZapWalletTxRet = CWalletDB(*dbw, "cr+").ZapWalletTx(vWtx); | DBErrors nZapWalletTxRet = WalletBatch(*database, "cr+").ZapWalletTx(vWtx); | ||||
if (nZapWalletTxRet == DBErrors::NEED_REWRITE) { | if (nZapWalletTxRet == DBErrors::NEED_REWRITE) { | ||||
if (dbw->Rewrite("\x04pool")) { | if (database->Rewrite("\x04pool")) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
m_pool_key_to_index.clear(); | m_pool_key_to_index.clear(); | ||||
// Note: can't top-up keypool here, because wallet is locked. | // Note: can't top-up keypool here, because wallet is locked. | ||||
// User will be prompted to unlock wallet the next operation | // User will be prompted to unlock wallet the next operation | ||||
// that requires a new key. | // that requires a new key. | ||||
} | } | ||||
Show All 21 Lines | bool fUpdated = false; | ||||
if (!strPurpose.empty()) { | if (!strPurpose.empty()) { | ||||
mapAddressBook[address].purpose = strPurpose; | mapAddressBook[address].purpose = strPurpose; | ||||
} | } | ||||
} | } | ||||
NotifyAddressBookChanged(this, address, strName, | NotifyAddressBookChanged(this, address, strName, | ||||
::IsMine(*this, address) != ISMINE_NO, strPurpose, | ::IsMine(*this, address) != ISMINE_NO, strPurpose, | ||||
(fUpdated ? CT_UPDATED : CT_NEW)); | (fUpdated ? CT_UPDATED : CT_NEW)); | ||||
if (!strPurpose.empty() && | if (!strPurpose.empty() && | ||||
!CWalletDB(*dbw).WritePurpose(address, strPurpose)) { | !WalletBatch(*database).WritePurpose(address, strPurpose)) { | ||||
return false; | return false; | ||||
} | } | ||||
return WalletBatch(*database).WriteName(address, strName); | |||||
return CWalletDB(*dbw).WriteName(address, strName); | |||||
} | } | ||||
bool CWallet::DelAddressBook(const CTxDestination &address) { | bool CWallet::DelAddressBook(const CTxDestination &address) { | ||||
{ | { | ||||
// mapAddressBook | // mapAddressBook | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
// Delete destdata tuples associated with address. | // Delete destdata tuples associated with address. | ||||
for (const std::pair<std::string, std::string> &item : | for (const std::pair<std::string, std::string> &item : | ||||
mapAddressBook[address].destdata) { | mapAddressBook[address].destdata) { | ||||
CWalletDB(*dbw).EraseDestData(address, item.first); | WalletBatch(*database).EraseDestData(address, item.first); | ||||
} | } | ||||
mapAddressBook.erase(address); | mapAddressBook.erase(address); | ||||
} | } | ||||
NotifyAddressBookChanged(this, address, "", | NotifyAddressBookChanged(this, address, "", | ||||
::IsMine(*this, address) != ISMINE_NO, "", | ::IsMine(*this, address) != ISMINE_NO, "", | ||||
CT_DELETED); | CT_DELETED); | ||||
CWalletDB(*dbw).ErasePurpose(address); | WalletBatch(*database).ErasePurpose(address); | ||||
return CWalletDB(*dbw).EraseName(address); | return WalletBatch(*database).EraseName(address); | ||||
} | } | ||||
const std::string &CWallet::GetLabelName(const CScript &scriptPubKey) const { | const std::string &CWallet::GetLabelName(const CScript &scriptPubKey) const { | ||||
CTxDestination address; | CTxDestination address; | ||||
if (ExtractDestination(scriptPubKey, address) && | if (ExtractDestination(scriptPubKey, address) && | ||||
!scriptPubKey.IsUnspendable()) { | !scriptPubKey.IsUnspendable()) { | ||||
auto mi = mapAddressBook.find(address); | auto mi = mapAddressBook.find(address); | ||||
if (mi != mapAddressBook.end()) { | if (mi != mapAddressBook.end()) { | ||||
return mi->second.name; | return mi->second.name; | ||||
} | } | ||||
} | } | ||||
// A scriptPubKey that doesn't have an entry in the address book is | // A scriptPubKey that doesn't have an entry in the address book is | ||||
// associated with the default label (""). | // associated with the default label (""). | ||||
const static std::string DEFAULT_LABEL_NAME; | const static std::string DEFAULT_LABEL_NAME; | ||||
return DEFAULT_LABEL_NAME; | return DEFAULT_LABEL_NAME; | ||||
} | } | ||||
/** | /** | ||||
* Mark old keypool keys as used, and generate all new keys. | * Mark old keypool keys as used, and generate all new keys. | ||||
*/ | */ | ||||
bool CWallet::NewKeyPool() { | bool CWallet::NewKeyPool() { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
for (int64_t nIndex : setInternalKeyPool) { | for (int64_t nIndex : setInternalKeyPool) { | ||||
walletdb.ErasePool(nIndex); | batch.ErasePool(nIndex); | ||||
} | } | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
for (int64_t nIndex : setExternalKeyPool) { | for (int64_t nIndex : setExternalKeyPool) { | ||||
walletdb.ErasePool(nIndex); | batch.ErasePool(nIndex); | ||||
} | } | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
m_pool_key_to_index.clear(); | m_pool_key_to_index.clear(); | ||||
if (!TopUpKeyPool()) { | if (!TopUpKeyPool()) { | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | bool CWallet::TopUpKeyPool(unsigned int kpSize) { | ||||
int64_t missingInternal = std::max<int64_t>( | int64_t missingInternal = std::max<int64_t>( | ||||
std::max<int64_t>(nTargetSize, 1) - setInternalKeyPool.size(), 0); | std::max<int64_t>(nTargetSize, 1) - setInternalKeyPool.size(), 0); | ||||
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) { | if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) { | ||||
// don't create extra internal keys | // don't create extra internal keys | ||||
missingInternal = 0; | missingInternal = 0; | ||||
} | } | ||||
bool internal = false; | bool internal = false; | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
for (int64_t i = missingInternal + missingExternal; i--;) { | for (int64_t i = missingInternal + missingExternal; i--;) { | ||||
if (i < missingInternal) { | if (i < missingInternal) { | ||||
internal = true; | internal = true; | ||||
} | } | ||||
// How in the hell did you use so many keys? | // How in the hell did you use so many keys? | ||||
assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); | assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); | ||||
int64_t index = ++m_max_keypool_index; | int64_t index = ++m_max_keypool_index; | ||||
CPubKey pubkey(GenerateNewKey(walletdb, internal)); | CPubKey pubkey(GenerateNewKey(batch, internal)); | ||||
if (!walletdb.WritePool(index, CKeyPool(pubkey, internal))) { | if (!batch.WritePool(index, CKeyPool(pubkey, internal))) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": writing generated key failed"); | ": writing generated key failed"); | ||||
} | } | ||||
if (internal) { | if (internal) { | ||||
setInternalKeyPool.insert(index); | setInternalKeyPool.insert(index); | ||||
} else { | } else { | ||||
setExternalKeyPool.insert(index); | setExternalKeyPool.insert(index); | ||||
Show All 28 Lines | void CWallet::ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool, | ||||
std::set<int64_t> &setKeyPool = | std::set<int64_t> &setKeyPool = | ||||
fReturningInternal ? setInternalKeyPool : setExternalKeyPool; | fReturningInternal ? setInternalKeyPool : setExternalKeyPool; | ||||
// Get the oldest key | // Get the oldest key | ||||
if (setKeyPool.empty()) { | if (setKeyPool.empty()) { | ||||
return; | return; | ||||
} | } | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
auto it = setKeyPool.begin(); | auto it = setKeyPool.begin(); | ||||
nIndex = *it; | nIndex = *it; | ||||
setKeyPool.erase(it); | setKeyPool.erase(it); | ||||
if (!walletdb.ReadPool(nIndex, keypool)) { | if (!batch.ReadPool(nIndex, keypool)) { | ||||
throw std::runtime_error(std::string(__func__) + ": read failed"); | throw std::runtime_error(std::string(__func__) + ": read failed"); | ||||
} | } | ||||
if (!HaveKey(keypool.vchPubKey.GetID())) { | if (!HaveKey(keypool.vchPubKey.GetID())) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": unknown key in key pool"); | ": unknown key in key pool"); | ||||
} | } | ||||
if (keypool.fInternal != fReturningInternal) { | if (keypool.fInternal != fReturningInternal) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": keypool entry misclassified"); | ": keypool entry misclassified"); | ||||
} | } | ||||
assert(keypool.vchPubKey.IsValid()); | assert(keypool.vchPubKey.IsValid()); | ||||
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | ||||
LogPrintf("keypool reserve %d\n", nIndex); | LogPrintf("keypool reserve %d\n", nIndex); | ||||
} | } | ||||
void CWallet::KeepKey(int64_t nIndex) { | void CWallet::KeepKey(int64_t nIndex) { | ||||
// Remove from key pool. | // Remove from key pool. | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
walletdb.ErasePool(nIndex); | batch.ErasePool(nIndex); | ||||
LogPrintf("keypool keep %d\n", nIndex); | LogPrintf("keypool keep %d\n", nIndex); | ||||
} | } | ||||
void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) { | void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) { | ||||
// Return to key pool | // Return to key pool | ||||
{ | { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (fInternal) { | if (fInternal) { | ||||
Show All 11 Lines | bool CWallet::GetKeyFromPool(CPubKey &result, bool internal) { | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
int64_t nIndex = 0; | int64_t nIndex = 0; | ||||
ReserveKeyFromKeyPool(nIndex, keypool, internal); | ReserveKeyFromKeyPool(nIndex, keypool, internal); | ||||
if (nIndex == -1) { | if (nIndex == -1) { | ||||
if (IsLocked()) { | if (IsLocked()) { | ||||
return false; | return false; | ||||
} | } | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
result = GenerateNewKey(walletdb, internal); | result = GenerateNewKey(batch, internal); | ||||
return true; | return true; | ||||
} | } | ||||
KeepKey(nIndex); | KeepKey(nIndex); | ||||
result = keypool.vchPubKey; | result = keypool.vchPubKey; | ||||
return true; | return true; | ||||
} | } | ||||
static int64_t GetOldestKeyTimeInPool(const std::set<int64_t> &setKeyPool, | static int64_t GetOldestKeyTimeInPool(const std::set<int64_t> &setKeyPool, | ||||
CWalletDB &walletdb) { | WalletBatch &batch) { | ||||
if (setKeyPool.empty()) { | if (setKeyPool.empty()) { | ||||
return GetTime(); | return GetTime(); | ||||
} | } | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
int64_t nIndex = *(setKeyPool.begin()); | int64_t nIndex = *(setKeyPool.begin()); | ||||
if (!walletdb.ReadPool(nIndex, keypool)) { | if (!batch.ReadPool(nIndex, keypool)) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": read oldest key in keypool failed"); | ": read oldest key in keypool failed"); | ||||
} | } | ||||
assert(keypool.vchPubKey.IsValid()); | assert(keypool.vchPubKey.IsValid()); | ||||
return keypool.nTime; | return keypool.nTime; | ||||
} | } | ||||
int64_t CWallet::GetOldestKeyPoolTime() { | int64_t CWallet::GetOldestKeyPoolTime() { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
// load oldest key from keypool, get time and return | // load oldest key from keypool, get time and return | ||||
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, walletdb); | int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); | ||||
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { | if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { | ||||
oldestKey = std::max( | oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), | ||||
GetOldestKeyTimeInPool(setInternalKeyPool, walletdb), oldestKey); | oldestKey); | ||||
} | } | ||||
return oldestKey; | return oldestKey; | ||||
} | } | ||||
std::map<CTxDestination, Amount> CWallet::GetAddressBalances() { | std::map<CTxDestination, Amount> CWallet::GetAddressBalances() { | ||||
std::map<CTxDestination, Amount> balances; | std::map<CTxDestination, Amount> balances; | ||||
▲ Show 20 Lines • Show All 199 Lines • ▼ Show 20 Lines | void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { | ||||
if (!internal) { | if (!internal) { | ||||
assert(setExternalKeyPool.count(keypool_id)); | assert(setExternalKeyPool.count(keypool_id)); | ||||
} | } | ||||
std::set<int64_t> *setKeyPool = | std::set<int64_t> *setKeyPool = | ||||
internal ? &setInternalKeyPool : &setExternalKeyPool; | internal ? &setInternalKeyPool : &setExternalKeyPool; | ||||
auto it = setKeyPool->begin(); | auto it = setKeyPool->begin(); | ||||
CWalletDB walletdb(*dbw); | WalletBatch batch(*database); | ||||
while (it != std::end(*setKeyPool)) { | while (it != std::end(*setKeyPool)) { | ||||
const int64_t &index = *(it); | const int64_t &index = *(it); | ||||
if (index > keypool_id) { | if (index > keypool_id) { | ||||
// set*KeyPool is ordered | // set*KeyPool is ordered | ||||
break; | break; | ||||
} | } | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
if (walletdb.ReadPool(index, keypool)) { | if (batch.ReadPool(index, keypool)) { | ||||
// TODO: This should be unnecessary | // TODO: This should be unnecessary | ||||
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | ||||
} | } | ||||
LearnAllRelatedScripts(keypool.vchPubKey); | LearnAllRelatedScripts(keypool.vchPubKey); | ||||
walletdb.ErasePool(index); | batch.ErasePool(index); | ||||
it = setKeyPool->erase(it); | it = setKeyPool->erase(it); | ||||
} | } | ||||
} | } | ||||
bool CWallet::HasUnusedKeys(size_t min_keys) const { | bool CWallet::HasUnusedKeys(size_t min_keys) const { | ||||
return setExternalKeyPool.size() >= min_keys && | return setExternalKeyPool.size() >= min_keys && | ||||
(setInternalKeyPool.size() >= min_keys || | (setInternalKeyPool.size() >= min_keys || | ||||
!CanSupportFeature(FEATURE_HD_SPLIT)); | !CanSupportFeature(FEATURE_HD_SPLIT)); | ||||
▲ Show 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | |||||
bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, | bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, | ||||
const std::string &value) { | const std::string &value) { | ||||
if (boost::get<CNoDestination>(&dest)) { | if (boost::get<CNoDestination>(&dest)) { | ||||
return false; | return false; | ||||
} | } | ||||
mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); | mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); | ||||
return CWalletDB(*dbw).WriteDestData(dest, key, value); | return WalletBatch(*database).WriteDestData(dest, key, value); | ||||
} | } | ||||
bool CWallet::EraseDestData(const CTxDestination &dest, | bool CWallet::EraseDestData(const CTxDestination &dest, | ||||
const std::string &key) { | const std::string &key) { | ||||
if (!mapAddressBook[dest].destdata.erase(key)) { | if (!mapAddressBook[dest].destdata.erase(key)) { | ||||
return false; | return false; | ||||
} | } | ||||
return CWalletDB(*dbw).EraseDestData(dest, key); | return WalletBatch(*database).EraseDestData(dest, key); | ||||
} | } | ||||
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, | bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, | ||||
const std::string &value) { | const std::string &value) { | ||||
mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); | mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); | ||||
return true; | return true; | ||||
} | } | ||||
Show All 36 Lines | CWallet *CWallet::CreateWalletFromFile(const CChainParams &chainParams, | ||||
// Needed to restore wallet transaction meta data after -zapwallettxes | // Needed to restore wallet transaction meta data after -zapwallettxes | ||||
std::vector<CWalletTx> vWtx; | std::vector<CWalletTx> vWtx; | ||||
if (gArgs.GetBoolArg("-zapwallettxes", false)) { | if (gArgs.GetBoolArg("-zapwallettxes", false)) { | ||||
uiInterface.InitMessage(_("Zapping all transactions from wallet...")); | uiInterface.InitMessage(_("Zapping all transactions from wallet...")); | ||||
std::unique_ptr<CWallet> tempWallet = std::make_unique<CWallet>( | std::unique_ptr<CWallet> tempWallet = std::make_unique<CWallet>( | ||||
chainParams, name, CWalletDBWrapper::Create(path)); | chainParams, name, WalletDatabase::Create(path)); | ||||
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); | DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); | ||||
if (nZapWalletRet != DBErrors::LOAD_OK) { | if (nZapWalletRet != DBErrors::LOAD_OK) { | ||||
InitError( | InitError( | ||||
strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
uiInterface.InitMessage(_("Loading wallet...")); | uiInterface.InitMessage(_("Loading wallet...")); | ||||
int64_t nStart = GetTimeMillis(); | int64_t nStart = GetTimeMillis(); | ||||
bool fFirstRun = true; | bool fFirstRun = true; | ||||
CWallet *walletInstance = | CWallet *walletInstance = | ||||
new CWallet(chainParams, name, CWalletDBWrapper::Create(path)); | new CWallet(chainParams, name, WalletDatabase::Create(path)); | ||||
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); | DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); | ||||
if (nLoadWalletRet != DBErrors::LOAD_OK) { | if (nLoadWalletRet != DBErrors::LOAD_OK) { | ||||
if (nLoadWalletRet == DBErrors::CORRUPT) { | if (nLoadWalletRet == DBErrors::CORRUPT) { | ||||
InitError( | InitError( | ||||
strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | CWallet *CWallet::CreateWalletFromFile(const CChainParams &chainParams, | ||||
// Try to top up keypool. No-op if the wallet is locked. | // Try to top up keypool. No-op if the wallet is locked. | ||||
walletInstance->TopUpKeyPool(); | walletInstance->TopUpKeyPool(); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CBlockIndex *pindexRescan = chainActive.Genesis(); | CBlockIndex *pindexRescan = chainActive.Genesis(); | ||||
if (!gArgs.GetBoolArg("-rescan", false)) { | if (!gArgs.GetBoolArg("-rescan", false)) { | ||||
CWalletDB walletdb(*walletInstance->dbw); | WalletBatch batch(*walletInstance->database); | ||||
CBlockLocator locator; | CBlockLocator locator; | ||||
if (walletdb.ReadBestBlock(locator)) { | if (batch.ReadBestBlock(locator)) { | ||||
pindexRescan = FindForkInGlobalIndex(chainActive, locator); | pindexRescan = FindForkInGlobalIndex(chainActive, locator); | ||||
} | } | ||||
} | } | ||||
walletInstance->m_last_block_processed = chainActive.Tip(); | walletInstance->m_last_block_processed = chainActive.Tip(); | ||||
RegisterValidationInterface(walletInstance); | RegisterValidationInterface(walletInstance); | ||||
if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { | if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { | ||||
Show All 37 Lines | if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { | ||||
_("Failed to rescan the wallet during initialization")); | _("Failed to rescan the wallet during initialization")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, | walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, | ||||
reserver, true); | reserver, true); | ||||
} | } | ||||
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); | LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); | ||||
walletInstance->ChainStateFlushed(chainActive.GetLocator()); | walletInstance->ChainStateFlushed(chainActive.GetLocator()); | ||||
walletInstance->dbw->IncrementUpdateCounter(); | walletInstance->database->IncrementUpdateCounter(); | ||||
// Restore wallet transaction metadata after -zapwallettxes=1 | // Restore wallet transaction metadata after -zapwallettxes=1 | ||||
if (gArgs.GetBoolArg("-zapwallettxes", false) && | if (gArgs.GetBoolArg("-zapwallettxes", false) && | ||||
gArgs.GetArg("-zapwallettxes", "1") != "2") { | gArgs.GetArg("-zapwallettxes", "1") != "2") { | ||||
CWalletDB walletdb(*walletInstance->dbw); | WalletBatch batch(*walletInstance->database); | ||||
for (const CWalletTx &wtxOld : vWtx) { | for (const CWalletTx &wtxOld : vWtx) { | ||||
const TxId txid = wtxOld.GetId(); | const TxId txid = wtxOld.GetId(); | ||||
std::map<TxId, CWalletTx>::iterator mi = | std::map<TxId, CWalletTx>::iterator mi = | ||||
walletInstance->mapWallet.find(txid); | walletInstance->mapWallet.find(txid); | ||||
if (mi != walletInstance->mapWallet.end()) { | if (mi != walletInstance->mapWallet.end()) { | ||||
const CWalletTx *copyFrom = &wtxOld; | const CWalletTx *copyFrom = &wtxOld; | ||||
CWalletTx *copyTo = &mi->second; | CWalletTx *copyTo = &mi->second; | ||||
copyTo->mapValue = copyFrom->mapValue; | copyTo->mapValue = copyFrom->mapValue; | ||||
copyTo->vOrderForm = copyFrom->vOrderForm; | copyTo->vOrderForm = copyFrom->vOrderForm; | ||||
copyTo->nTimeReceived = copyFrom->nTimeReceived; | copyTo->nTimeReceived = copyFrom->nTimeReceived; | ||||
copyTo->nTimeSmart = copyFrom->nTimeSmart; | copyTo->nTimeSmart = copyFrom->nTimeSmart; | ||||
copyTo->fFromMe = copyFrom->fFromMe; | copyTo->fFromMe = copyFrom->fFromMe; | ||||
copyTo->strFromAccount = copyFrom->strFromAccount; | copyTo->strFromAccount = copyFrom->strFromAccount; | ||||
copyTo->nOrderPos = copyFrom->nOrderPos; | copyTo->nOrderPos = copyFrom->nOrderPos; | ||||
walletdb.WriteTx(*copyTo); | batch.WriteTx(*copyTo); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
walletInstance->SetBroadcastTransactions( | walletInstance->SetBroadcastTransactions( | ||||
gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); | gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); | ||||
Show All 20 Lines | if (!CWallet::fFlushScheduled.exchange(true)) { | ||||
MaybeCompactWalletDB(); | MaybeCompactWalletDB(); | ||||
return true; | return true; | ||||
}, | }, | ||||
500); | 500); | ||||
} | } | ||||
} | } | ||||
bool CWallet::BackupWallet(const std::string &strDest) { | bool CWallet::BackupWallet(const std::string &strDest) { | ||||
return dbw->Backup(strDest); | return database->Backup(strDest); | ||||
} | } | ||||
CKeyPool::CKeyPool() { | CKeyPool::CKeyPool() { | ||||
nTime = GetTime(); | nTime = GetTime(); | ||||
fInternal = false; | fInternal = false; | ||||
} | } | ||||
CKeyPool::CKeyPool(const CPubKey &vchPubKeyIn, bool internalIn) { | CKeyPool::CKeyPool(const CPubKey &vchPubKeyIn, bool internalIn) { | ||||
▲ Show 20 Lines • Show All 162 Lines • Show Last 20 Lines |