diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/db.cpp | 3 | ||||
-rw-r--r-- | src/wallet/db.h | 2 | ||||
-rw-r--r-- | src/wallet/rpcdump.cpp | 74 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 48 | ||||
-rw-r--r-- | src/wallet/wallet.h | 49 | ||||
-rw-r--r-- | src/wallet/walletdb.cpp | 111 | ||||
-rw-r--r-- | src/wallet/walletdb.h | 5 |
7 files changed, 184 insertions, 108 deletions
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 800e1f4e29..7d1b429b30 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -24,9 +24,6 @@ using namespace std; -unsigned int nWalletDBUpdated; - - // // CDB // diff --git a/src/wallet/db.h b/src/wallet/db.h index bc15f2147f..b4ce044e7f 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -23,8 +23,6 @@ static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; static const bool DEFAULT_WALLET_PRIVDB = true; -extern unsigned int nWalletDBUpdated; - class CDBEnv { private: diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 7d4ed70ed9..30f2414675 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -143,7 +143,7 @@ UniValue importprivkey(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); // whenever a key is imported, we need to scan the whole chain - pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' + pwalletMain->UpdateTimeFirstKey(1); if (fRescan) { pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); @@ -161,7 +161,7 @@ void ImportScript(const CScript& script, const string& strLabel, bool isRedeemSc pwalletMain->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script)) + if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script, 0 /* nCreateTime */)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); if (isRedeemScript) { @@ -500,8 +500,7 @@ UniValue importwallet(const JSONRPCRequest& request) while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200) pindex = pindex->pprev; - if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey) - pwalletMain->nTimeFirstKey = nTimeBegin; + pwalletMain->UpdateTimeFirstKey(nTimeBegin); LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); pwalletMain->ScanForWalletTransactions(pindex); @@ -576,15 +575,17 @@ UniValue dumpwallet(const JSONRPCRequest& request) if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); - std::map<CKeyID, int64_t> mapKeyBirth; + std::map<CTxDestination, int64_t> mapKeyBirth; std::set<CKeyID> setKeyPool; pwalletMain->GetKeyBirthTimes(mapKeyBirth); pwalletMain->GetAllReserveKeys(setKeyPool); // sort time/key pairs std::vector<std::pair<int64_t, CKeyID> > vKeyBirth; - for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) { - vKeyBirth.push_back(std::make_pair(it->second, it->first)); + for (const auto& entry : mapKeyBirth) { + if (const CKeyID* keyID = boost::get<CKeyID>(&entry.first)) { // set and test + vKeyBirth.push_back(std::make_pair(entry.second, *keyID)); + } } mapKeyBirth.clear(); std::sort(vKeyBirth.begin(), vKeyBirth.end()); @@ -640,7 +641,8 @@ UniValue dumpwallet(const JSONRPCRequest& request) } -UniValue processImport(const UniValue& data) { +UniValue ProcessImport(const UniValue& data, const int64_t timestamp) +{ try { bool success = false; @@ -659,7 +661,6 @@ UniValue processImport(const UniValue& data) { const bool& internal = data.exists("internal") ? data["internal"].get_bool() : false; const bool& watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false; const string& label = data.exists("label") && !internal ? data["label"].get_str() : ""; - const int64_t& timestamp = data.exists("timestamp") && data["timestamp"].get_int64() > 1 ? data["timestamp"].get_int64() : 1; bool isScript = scriptPubKey.getType() == UniValue::VSTR; bool isP2SH = strRedeemScript.length() > 0; @@ -671,6 +672,9 @@ UniValue processImport(const UniValue& data) { if (!isScript) { address = CBitcoinAddress(output); + if (!address.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } script = GetScriptForDestination(address.Get()); } else { if (!IsHex(output)) { @@ -721,7 +725,7 @@ UniValue processImport(const UniValue& data) { pwalletMain->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(redeemScript) && !pwalletMain->AddWatchOnly(redeemScript)) { + if (!pwalletMain->HaveWatchOnly(redeemScript) && !pwalletMain->AddWatchOnly(redeemScript, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } @@ -738,7 +742,7 @@ UniValue processImport(const UniValue& data) { pwalletMain->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(redeemDestination) && !pwalletMain->AddWatchOnly(redeemDestination)) { + if (!pwalletMain->HaveWatchOnly(redeemDestination) && !pwalletMain->AddWatchOnly(redeemDestination, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } @@ -782,9 +786,7 @@ UniValue processImport(const UniValue& data) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } - if (timestamp < pwalletMain->nTimeFirstKey) { - pwalletMain->nTimeFirstKey = timestamp; - } + pwalletMain->UpdateTimeFirstKey(timestamp); } } @@ -833,7 +835,7 @@ UniValue processImport(const UniValue& data) { pwalletMain->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(pubKeyScript) && !pwalletMain->AddWatchOnly(pubKeyScript)) { + if (!pwalletMain->HaveWatchOnly(pubKeyScript) && !pwalletMain->AddWatchOnly(pubKeyScript, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } @@ -851,7 +853,7 @@ UniValue processImport(const UniValue& data) { pwalletMain->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(scriptRawPubKey) && !pwalletMain->AddWatchOnly(scriptRawPubKey)) { + if (!pwalletMain->HaveWatchOnly(scriptRawPubKey) && !pwalletMain->AddWatchOnly(scriptRawPubKey, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } @@ -912,9 +914,7 @@ UniValue processImport(const UniValue& data) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } - if (timestamp < pwalletMain->nTimeFirstKey) { - pwalletMain->nTimeFirstKey = timestamp; - } + pwalletMain->UpdateTimeFirstKey(timestamp); success = true; } @@ -927,7 +927,7 @@ UniValue processImport(const UniValue& data) { pwalletMain->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script)) { + if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script, timestamp)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } @@ -958,6 +958,20 @@ UniValue processImport(const UniValue& data) { } } +int64_t GetImportTimestamp(const UniValue& data, int64_t now) +{ + if (data.exists("timestamp")) { + const UniValue& timestamp = data["timestamp"]; + if (timestamp.isNum()) { + return timestamp.get_int64(); + } else if (timestamp.isStr() && timestamp.get_str() == "now") { + return now; + } + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp.type()))); + } + throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key"); +} + UniValue importmulti(const JSONRPCRequest& mainRequest) { // clang-format off @@ -970,13 +984,18 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) " [ (array of json objects)\n" " {\n" " \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n" + " \"timestamp\": timestamp | \"now\" , (integer / string, required) Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n" + " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n" + " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n" + " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n" + " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n" + " creation time of all keys being imported by the importmulti call will be scanned.\n" " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n" " \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n" " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n" " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be be treated as not incoming payments\n" " \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n" " \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n" - " \"timestamp\": 1454686740, (integer, optional, default now) Timestamp\n" " }\n" " ,...\n" " ]\n" @@ -1015,6 +1034,12 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) LOCK2(cs_main, pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); + // Verify all timestamps are present before importing any keys. + const int64_t now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0; + for (const UniValue& data : requests.getValues()) { + GetImportTimestamp(data, now); + } + bool fRunScan = false; const int64_t minimumTimestamp = 1; int64_t nLowestTimestamp = 0; @@ -1028,7 +1053,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) UniValue response(UniValue::VARR); BOOST_FOREACH (const UniValue& data, requests.getValues()) { - const UniValue result = processImport(data); + const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp); + const UniValue result = ProcessImport(data, timestamp); response.push_back(result); if (!fRescan) { @@ -1041,15 +1067,13 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } // Get the lowest timestamp. - const int64_t& timestamp = data.exists("timestamp") && data["timestamp"].get_int64() > minimumTimestamp ? data["timestamp"].get_int64() : minimumTimestamp; - if (timestamp < nLowestTimestamp) { nLowestTimestamp = timestamp; } } if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTimeMax()) { - CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(nLowestTimestamp) : chainActive.Genesis(); + CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(std::max<int64_t>(nLowestTimestamp - 7200, 0)) : chainActive.Genesis(); if (pindex) { pwalletMain->ScanForWalletTransactions(pindex, true); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 49b943d87b..c5be732ccc 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -113,8 +113,7 @@ CPubKey CWallet::GenerateNewKey() assert(secret.VerifyPubKey(pubkey)); mapKeyMetadata[pubkey.GetID()] = metadata; - if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) - nTimeFirstKey = nCreationTime; + UpdateTimeFirstKey(nCreationTime); if (!AddKeyPubKey(secret, pubkey)) throw std::runtime_error(std::string(__func__) + ": AddKey failed"); @@ -207,13 +206,11 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, return false; } -bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) +bool CWallet::LoadKeyMetadata(const CTxDestination& keyID, const CKeyMetadata &meta) { AssertLockHeld(cs_wallet); // mapKeyMetadata - if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey)) - nTimeFirstKey = meta.nCreateTime; - - mapKeyMetadata[pubkey.GetID()] = meta; + UpdateTimeFirstKey(meta.nCreateTime); + mapKeyMetadata[keyID] = meta; return true; } @@ -222,6 +219,18 @@ bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigne return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } +void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) +{ + AssertLockHeld(cs_wallet); + if (nCreateTime <= 1) { + // Cannot determine birthday information, so set the wallet birthday to + // the beginning of time. + nTimeFirstKey = 1; + } else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) { + nTimeFirstKey = nCreateTime; + } +} + bool CWallet::AddCScript(const CScript& redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) @@ -247,15 +256,22 @@ bool CWallet::LoadCScript(const CScript& redeemScript) return CCryptoKeyStore::AddCScript(redeemScript); } -bool CWallet::AddWatchOnly(const CScript &dest) +bool CWallet::AddWatchOnly(const CScript& dest) { if (!CCryptoKeyStore::AddWatchOnly(dest)) return false; - nTimeFirstKey = 1; // No birthday information for watch-only keys. + const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)]; + UpdateTimeFirstKey(meta.nCreateTime); NotifyWatchonlyChanged(true); if (!fFileBacked) return true; - return CWalletDB(strWalletFile).WriteWatchOnly(dest); + return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta); +} + +bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime) +{ + mapKeyMetadata[CScriptID(dest)].nCreateTime = nCreateTime; + return AddWatchOnly(dest); } bool CWallet::RemoveWatchOnly(const CScript &dest) @@ -3416,14 +3432,16 @@ public: void operator()(const CNoDestination &none) {} }; -void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const { +void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const { AssertLockHeld(cs_wallet); // mapKeyMetadata mapKeyBirth.clear(); // get birth times for keys with metadata - for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) - if (it->second.nCreateTime) - mapKeyBirth[it->first] = it->second.nCreateTime; + for (const auto& entry : mapKeyMetadata) { + if (entry.second.nCreateTime) { + mapKeyBirth[entry.first] = entry.second.nCreateTime; + } + } // map in which we'll infer heights of other keys CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin @@ -3691,7 +3709,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) walletInstance->ScanForWalletTransactions(pindexRescan, true); LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); walletInstance->SetBestChain(chainActive.GetLocator()); - nWalletDBUpdated++; + CWalletDB::IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index ea4787c363..a7b15a8441 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -611,6 +611,20 @@ private: bool fFileBacked; std::set<int64_t> setKeyPool; + + int64_t nTimeFirstKey; + + /** + * Private version of AddWatchOnly method which does not accept a + * timestamp, and which will reset the wallet's nTimeFirstKey value to 1 if + * the watch key did not previously have a timestamp associated with it. + * Because this is an inherited virtual method, it is accessible despite + * being marked private, but it is marked private anyway to encourage use + * of the other AddWatchOnly which accepts a timestamp and sets + * nTimeFirstKey more intelligently for more efficient rescans. + */ + bool AddWatchOnly(const CScript& dest) override; + public: /* * Main wallet lock. @@ -635,7 +649,9 @@ public: mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); } - std::map<CKeyID, CKeyMetadata> mapKeyMetadata; + // Map from Key ID (for regular keys) or Script ID (for watch-only keys) to + // key metadata. + std::map<CTxDestination, CKeyMetadata> mapKeyMetadata; typedef std::map<unsigned int, CMasterKey> MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -688,8 +704,6 @@ public: std::set<COutPoint> setLockedCoins; - int64_t nTimeFirstKey; - const CWalletTx* GetWalletTx(const uint256& hash) const; //! check whether we are allowed to upgrade (or already support) to the named feature @@ -723,19 +737,20 @@ public: CPubKey GenerateNewKey(); void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret); //! Adds a key to the store, and saves it to disk. - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; //! Adds a key to the store, without saving it to disk (used by LoadWallet) bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } //! Load metadata (used by LoadWallet) - bool LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &metadata); + bool LoadKeyMetadata(const CTxDestination& pubKey, const CKeyMetadata &metadata); bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } + void UpdateTimeFirstKey(int64_t nCreateTime); //! Adds an encrypted key to the store, and saves it to disk. - bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); + bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) override; //! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); - bool AddCScript(const CScript& redeemScript); + bool AddCScript(const CScript& redeemScript) override; bool LoadCScript(const CScript& redeemScript); //! Adds a destination data tuple to the store, and saves it to disk @@ -748,8 +763,8 @@ public: bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const; //! Adds a watch-only address to the store, and saves it to disk. - bool AddWatchOnly(const CScript &dest); - bool RemoveWatchOnly(const CScript &dest); + bool AddWatchOnly(const CScript& dest, int64_t nCreateTime); + bool RemoveWatchOnly(const CScript &dest) override; //! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) bool LoadWatchOnly(const CScript &dest); @@ -757,7 +772,7 @@ public: bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); - void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const; + void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const; /** * Increment the next transaction order id @@ -771,11 +786,11 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true); bool LoadToWallet(const CWalletTx& wtxIn); - void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock); + void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) override; bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); - void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman); + void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override; std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman); CAmount GetBalance() const; CAmount GetUnconfirmedBalance() const; @@ -857,7 +872,7 @@ public: bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const; CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetChange(const CTransaction& tx) const; - void SetBestChain(const CBlockLocator& loc); + void SetBestChain(const CBlockLocator& loc) override; DBErrors LoadWallet(bool& fFirstRunRet); DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); @@ -867,9 +882,9 @@ public: bool DelAddressBook(const CTxDestination& address); - void UpdatedTransaction(const uint256 &hashTx); + void UpdatedTransaction(const uint256 &hashTx) override; - void Inventory(const uint256 &hash) + void Inventory(const uint256 &hash) override { { LOCK(cs_wallet); @@ -879,8 +894,8 @@ public: } } - void GetScriptForMining(boost::shared_ptr<CReserveScript> &script); - void ResetRequestCount(const uint256 &hash) + void GetScriptForMining(boost::shared_ptr<CReserveScript> &script) override; + void ResetRequestCount(const uint256 &hash) override { LOCK(cs_wallet); mapRequestCount[hash] = 0; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 9cd19ab619..81fdde401e 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -15,6 +15,8 @@ #include "utiltime.h" #include "wallet/wallet.h" +#include <atomic> + #include <boost/version.hpp> #include <boost/filesystem.hpp> #include <boost/foreach.hpp> @@ -24,13 +26,15 @@ using namespace std; static uint64_t nAccountingEntryNumber = 0; +static std::atomic<unsigned int> nWalletDBUpdateCounter; + // // CWalletDB // bool CWalletDB::WriteName(const string& strAddress, const string& strName) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(make_pair(string("name"), strAddress), strName); } @@ -38,37 +42,37 @@ bool CWalletDB::EraseName(const string& strAddress) { // This should only be used for sending addresses, never for receiving addresses, // receiving addresses must always have an address book entry if they're not change return. - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(make_pair(string("name"), strAddress)); } bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(make_pair(string("purpose"), strAddress), strPurpose); } bool CWalletDB::ErasePurpose(const string& strPurpose) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(make_pair(string("purpose"), strPurpose)); } bool CWalletDB::WriteTx(const CWalletTx& wtx) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx); } bool CWalletDB::EraseTx(uint256 hash) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("tx"), hash)); } bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) @@ -88,7 +92,7 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, const CKeyMetadata &keyMeta) { const bool fEraseUnencryptedKey = true; - nWalletDBUpdated++; + nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) @@ -106,31 +110,35 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false); } -bool CWalletDB::WriteWatchOnly(const CScript &dest) +bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; + if (!Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta)) + return false; return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); } bool CWalletDB::EraseWatchOnly(const CScript &dest) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; + if (!Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)))) + return false; return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest))); } bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan return Write(std::string("bestblock_nomerkle"), locator); } @@ -143,13 +151,13 @@ bool CWalletDB::ReadBestBlock(CBlockLocator& locator) bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("orderposnext"), nOrderPosNext); } bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("defaultkey"), vchPubKey); } @@ -160,13 +168,13 @@ bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("pool"), nPool), keypool); } bool CWalletDB::ErasePool(int64_t nPool) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("pool"), nPool)); } @@ -255,6 +263,7 @@ class CWalletScanState { public: unsigned int nKeys; unsigned int nCKeys; + unsigned int nWatchKeys; unsigned int nKeyMeta; bool fIsEncrypted; bool fAnyUnordered; @@ -262,7 +271,7 @@ public: vector<uint256> vWalletUpgrade; CWalletScanState() { - nKeys = nCKeys = nKeyMeta = 0; + nKeys = nCKeys = nWatchKeys = nKeyMeta = 0; fIsEncrypted = false; fAnyUnordered = false; nFileVersion = 0; @@ -344,16 +353,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "watchs") { + wss.nWatchKeys++; CScript script; ssKey >> *(CScriptBase*)(&script); char fYes; ssValue >> fYes; if (fYes == '1') pwallet->LoadWatchOnly(script); - - // Watch-only addresses have no birthday information for now, - // so set the wallet birthday to the beginning of time. - pwallet->nTimeFirstKey = 1; } else if (strType == "key" || strType == "wkey") { @@ -454,20 +460,27 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } wss.fIsEncrypted = true; } - else if (strType == "keymeta") + else if (strType == "keymeta" || strType == "watchmeta") { - CPubKey vchPubKey; - ssKey >> vchPubKey; + CTxDestination keyID; + if (strType == "keymeta") + { + CPubKey vchPubKey; + ssKey >> vchPubKey; + keyID = vchPubKey.GetID(); + } + else if (strType == "watchmeta") + { + CScript script; + ssKey >> *(CScriptBase*)(&script); + keyID = CScriptID(script); + } + CKeyMetadata keyMeta; ssValue >> keyMeta; wss.nKeyMeta++; - pwallet->LoadKeyMetadata(vchPubKey, keyMeta); - - // find earliest key creation time, as wallet birthday - if (!pwallet->nTimeFirstKey || - (keyMeta.nCreateTime < pwallet->nTimeFirstKey)) - pwallet->nTimeFirstKey = keyMeta.nCreateTime; + pwallet->LoadKeyMetadata(keyID, keyMeta); } else if (strType == "defaultkey") { @@ -546,8 +559,8 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; + LOCK(pwallet->cs_wallet); try { - LOCK(pwallet->cs_wallet); int nMinVersion = 0; if (Read((string)"minversion", nMinVersion)) { @@ -621,8 +634,8 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); // nTimeFirstKey is only reliable if all keys have metadata - if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta) - pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value' + if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) + pwallet->UpdateTimeFirstKey(1); BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade) WriteTx(pwallet->mapWallet[hash]); @@ -780,20 +793,20 @@ void ThreadFlushWalletDB() if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) return; - unsigned int nLastSeen = nWalletDBUpdated; - unsigned int nLastFlushed = nWalletDBUpdated; + unsigned int nLastSeen = CWalletDB::GetUpdateCounter(); + unsigned int nLastFlushed = CWalletDB::GetUpdateCounter(); int64_t nLastWalletUpdate = GetTime(); while (true) { MilliSleep(500); - if (nLastSeen != nWalletDBUpdated) + if (nLastSeen != CWalletDB::GetUpdateCounter()) { - nLastSeen = nWalletDBUpdated; + nLastSeen = CWalletDB::GetUpdateCounter(); nLastWalletUpdate = GetTime(); } - if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) + if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2) { TRY_LOCK(bitdb.cs_db,lockDb); if (lockDb) @@ -815,7 +828,7 @@ void ThreadFlushWalletDB() if (_mi != bitdb.mapFileUseCount.end()) { LogPrint("db", "Flushing %s\n", strFile); - nLastFlushed = nWalletDBUpdated; + nLastFlushed = CWalletDB::GetUpdateCounter(); int64_t nStart = GetTimeMillis(); // Flush wallet file so it's self contained @@ -922,19 +935,29 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename) bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); } bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } bool CWalletDB::WriteHDChain(const CHDChain& chain) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("hdchain"), chain); } + +void CWalletDB::IncrementUpdateCounter() +{ + nWalletDBUpdateCounter++; +} + +unsigned int CWalletDB::GetUpdateCounter() +{ + return nWalletDBUpdateCounter; +} diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index b9db55baa4..c7c65465df 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -135,7 +135,7 @@ public: bool WriteCScript(const uint160& hash, const CScript& redeemScript); - bool WriteWatchOnly(const CScript &script); + bool WriteWatchOnly(const CScript &script, const CKeyMetadata &keymeta); bool EraseWatchOnly(const CScript &script); bool WriteBestBlock(const CBlockLocator& locator); @@ -176,10 +176,11 @@ public: //! write the hdchain model (external chain child index counter) bool WriteHDChain(const CHDChain& chain); + static void IncrementUpdateCounter(); + static unsigned int GetUpdateCounter(); private: CWalletDB(const CWalletDB&); void operator=(const CWalletDB&); - }; void ThreadFlushWalletDB(); |