From e8dfb32ba16b580453766a156a26a3998af4f95d Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 31 Mar 2021 21:49:23 +0200 Subject: [PATCH 01/10] Move birthday optimization out of ScanForWalletTransactions >>> backports bitcoin@ccf84bb9c10b4397f1a2aed6cf83fa0172c5cf7f This change has no effect on wallet behavior. On wallet startup, the transaction scan avoids reading any blocks with timestamps older than the wallet birthday (less than nTimeFirstKey - TIMESTAMP_WINDOW). This block skipping code currently resides in CWallet::ScanForWalletTransactions but it doesn't really belong there because it makes the implementation unnecessarily fragile and hard to understand, and it never has any effect except at startup (because all other callers do their rescans based on timestamps other than, but always greater or equal to, nTimeFirstKey). --- src/wallet/wallet.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index dc2b4ee3a818..efb2d7d8c56c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1830,13 +1830,6 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock fScanningWallet = true; CBlockIndex* pindex = pindexStart; - { - LOCK(cs_main); - // no need to read and scan block, if block was created before - // our wallet birthday (as adjusted for block time variability) - while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - TIMESTAMP_WINDOW))) - pindex = chainActive.Next(pindex); - } { ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup @@ -4324,6 +4317,13 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { uiInterface.InitMessage(_("Rescanning...")); LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); + + // no need to read and scan block, if block was created before + // our wallet birthday (as adjusted for block time variability) + while (pindexRescan && walletInstance->nTimeFirstKey && + pindexRescan->GetBlockTime() < (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW)) { + pindexRescan = chainActive.Next(pindexRescan); + } const int64_t nWalletRescanTime = GetTimeMillis(); if (walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, true, true) != nullptr) { UIError(_("Shutdown requested over the txs scan. Exiting.")); From 2d84beea841aafd6e504fc4456ea40b8b9e61ea1 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 2 Mar 2017 15:24:50 -0500 Subject: [PATCH 02/10] Add RescanFromTime method and use from rpcdump No change in behavior. --- src/wallet/rpcdump.cpp | 43 ++++++++++++----------------------------- src/wallet/wallet.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++ src/wallet/wallet.h | 3 +++ 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d739f91e6615..f7ee3efdb854 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -139,12 +139,7 @@ UniValue importprivkey(const JSONRPCRequest& request) pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' if (fRescan) { - CBlockIndex *pindex = chainActive.Genesis(); - if (fStakingAddress && !Params().IsRegTestNet()) { - // cold staking was activated after nBlockTimeProtocolV2 (PIVX v4.0). No need to scan the whole chain - pindex = chainActive[Params().GetConsensus().vUpgrades[Consensus::UPGRADE_V4_0].nActivationHeight]; - } - pwalletMain->ScanForWalletTransactions(pindex, nullptr, true); + pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); } } @@ -246,13 +241,12 @@ UniValue importaddress(const JSONRPCRequest& request) } else if (IsHex(request.params[0].get_str())) { std::vector data(ParseHex(request.params[0].get_str())); ImportScript(CScript(data.begin(), data.end()), strLabel, fP2SH); - } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address or script"); } if (fRescan) { - pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), nullptr, true); + pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); pwalletMain->ReacceptWalletTransactions(); } @@ -296,7 +290,7 @@ UniValue importpubkey(const JSONRPCRequest& request) ImportScript(GetScriptForRawPubKey(pubKey), strLabel, false); if (fRescan) { - pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), nullptr, true); + pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); pwalletMain->ReacceptWalletTransactions(); } @@ -411,16 +405,8 @@ UniValue importwallet(const JSONRPCRequest& request) } file.close(); pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI - - CBlockIndex* pindex = chainActive.Tip(); - while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - TIMESTAMP_WINDOW) - pindex = pindex->pprev; - - if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey) - pwalletMain->nTimeFirstKey = nTimeBegin; - - LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); - pwalletMain->ScanForWalletTransactions(pindex); + pwalletMain->UpdateTimeFirstKey(nTimeBegin); + pwalletMain->RescanFromTime(nTimeBegin - TIMESTAMP_WINDOW, false /* update */); pwalletMain->MarkDirty(); if (!fGood) @@ -1027,17 +1013,12 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } } - if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTimeMax()) { - CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(std::max(nLowestTimestamp - TIMESTAMP_WINDOW, 0)) - : chainActive.Genesis(); - CBlockIndex* scanFailed = nullptr; - if (pindex) { - scanFailed = pwalletMain->ScanForWalletTransactions(pindex, nullptr, true); - pwalletMain->ReacceptWalletTransactions(); - } + if (fRescan && fRunScan && requests.size()) { + int64_t scannedTime = pwalletMain->RescanFromTime(nLowestTimestamp - TIMESTAMP_WINDOW, true /* update */); + pwalletMain->ReacceptWalletTransactions(); - if (scanFailed) { - const std::vector& results = response.getValues(); + if (scannedTime > nLowestTimestamp - TIMESTAMP_WINDOW) { + std::vector results = response.getValues(); response.clear(); response.setArray(); size_t i = 0; @@ -1046,7 +1027,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) // range, or if the import result already has an error set, let // the result stand unmodified. Otherwise replace the result // with an error message. - if (GetImportTimestamp(request, now) - TIMESTAMP_WINDOW >= scanFailed->GetBlockTimeMax() || results.at(i).exists("error")) { + if (scannedTime <= GetImportTimestamp(request, now) - TIMESTAMP_WINDOW || results.at(i).exists("error")) { response.push_back(results.at(i)); } else { UniValue result = UniValue(UniValue::VOBJ); @@ -1059,7 +1040,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) "caused by pruning or data corruption (see pivxd log for details) and could " "be dealt with by downloading and rescanning the relevant blocks (see -reindex " "and -rescan options).", - GetImportTimestamp(request, now), scanFailed->GetBlockTimeMax(), TIMESTAMP_WINDOW))); + GetImportTimestamp(request, now), scannedTime - 1, TIMESTAMP_WINDOW))); response.push_back(std::move(result)); } ++i; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index efb2d7d8c56c..a95f358cab1d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -252,6 +252,22 @@ bool CWallet::LoadCryptedKey(const CPubKey& vchPubKey, const std::vectornHeight + 1 : 0); + + if (startBlock) { + const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update); + if (failedBlock) { + return failedBlock->GetBlockTimeMax() + 1; + } + } + return startTime; +} + /** * Scan the block chain (starting in pindexStart) for transactions * from or to us. If fUpdate is true, found transactions that already diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index a0079e58f511..f98d46c52f4a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -85,6 +85,7 @@ static const unsigned int DEFAULT_CREATEWALLETBACKUPS = 10; static const bool DEFAULT_DISABLE_WALLET = false; extern const char * DEFAULT_WALLET_DAT; +static const int64_t TIMESTAMP_MIN = 0; class CAddressBookIterator; class CCoinControl; @@ -906,6 +907,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool LoadKeyMetadata(const CPubKey& pubkey, const CKeyMetadata& metadata); bool LoadMinVersion(int nVersion); + 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& vchCryptedSecret) override; @@ -958,6 +960,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool Upgrade(std::string& error, const int& prevVersion); bool ActivateSaplingWallet(bool memOnly = false); + int64_t RescanFromTime(int64_t startTime, bool update); CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop = nullptr, bool fUpdate = false, bool fromStartup = false); void TransactionRemovedFromMempool(const CTransactionRef &ptx) override; void ReacceptWalletTransactions(bool fFirstLoad = false); From fb1276794755b9b2654968f9a962d577cd58c12d Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 22 Jun 2017 17:14:40 -0400 Subject: [PATCH 03/10] Make CWallet::RescanFromTime comment less ambiguous --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a95f358cab1d..e4af924da909 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1827,7 +1827,7 @@ bool CWallet::Upgrade(std::string& error, const int& prevVersion) * creation time minus TIMESTAMP_WINDOW. * * @return Earliest timestamp that could be successfully scanned from. Timestamp - * returned may be higher than startTime if some blocks could not be read. + * returned will be higher than startTime if relevant blocks could not be read. */ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) { From 69a7288a4edce5fa5f0919adf36f11cf53a03f2b Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 22 Jun 2017 17:16:24 -0400 Subject: [PATCH 04/10] Handle TIMESTAMP_WINDOW within CWallet::RescanFromTime This way CWallet::RescanFromTime callers don't need to subtract TIMESTAMP_WINDOW themselves. This is pure refactoring, there is no change in behavior. --- src/wallet/rpcdump.cpp | 12 ++++++------ src/wallet/wallet.cpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index f7ee3efdb854..6e7ae437ceee 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -406,7 +406,7 @@ UniValue importwallet(const JSONRPCRequest& request) file.close(); pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI pwalletMain->UpdateTimeFirstKey(nTimeBegin); - pwalletMain->RescanFromTime(nTimeBegin - TIMESTAMP_WINDOW, false /* update */); + pwalletMain->RescanFromTime(nTimeBegin, false /* update */); pwalletMain->MarkDirty(); if (!fGood) @@ -1014,10 +1014,10 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } if (fRescan && fRunScan && requests.size()) { - int64_t scannedTime = pwalletMain->RescanFromTime(nLowestTimestamp - TIMESTAMP_WINDOW, true /* update */); + int64_t scannedTime = pwalletMain->RescanFromTime(nLowestTimestamp, true /* update */); pwalletMain->ReacceptWalletTransactions(); - if (scannedTime > nLowestTimestamp - TIMESTAMP_WINDOW) { + if (scannedTime > nLowestTimestamp) { std::vector results = response.getValues(); response.clear(); response.setArray(); @@ -1027,7 +1027,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) // range, or if the import result already has an error set, let // the result stand unmodified. Otherwise replace the result // with an error message. - if (scannedTime <= GetImportTimestamp(request, now) - TIMESTAMP_WINDOW || results.at(i).exists("error")) { + if (scannedTime <= GetImportTimestamp(request, now) || results.at(i).exists("error")) { response.push_back(results.at(i)); } else { UniValue result = UniValue(UniValue::VOBJ); @@ -1037,10 +1037,10 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) "block from time %d, which is after or within %d seconds of key creation, and " "could contain transactions pertaining to the key. As a result, transactions " "and coins using this key may not appear in the wallet. This error could be " - "caused by pruning or data corruption (see pivxd log for details) and could " + "caused by pruning or data corruption (see bitcoind log for details) and could " "be dealt with by downloading and rescanning the relevant blocks (see -reindex " "and -rescan options).", - GetImportTimestamp(request, now), scannedTime - 1, TIMESTAMP_WINDOW))); + GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW))); response.push_back(std::move(result)); } ++i; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e4af924da909..5c02585986e2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1824,7 +1824,7 @@ bool CWallet::Upgrade(std::string& error, const int& prevVersion) /** * Scan active chain for relevant transactions after importing keys. This should * be called whenever new keys are added to the wallet, with the oldest key - * creation time minus TIMESTAMP_WINDOW. + * creation time. * * @return Earliest timestamp that could be successfully scanned from. Timestamp * returned will be higher than startTime if relevant blocks could not be read. @@ -1837,13 +1837,13 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) // Find starting block. May be null if nCreateTime is greater than the // highest blockchain timestamp, in which case there is nothing that needs // to be scanned. - CBlockIndex* const startBlock = chainActive.FindEarliestAtLeast(startTime); + CBlockIndex* const startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); if (startBlock) { const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update); if (failedBlock) { - return failedBlock->GetBlockTimeMax() + 1; + return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; } } return startTime; From c3fc22d510decbc6791ff97f551e7656d7baf5e0 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Thu, 1 Apr 2021 02:18:36 +0200 Subject: [PATCH 05/10] Avoid pemanent cs_main/cs_wallet lock during wallet rescans >>> backports bitcoin@8d0b610fe8d0916404aa9158c525b80b1c581c0e --- src/wallet/rpcdump.cpp | 263 ++++++++++++++++++++------------------- src/wallet/rpcwallet.cpp | 39 +++--- src/wallet/wallet.cpp | 92 +++++++------- 3 files changed, 205 insertions(+), 189 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 6e7ae437ceee..02707ecc5f87 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -130,17 +130,15 @@ UniValue importprivkey(const JSONRPCRequest& request) if (pwalletMain->HaveKey(vchAddress)) return NullUniValue; + // whenever a key is imported, we need to scan the whole chain + pwalletMain->UpdateTimeFirstKey(1); pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; if (!pwalletMain->AddKeyPubKey(key, pubkey)) 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' - - if (fRescan) { - pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); - } + } + if (fRescan) { + pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); } return NullUniValue; @@ -226,25 +224,26 @@ UniValue importaddress(const JSONRPCRequest& request) // Whether to import a p2sh version, too const bool fP2SH = (request.params.size() > 3 ? request.params[3].get_bool() : false); - LOCK2(cs_main, pwalletMain->cs_wallet); + { + LOCK2(cs_main, pwalletMain->cs_wallet); - bool isStakingAddress = false; - CTxDestination dest = DecodeDestination(request.params[0].get_str(), isStakingAddress); + bool isStakingAddress = false; + CTxDestination dest = DecodeDestination(request.params[0].get_str(), isStakingAddress); - if (IsValidDestination(dest)) { - if (fP2SH) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead"); - ImportAddress(dest, strLabel, isStakingAddress ? - AddressBook::AddressBookPurpose::COLD_STAKING : - AddressBook::AddressBookPurpose::RECEIVE); - - } else if (IsHex(request.params[0].get_str())) { - std::vector data(ParseHex(request.params[0].get_str())); - ImportScript(CScript(data.begin(), data.end()), strLabel, fP2SH); - } else { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address or script"); - } + if (IsValidDestination(dest)) { + if (fP2SH) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead"); + ImportAddress(dest, strLabel, isStakingAddress ? + AddressBook::AddressBookPurpose::COLD_STAKING : + AddressBook::AddressBookPurpose::RECEIVE); + } else if (IsHex(request.params[0].get_str())) { + std::vector data(ParseHex(request.params[0].get_str())); + ImportScript(CScript(data.begin(), data.end()), strLabel, fP2SH); + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address or script"); + } + } if (fRescan) { pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); pwalletMain->ReacceptWalletTransactions(); @@ -284,11 +283,12 @@ UniValue importpubkey(const JSONRPCRequest& request) if (!pubKey.IsFullyValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); - LOCK2(cs_main, pwalletMain->cs_wallet); - - ImportAddress(pubKey.GetID(), strLabel, "receive"); - ImportScript(GetScriptForRawPubKey(pubKey), strLabel, false); + { + LOCK2(cs_main, pwalletMain->cs_wallet); + ImportAddress(pubKey.GetID(), strLabel, "receive"); + ImportScript(GetScriptForRawPubKey(pubKey), strLabel, false); + } if (fRescan) { pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); pwalletMain->ReacceptWalletTransactions(); @@ -317,9 +317,6 @@ UniValue importwallet(const JSONRPCRequest& request) "\nImport using the json rpc call\n" + HelpExampleRpc("importwallet", "\"test\"")); - LOCK2(cs_main, pwalletMain->cs_wallet); - - EnsureWalletIsUnlocked(); std::ifstream file; file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate); @@ -329,83 +326,87 @@ UniValue importwallet(const JSONRPCRequest& request) int64_t nTimeBegin = chainActive.Tip()->GetBlockTime(); bool fGood = true; + { + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); - int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); - file.seekg(0, file.beg); - - pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI - while (file.good()) { - pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); - std::string line; - std::getline(file, line); - if (line.empty() || line[0] == '#') - continue; - - std::vector vstr; - boost::split(vstr, line, boost::is_any_of(" ")); - if (vstr.size() < 2) - continue; - - // Sapling keys - // Let's see if the address is a valid PIVX spending key - if (pwalletMain->HasSaplingSPKM()) { - libzcash::SpendingKey spendingkey = KeyIO::DecodeSpendingKey(vstr[0]); - int64_t nTime = DecodeDumpTime(vstr[1]); - if (IsValidSpendingKey(spendingkey)) { - libzcash::SaplingExtendedSpendingKey saplingSpendingKey = *boost::get(&spendingkey); - auto addResult = pwalletMain->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet( - Params().GetConsensus(), saplingSpendingKey, nTime); - if (addResult == KeyAlreadyExists) { - LogPrint(BCLog::SAPLING, "Skipping import of shielded addr (key already present)\n"); - } else if (addResult == KeyNotAdded) { - // Something went wrong - fGood = false; - } + int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); + file.seekg(0, file.beg); + + pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI + while (file.good()) { + pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); + std::string line; + std::getline(file, line); + if (line.empty() || line[0] == '#') + continue; + + std::vector vstr; + boost::split(vstr, line, boost::is_any_of(" ")); + if (vstr.size() < 2) continue; + + // Sapling keys + // Let's see if the address is a valid PIVX spending key + if (pwalletMain->HasSaplingSPKM()) { + libzcash::SpendingKey spendingkey = KeyIO::DecodeSpendingKey(vstr[0]); + int64_t nTime = DecodeDumpTime(vstr[1]); + if (IsValidSpendingKey(spendingkey)) { + libzcash::SaplingExtendedSpendingKey saplingSpendingKey = *boost::get(&spendingkey); + auto addResult = pwalletMain->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet( + Params().GetConsensus(), saplingSpendingKey, nTime); + if (addResult == KeyAlreadyExists) { + LogPrint(BCLog::SAPLING, "Skipping import of shielded addr (key already present)\n"); + } else if (addResult == KeyNotAdded) { + // Something went wrong + fGood = false; + } + continue; + } } - } - CKey key = DecodeSecret(vstr[0]); - if (!key.IsValid()) - continue; - CPubKey pubkey = key.GetPubKey(); - assert(key.VerifyPubKey(pubkey)); - CKeyID keyid = pubkey.GetID(); - if (pwalletMain->HaveKey(keyid)) { - LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid)); - continue; - } - int64_t nTime = DecodeDumpTime(vstr[1]); - std::string strLabel; - bool fLabel = true; - for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { - const std::string& type = vstr[nStr]; - if (boost::algorithm::starts_with(type, "#")) - break; - if (type == "change=1") - fLabel = false; - else if (type == "reserve=1") - fLabel = false; - else if (type == "hdseed") - fLabel = false; - if (boost::algorithm::starts_with(type, "label=")) { - strLabel = DecodeDumpString(vstr[nStr].substr(6)); - fLabel = true; + CKey key = DecodeSecret(vstr[0]); + if (!key.IsValid()) + continue; + CPubKey pubkey = key.GetPubKey(); + assert(key.VerifyPubKey(pubkey)); + CKeyID keyid = pubkey.GetID(); + if (pwalletMain->HaveKey(keyid)) { + LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid)); + continue; } + int64_t nTime = DecodeDumpTime(vstr[1]); + std::string strLabel; + bool fLabel = true; + for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { + const std::string& type = vstr[nStr]; + if (boost::algorithm::starts_with(type, "#")) + break; + if (type == "change=1") + fLabel = false; + else if (type == "reserve=1") + fLabel = false; + else if (type == "hdseed") + fLabel = false; + if (boost::algorithm::starts_with(type, "label=")) { + strLabel = DecodeDumpString(vstr[nStr].substr(6)); + fLabel = true; + } + } + LogPrintf("Importing %s...\n", EncodeDestination(keyid)); + if (!pwalletMain->AddKeyPubKey(key, pubkey)) { + fGood = false; + continue; + } + pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime; + if (fLabel) // TODO: This is not entirely true.. needs to be reviewed properly. + pwalletMain->SetAddressBook(keyid, strLabel, AddressBook::AddressBookPurpose::RECEIVE); + nTimeBegin = std::min(nTimeBegin, nTime); } - LogPrintf("Importing %s...\n", EncodeDestination(keyid)); - if (!pwalletMain->AddKeyPubKey(key, pubkey)) { - fGood = false; - continue; - } - pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime; - if (fLabel) // TODO: This is not entirely true.. needs to be reviewed properly. - pwalletMain->SetAddressBook(keyid, strLabel, AddressBook::AddressBookPurpose::RECEIVE); - nTimeBegin = std::min(nTimeBegin, nTime); + file.close(); + pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI + pwalletMain->UpdateTimeFirstKey(nTimeBegin); } - file.close(); - pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI - pwalletMain->UpdateTimeFirstKey(nTimeBegin); pwalletMain->RescanFromTime(nTimeBegin, false /* update */); pwalletMain->MarkDirty(); @@ -972,47 +973,49 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } } - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); - - // Verify all timestamps are present before importing any keys. - int64_t now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0; - for (const UniValue& data : requests.getValues()) { - GetImportTimestamp(data, now); - } - + int64_t now = 0; bool fRunScan = false; - const int64_t minimumTimestamp = 1; int64_t nLowestTimestamp = 0; - if (fRescan && chainActive.Tip()) { - nLowestTimestamp = chainActive.Tip()->GetBlockTime(); - } else { - fRescan = false; - } - UniValue response(UniValue::VARR); + { + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); - for (const UniValue& data: requests.getValues()) { - const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp); - const UniValue result = processImport(data, timestamp); - response.push_back(result); - - if (!fRescan) { - continue; + // Verify all timestamps are present before importing any keys. + int64_t now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0; + for (const UniValue& data : requests.getValues()) { + GetImportTimestamp(data, now); } - // If at least one request was successful then allow rescan. - if (result["success"].get_bool()) { - fRunScan = true; + const int64_t minimumTimestamp = 1; + + if (fRescan && chainActive.Tip()) { + nLowestTimestamp = chainActive.Tip()->GetBlockTime(); + } else { + fRescan = false; } - // Get the lowest timestamp. - if (timestamp < nLowestTimestamp) { - nLowestTimestamp = timestamp; + for (const UniValue& data: requests.getValues()) { + const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp); + const UniValue result = processImport(data, timestamp); + response.push_back(result); + + if (!fRescan) { + continue; + } + + // If at least one request was successful then allow rescan. + if (result["success"].get_bool()) { + fRunScan = true; + } + + // Get the lowest timestamp. + if (timestamp < nLowestTimestamp) { + nLowestTimestamp = timestamp; + } } } - if (fRescan && fRunScan && requests.size()) { int64_t scannedTime = pwalletMain->RescanFromTime(nLowestTimestamp, true /* update */); pwalletMain->ReacceptWalletTransactions(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 322e2257841a..e600b952deb2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4207,24 +4207,33 @@ UniValue rescanblockchain(const JSONRPCRequest& request) } EnsureWallet(); - LOCK2(cs_main, pwalletMain->cs_wallet); + if (pwalletMain->IsScanning()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); + } - CBlockIndex *pindexStart = chainActive.Genesis(); + CBlockIndex *pindexStart = nullptr; CBlockIndex *pindexStop = nullptr; - if (!request.params[0].isNull()) { - pindexStart = chainActive[request.params[0].get_int()]; - if (!pindexStart) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height"); + CBlockIndex *pChainTip = nullptr; + { + LOCK(cs_main); + pindexStart = chainActive.Genesis(); + pChainTip = chainActive.Tip(); + + if (!request.params[0].isNull()) { + pindexStart = chainActive[request.params[0].get_int()]; + if (!pindexStart) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height"); + } } - } - if (!request.params[1].isNull()) { - pindexStop = chainActive[request.params[1].get_int()]; - if (!pindexStop) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height"); - } - else if (pindexStop->nHeight < pindexStart->nHeight) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater then start_height"); + if (!request.params[1].isNull()) { + pindexStop = chainActive[request.params[1].get_int()]; + if (!pindexStop) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height"); + } + else if (pindexStop->nHeight < pindexStart->nHeight) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater then start_height"); + } } } @@ -4234,7 +4243,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request) throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted."); } // if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex - stopBlock = pindexStop ? pindexStop : chainActive.Tip(); + stopBlock = pindexStop ? pindexStop : pChainTip; } else { throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files."); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 5c02585986e2..5da453490f12 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1831,14 +1831,15 @@ bool CWallet::Upgrade(std::string& error, const int& prevVersion) */ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) { - AssertLockHeld(cs_main); - AssertLockHeld(cs_wallet); - // Find starting block. May be null if nCreateTime is greater than the // highest blockchain timestamp, in which case there is nothing that needs // to be scanned. - CBlockIndex* const startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); - LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); + CBlockIndex* startBlock = nullptr; + { + LOCK(cs_main); + startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); + LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); + } if (startBlock) { const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update); @@ -1854,41 +1855,45 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) * from or to us. If fUpdate is true, found transactions that already * exist in the wallet will be updated. * - * If pindexStop is not a nullptr, the scan will stop at the block-index - * defined by pindexStop - * * Returns null if scan was successful. Otherwise, if a complete rescan was not * possible (due to pruning or corruption), returns pointer to the most recent * block that could not be scanned. + * + * If pindexStop is not a nullptr, the scan will stop at the block-index + * defined by pindexStop + * + * Caller needs to make sure pindexStop (and the optional pindexStart) are on + * the main chain after to the addition of any new keys you want to detect + * transactions for. */ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate, bool fromStartup) { - CBlockIndex* ret = nullptr; int64_t nNow = GetTime(); if (pindexStop) { assert(pindexStop->nHeight >= pindexStart->nHeight); } - fAbortRescan = false; - fScanningWallet = true; - CBlockIndex* pindex = pindexStart; - + CBlockIndex* ret = nullptr; { ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup - const double dProgressStart = Checkpoints::GuessVerificationProgress(pindex, false); - const CBlockIndex* tip = nullptr; - double dProgressTip = 0.0; - std::vector myTxHashes; + CBlockIndex* tip = nullptr; + double dProgressStart; + double dProgressTip; + { + LOCK(cs_main); + tip = chainActive.Tip(); + dProgressStart = Checkpoints::GuessVerificationProgress(pindex, false); + dProgressTip = Checkpoints::GuessVerificationProgress(tip, false); + } - double gvp = dProgressStart; + std::vector myTxHashes; while (pindex && !fAbortRescan) { - gvp = Checkpoints::GuessVerificationProgress(pindex, false); + double gvp = 0; if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) { - ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int) ((gvp - dProgressStart) / - (dProgressTip - dProgressStart) * - 100)))); + gvp = WITH_LOCK(cs_main, return Checkpoints::GuessVerificationProgress(pindex, false); ); + ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((gvp - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); } if (GetTime() >= nNow + 60) { nNow = GetTime(); @@ -1899,26 +1904,15 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock } CBlock block; - if (!ReadBlockFromDisk(block, pindex)) { - LogPrintf("Unable to read block %d (%s) from disk.", pindex->nHeight, pindex->GetBlockHash().ToString()); - ret = pindex; - break; // failed, try to save txs and return the failed index - } - - { + bool readRet = WITH_LOCK(cs_main, return ReadBlockFromDisk(block, pindex); ); + if (readRet) { LOCK2(cs_main, cs_wallet); - if (tip != chainActive.Tip()) { - tip = chainActive.Tip(); - // in case the tip has changed, update progress max - dProgressTip = Checkpoints::GuessVerificationProgress(tip, false); - } - - if (!chainActive.Contains(pindex)) { - // Abort scan if current block is no longer active, to prevent - // marking transactions as coming from the wrong block. - ret = pindex; - break; - } + if (pindex && !chainActive.Contains(pindex)) { + // Abort scan if current block is no longer active, to prevent + // marking transactions as coming from the wrong block. + ret = pindex; + break; + } for (int posInBlock = 0; posInBlock < (int) block.vtx.size(); posInBlock++) { const auto& tx = block.vtx[posInBlock]; CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, pindex->nHeight, pindex->GetBlockHash(), posInBlock); @@ -1938,10 +1932,20 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock ChainTipAdded(pindex, &block, saplingTree); } } - if (pindex == pindexStop) { - break; - } + } else { + ret = pindex; + } + if (pindex == pindexStop) { + break; + } + { + LOCK(cs_main); pindex = chainActive.Next(pindex); + if (tip != chainActive.Tip()) { + tip = chainActive.Tip(); + // in case the tip has changed, update progress max + dProgressTip = Checkpoints::GuessVerificationProgress(tip, false); + } } } From 4d056725a271f47824f609d93439d4f012d26852 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 8 Dec 2017 11:07:37 -1000 Subject: [PATCH 06/10] Add RAII wallet rescan reserver --- src/wallet/rpcdump.cpp | 24 ++++++++++++++++++++++-- src/wallet/rpcwallet.cpp | 3 ++- src/wallet/wallet.h | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 02707ecc5f87..6a800db77d6e 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -221,6 +221,12 @@ UniValue importaddress(const JSONRPCRequest& request) const std::string strLabel = (request.params.size() > 1 ? request.params[1].get_str() : ""); // Whether to perform rescan after import const bool fRescan = (request.params.size() > 2 ? request.params[2].get_bool() : true); + + WalletRescanReserver reserver(pwalletMain); + if (fRescan && !reserver.reserve()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); + } + // Whether to import a p2sh version, too const bool fP2SH = (request.params.size() > 3 ? request.params[3].get_bool() : false); @@ -276,6 +282,11 @@ UniValue importpubkey(const JSONRPCRequest& request) // Whether to perform rescan after import const bool fRescan = (request.params.size() > 2 ? request.params[2].get_bool() : true); + WalletRescanReserver reserver(pwalletMain); + if (fRescan && !reserver.reserve()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); + } + if (!IsHex(request.params[0].get_str())) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string"); std::vector data(ParseHex(request.params[0].get_str())); @@ -317,19 +328,23 @@ UniValue importwallet(const JSONRPCRequest& request) "\nImport using the json rpc call\n" + HelpExampleRpc("importwallet", "\"test\"")); - std::ifstream file; file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); - int64_t nTimeBegin = chainActive.Tip()->GetBlockTime(); + WalletRescanReserver reserver(pwalletMain); + if (!reserver.reserve()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); + } + int64_t nTimeBegin = 0; bool fGood = true; { LOCK2(cs_main, pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); + nTimeBegin = chainActive.Tip()->GetBlockTime(); int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); file.seekg(0, file.beg); @@ -973,6 +988,11 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } } + WalletRescanReserver reserver(pwalletMain); + if (fRescan && !reserver.reserve()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); + } + int64_t now = 0; bool fRunScan = false; int64_t nLowestTimestamp = 0; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e600b952deb2..0f8e588965f1 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4207,7 +4207,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request) } EnsureWallet(); - if (pwalletMain->IsScanning()) { + WalletRescanReserver reserver(pwalletMain); + if (!reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f98d46c52f4a..4663811f8acc 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -44,7 +44,8 @@ #include #include -extern CWallet* pwalletMain; +typedef CWallet* CWalletRef; +extern CWalletRef pwalletMain; /** * Settings @@ -564,6 +565,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface static std::atomic fFlushScheduled; std::atomic fAbortRescan; std::atomic fScanningWallet; + std::mutex mutexScanning; + friend class WalletRescanReserver; + //! keeps track of whether Unlock has run a thorough check before bool fDecryptionThoroughlyChecked{false}; @@ -1229,4 +1233,34 @@ class CStakeableOutput : public COutput }; +/** RAII object to check and reserve a wallet rescan */ +class WalletRescanReserver +{ +private: + CWalletRef m_wallet; + bool m_could_reserve; +public: + explicit WalletRescanReserver(CWalletRef w) : m_wallet(w), m_could_reserve(false) {} + + bool reserve() + { + assert(!m_could_reserve); + std::lock_guard lock(m_wallet->mutexScanning); + if (m_wallet->fScanningWallet) { + return false; + } + m_wallet->fScanningWallet = true; + m_could_reserve = true; + return true; + } + + ~WalletRescanReserver() + { + std::lock_guard lock(m_wallet->mutexScanning); + if (m_could_reserve) { + m_wallet->fScanningWallet = false; + } + } +}; + #endif // BITCOIN_WALLET_H From 3e0166517b444e0000aa7140f6aeaea00ee54411 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Thu, 1 Apr 2021 03:24:23 +0200 Subject: [PATCH 07/10] Make sure WalletRescanReserver has successfully reserved the rescan >>> backports bitcoin@bc356b4268e222ac57d9e9297d2a986bb6e09de8 and adapt RPC importsaplingkey and importsaplingviewingkey --- .../pivx/settings/settingsbittoolwidget.cpp | 12 +- src/wallet/rpcdump.cpp | 166 ++++++++++-------- src/wallet/rpcwallet.cpp | 2 +- src/wallet/test/wallet_tests.cpp | 10 +- src/wallet/wallet.cpp | 21 ++- src/wallet/wallet.h | 14 +- 6 files changed, 137 insertions(+), 88 deletions(-) diff --git a/src/qt/pivx/settings/settingsbittoolwidget.cpp b/src/qt/pivx/settings/settingsbittoolwidget.cpp index 3fcad570f4bf..671962bf7ec6 100644 --- a/src/qt/pivx/settings/settingsbittoolwidget.cpp +++ b/src/qt/pivx/settings/settingsbittoolwidget.cpp @@ -271,6 +271,13 @@ void SettingsBitToolWidget::onDecryptClicked() void SettingsBitToolWidget::importAddressFromDecKey() { + // whenever a key is imported, we need to scan the whole chain + WalletRescanReserver reserver(pwalletMain); + if (!reserver.reserve()) { + ui->statusLabel_DEC->setStyleSheet("QLabel { color: red; }"); + ui->statusLabel_DEC->setText(tr("Wallet is currently rescanning. Abort existing rescan or wait.")); + return; + } WalletModel::UnlockContext ctx(walletModel->requestUnlock()); if (!ctx.isValid()) { ui->statusLabel_DEC->setStyleSheet("QLabel { color: red; }"); @@ -309,10 +316,7 @@ void SettingsBitToolWidget::importAddressFromDecKey() ui->statusLabel_DEC->setText(tr("Error adding key to the wallet")); return; } - - // whenever a key is imported, we need to scan the whole chain - pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' - pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), nullptr, true); + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true); } ui->statusLabel_DEC->setStyleSheet("QLabel { color: green; }"); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 6a800db77d6e..d89b7648bd04 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -108,6 +108,12 @@ UniValue importprivkey(const JSONRPCRequest& request) const std::string strSecret = request.params[0].get_str(); const std::string strLabel = (request.params.size() > 1 ? request.params[1].get_str() : ""); const bool fRescan = (request.params.size() > 2 ? request.params[2].get_bool() : true); + + WalletRescanReserver reserver(pwalletMain); + if (fRescan && !reserver.reserve()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); + } + const bool fStakingAddress = (request.params.size() > 3 ? request.params[3].get_bool() : false); CKey key = DecodeSecret(strSecret); @@ -138,7 +144,7 @@ UniValue importprivkey(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } if (fRescan) { - pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); + pwalletMain->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); } return NullUniValue; @@ -251,7 +257,7 @@ UniValue importaddress(const JSONRPCRequest& request) } } if (fRescan) { - pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); + pwalletMain->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); pwalletMain->ReacceptWalletTransactions(); } @@ -301,7 +307,7 @@ UniValue importpubkey(const JSONRPCRequest& request) ImportScript(GetScriptForRawPubKey(pubKey), strLabel, false); } if (fRescan) { - pwalletMain->RescanFromTime(TIMESTAMP_MIN, true /* update */); + pwalletMain->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); pwalletMain->ReacceptWalletTransactions(); } @@ -422,7 +428,7 @@ UniValue importwallet(const JSONRPCRequest& request) pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI pwalletMain->UpdateTimeFirstKey(nTimeBegin); } - pwalletMain->RescanFromTime(nTimeBegin, false /* update */); + pwalletMain->RescanFromTime(nTimeBegin, reserver, false /* update */); pwalletMain->MarkDirty(); if (!fGood) @@ -1037,7 +1043,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } } if (fRescan && fRunScan && requests.size()) { - int64_t scannedTime = pwalletMain->RescanFromTime(nLowestTimestamp, true /* update */); + int64_t scannedTime = pwalletMain->RescanFromTime(nLowestTimestamp, reserver, true /* update */); pwalletMain->ReacceptWalletTransactions(); if (scannedTime > nLowestTimestamp) { @@ -1139,9 +1145,6 @@ UniValue bip38decrypt(const JSONRPCRequest& request) HelpExampleCli("bip38decrypt", "\"encryptedkey\" \"mypassphrase\"") + HelpExampleRpc("bip38decrypt", "\"encryptedkey\" \"mypassphrase\"")); - LOCK2(cs_main, pwalletMain->cs_wallet); - - EnsureWalletIsUnlocked(); /** Collect private key and passphrase **/ std::string strKey = request.params[0].get_str(); @@ -1161,11 +1164,18 @@ UniValue bip38decrypt(const JSONRPCRequest& request) if (!key.IsValid()) throw JSONRPCError(RPC_WALLET_ERROR, "Private Key Not Valid"); + WalletRescanReserver reserver(pwalletMain); + if (!reserver.reserve()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); + } + CPubKey pubkey = key.GetPubKey(); assert(key.VerifyPubKey(pubkey)); result.pushKV("Address", EncodeDestination(pubkey.GetID())); CKeyID vchAddress = pubkey.GetID(); { + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); pwalletMain->MarkDirty(); pwalletMain->SetAddressBook(vchAddress, "", AddressBook::AddressBookPurpose::RECEIVE); @@ -1177,12 +1187,11 @@ UniValue bip38decrypt(const JSONRPCRequest& request) if (!pwalletMain->AddKeyPubKey(key, pubkey)) 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->ScanForWalletTransactions(chainActive.Genesis(), nullptr, true); } + // whenever a key is imported, we need to scan the whole chain + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true); + return result; } @@ -1221,8 +1230,6 @@ UniValue importsaplingkey(const JSONRPCRequest& request) ); EnsureWallet(); - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); // Whether to perform rescan after import bool fRescan = true; @@ -1241,40 +1248,49 @@ UniValue importsaplingkey(const JSONRPCRequest& request) } } - // Height to rescan from - int nRescanHeight = 0; - if (request.params.size() > 2) - nRescanHeight = request.params[2].get_int(); - if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - } - - std::string strSecret = request.params[0].get_str(); - auto spendingkey = KeyIO::DecodeSpendingKey(strSecret); - if (!IsValidSpendingKey(spendingkey)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); + WalletRescanReserver reserver(pwalletMain); + if (fRescan && !reserver.reserve()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } - libzcash::SaplingExtendedSpendingKey saplingSpendingKey = *boost::get(&spendingkey); UniValue result(UniValue::VOBJ); - result.pushKV("address", KeyIO::EncodePaymentAddress( saplingSpendingKey.DefaultAddress())); + CBlockIndex* pindexRescan{nullptr}; + { + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); - // Sapling support - auto addResult = pwalletMain->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet(Params().GetConsensus(), saplingSpendingKey, -1); - if (addResult == KeyAlreadyExists && fIgnoreExistingKey) { - return result; - } - pwalletMain->MarkDirty(); - if (addResult == KeyNotAdded) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet"); - } + // Height to rescan from + int nRescanHeight = 0; + if (request.params.size() > 2) + nRescanHeight = request.params[2].get_int(); + if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + } - // whenever a key is imported, we need to scan the whole chain - pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' + std::string strSecret = request.params[0].get_str(); + auto spendingkey = KeyIO::DecodeSpendingKey(strSecret); + if (!IsValidSpendingKey(spendingkey)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); + } + + libzcash::SaplingExtendedSpendingKey saplingSpendingKey = *boost::get(&spendingkey); + result.pushKV("address", KeyIO::EncodePaymentAddress( saplingSpendingKey.DefaultAddress())); + + // Sapling support + auto addResult = pwalletMain->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet(Params().GetConsensus(), saplingSpendingKey, -1); + if (addResult == KeyAlreadyExists && fIgnoreExistingKey) { + return result; + } + pwalletMain->MarkDirty(); + if (addResult == KeyNotAdded) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet"); + } + pindexRescan = chainActive[nRescanHeight]; + } // We want to scan for transactions and notes if (fRescan) { - pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], nullptr, true); + pwalletMain->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true); } return result; @@ -1313,9 +1329,6 @@ UniValue importsaplingviewingkey(const JSONRPCRequest& request) ); EnsureWallet(); - LOCK2(cs_main, pwalletMain->cs_wallet); - - EnsureWalletIsUnlocked(); // Whether to perform rescan after import bool fRescan = true; @@ -1334,40 +1347,53 @@ UniValue importsaplingviewingkey(const JSONRPCRequest& request) } } - // Height to rescan from - int nRescanHeight = 0; - if (request.params.size() > 2) { - nRescanHeight = request.params[2].get_int(); - } - if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + WalletRescanReserver reserver(pwalletMain); + if (fRescan && !reserver.reserve()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } - std::string strVKey = request.params[0].get_str(); - libzcash::ViewingKey viewingkey = KeyIO::DecodeViewingKey(strVKey); - if (!IsValidViewingKey(viewingkey)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); - } - libzcash::SaplingExtendedFullViewingKey efvk = *boost::get(&viewingkey); UniValue result(UniValue::VOBJ); - result.pushKV("address", KeyIO::EncodePaymentAddress(efvk.DefaultAddress())); - - auto addResult = pwalletMain->GetSaplingScriptPubKeyMan()->AddViewingKeyToWallet(efvk); - if (addResult == SpendingKeyExists) { - throw JSONRPCError( - RPC_WALLET_ERROR, - "The wallet already contains the private key for this viewing key"); - } else if (addResult == KeyAlreadyExists && fIgnoreExistingKey) { - return result; - } - pwalletMain->MarkDirty(); - if (addResult == KeyNotAdded) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet"); + CBlockIndex* pindexRescan{nullptr}; + { + LOCK2(cs_main, pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + + // Height to rescan from + int nRescanHeight = 0; + if (request.params.size() > 2) { + nRescanHeight = request.params[2].get_int(); + } + if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + } + + std::string strVKey = request.params[0].get_str(); + libzcash::ViewingKey viewingkey = KeyIO::DecodeViewingKey(strVKey); + if (!IsValidViewingKey(viewingkey)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); + } + libzcash::SaplingExtendedFullViewingKey efvk = *boost::get(&viewingkey); + result.pushKV("address", KeyIO::EncodePaymentAddress(efvk.DefaultAddress())); + + auto addResult = pwalletMain->GetSaplingScriptPubKeyMan()->AddViewingKeyToWallet(efvk); + if (addResult == SpendingKeyExists) { + throw JSONRPCError( + RPC_WALLET_ERROR, + "The wallet already contains the private key for this viewing key"); + } else if (addResult == KeyAlreadyExists && fIgnoreExistingKey) { + return result; + } + pwalletMain->MarkDirty(); + if (addResult == KeyNotAdded) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet"); + } + + pindexRescan = chainActive[nRescanHeight]; } // We want to scan for transactions and notes if (fRescan) { - pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], nullptr, true); + pwalletMain->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true); } return result; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 0f8e588965f1..6b27ba5655a7 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4238,7 +4238,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request) } } - CBlockIndex *stopBlock = pwalletMain->ScanForWalletTransactions(pindexStart, pindexStop, true); + CBlockIndex *stopBlock = pwalletMain->ScanForWalletTransactions(pindexStart, pindexStop, reserver, true); if (!stopBlock) { if (pwalletMain->IsAbortingRescan()) { throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted."); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index b2709e095ffd..659407c10b65 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -336,7 +336,9 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) CWallet wallet; WITH_LOCK(wallet.cs_wallet, wallet.SetLastBlockProcessed(newTip); ); AddKey(wallet, coinbaseKey); - BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr)); + WalletRescanReserver reserver(&wallet); + reserver.reserve(); + BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 500 * COIN); } @@ -350,8 +352,10 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { CWallet wallet; AddKey(wallet, coinbaseKey); - BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr)); - BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); + WalletRescanReserver reserver(&wallet); + reserver.reserve(); + BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver));; + BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 250 * COIN); } */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 5da453490f12..b277e2a7f7bb 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1829,7 +1829,7 @@ bool CWallet::Upgrade(std::string& error, const int& prevVersion) * @return Earliest timestamp that could be successfully scanned from. Timestamp * returned will be higher than startTime if relevant blocks could not be read. */ -int64_t CWallet::RescanFromTime(int64_t startTime, bool update) +int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update) { // Find starting block. May be null if nCreateTime is greater than the // highest blockchain timestamp, in which case there is nothing that needs @@ -1842,7 +1842,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) } if (startBlock) { - const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update); + const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, reserver, update); if (failedBlock) { return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; } @@ -1866,10 +1866,11 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) * the main chain after to the addition of any new keys you want to detect * transactions for. */ -CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate, bool fromStartup) +CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate, bool fromStartup) { int64_t nNow = GetTime(); + assert(reserver.isReserved()); if (pindexStop) { assert(pindexStop->nHeight >= pindexStart->nHeight); } @@ -1966,7 +1967,6 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, Checkpoints::GuessVerificationProgress(pindex, false)); } ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI - fScanningWallet = false; } return ret; } @@ -4373,9 +4373,16 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) pindexRescan = chainActive.Next(pindexRescan); } const int64_t nWalletRescanTime = GetTimeMillis(); - if (walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, true, true) != nullptr) { - UIError(_("Shutdown requested over the txs scan. Exiting.")); - return nullptr; + { + WalletRescanReserver reserver(walletInstance); + if (!reserver.reserve()) { + UIError(_("Failed to rescan the wallet during initialization")); + return nullptr; + } + if (walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true, true) != nullptr) { + UIError(_("Shutdown requested over the txs scan. Exiting.")); + return nullptr; + } } LogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nWalletRescanTime); walletInstance->SetBestChain(chainActive.GetLocator()); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4663811f8acc..5791665acfef 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -555,6 +555,9 @@ class CWalletTx bool AcceptToMemoryPool(CValidationState& state, bool fLimitFree = true, bool fRejectInsaneFee = true, bool ignoreFees = false); }; + +class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime + /** * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. @@ -564,7 +567,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface private: static std::atomic fFlushScheduled; std::atomic fAbortRescan; - std::atomic fScanningWallet; + std::atomic fScanningWallet; //controlled by WalletRescanReserver std::mutex mutexScanning; friend class WalletRescanReserver; @@ -964,8 +967,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool Upgrade(std::string& error, const int& prevVersion); bool ActivateSaplingWallet(bool memOnly = false); - int64_t RescanFromTime(int64_t startTime, bool update); - CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop = nullptr, bool fUpdate = false, bool fromStartup = false); + int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update); + CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false, bool fromStartup = false); void TransactionRemovedFromMempool(const CTransactionRef &ptx) override; void ReacceptWalletTransactions(bool fFirstLoad = false); void ResendWalletTransactions(CConnman* connman) override; @@ -1254,6 +1257,11 @@ class WalletRescanReserver return true; } + bool isReserved() const + { + return (m_could_reserve && m_wallet->fScanningWallet); + } + ~WalletRescanReserver() { std::lock_guard lock(m_wallet->mutexScanning); From f773203f2c68724d6c549f5cabdcf21c1b4a54da Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Wed, 13 Dec 2017 11:06:51 -1000 Subject: [PATCH 08/10] Reduce cs_main lock in ReadBlockFromDisk, only read GetBlockPos under the lock --- src/validation.cpp | 4 +++- src/wallet/wallet.cpp | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 8f9644441d50..be53443ebdb4 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -820,8 +820,10 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) { - if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) + CDiskBlockPos blockPos = WITH_LOCK(cs_main, return pindex->GetBlockPos(); ); + if (!ReadBlockFromDisk(block, blockPos)) { return false; + } if (block.GetHash() != pindex->GetBlockHash()) { LogPrintf("%s : block=%s index=%s\n", __func__, block.GetHash().GetHex(), pindex->GetBlockHash().GetHex()); return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index"); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b277e2a7f7bb..beee18dc5e26 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1905,8 +1905,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock } CBlock block; - bool readRet = WITH_LOCK(cs_main, return ReadBlockFromDisk(block, pindex); ); - if (readRet) { + if (ReadBlockFromDisk(block, pindex)) { LOCK2(cs_main, cs_wallet); if (pindex && !chainActive.Contains(pindex)) { // Abort scan if current block is no longer active, to prevent From 29c267ef2bd367ec6218e2b0ad2d7d7e2571e7b1 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 5 Jan 2018 10:43:31 -1000 Subject: [PATCH 09/10] Mention that other RPC calls report keys as "imported" while txns are still missing --- src/wallet/rpcdump.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d89b7648bd04..86d48540bf46 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -92,9 +92,8 @@ UniValue importprivkey(const JSONRPCRequest& request) "2. \"label\" (string, optional, default=\"\") An optional label\n" "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" "4. fStakingAddress (boolean, optional, default=false) Whether this key refers to a (cold) staking address\n" - - "\nNote: This call can take minutes to complete if rescan is true.\n" - + "\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n" + "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n" "\nExamples:\n" "\nDump a private key\n" + HelpExampleCli("dumpprivkey", "\"myaddress\"") + @@ -212,10 +211,8 @@ UniValue importaddress(const JSONRPCRequest& request) "2. \"label\" (string, optional, default=\"\") An optional label\n" "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" "4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n" - - "\nNote: This call can take minutes to complete if rescan is true.\n" - "If you have the full public key, you should call importpublickey instead of this.\n" - + "\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n" + "may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n" "\nExamples:\n" "\nImport a script with rescan\n" + HelpExampleCli("importaddress", "\"myscript\"") + @@ -274,7 +271,8 @@ UniValue importpubkey(const JSONRPCRequest& request) "1. \"pubkey\" (string, required) The hex-encoded public key\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" - "\nNote: This call can take minutes to complete if rescan is true.\n" + "\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n" + "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n" "\nExamples:\n" "\nImport a public key with rescan\n" + HelpExampleCli("importpubkey", "\"mypubkey\"") + @@ -972,7 +970,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) " }\n" " ,...\n" "]\n" - + "\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n" + "may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n" "\nExamples:\n" + HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"\" }, \"timestamp\":1455191478 }, " "{ \"scriptPubKey\": { \"address\": \"\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") + From 1cde8b4dd21f21f4fc61f63b5777febbffe898de Mon Sep 17 00:00:00 2001 From: random-zebra Date: Tue, 13 Apr 2021 10:30:44 +0200 Subject: [PATCH 10/10] [Trivial] Fix some comment/output: 'bitcoind' --> 'pivxd' --- src/pivxd.cpp | 4 ++-- src/util.cpp | 2 +- src/wallet/rpcdump.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pivxd.cpp b/src/pivxd.cpp index 888fb52ac35b..a47fc15c5d4b 100644 --- a/src/pivxd.cpp +++ b/src/pivxd.cpp @@ -105,12 +105,12 @@ bool AppInit(int argc, char* argv[]) // Error out when loose non-argument tokens are encountered on command line for (int i = 1; i < argc; i++) { if (!IsSwitchChar(argv[i][0])) { - fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]); + fprintf(stderr, "Error: Command line contains unexpected token '%s', see pivxd -h for a list of options.\n", argv[i]); exit(EXIT_FAILURE); } } - // -server defaults to true for bitcoind but not for the GUI so do this here + // -server defaults to true for pivxd but not for the GUI so do this here gArgs.SoftSetBoolArg("-server", true); // Set this early so that parameter interactions go to console InitLogging(); diff --git a/src/util.cpp b/src/util.cpp index 3ac197e0eeb2..c25216fc147f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -197,7 +197,7 @@ void ArgsManager::InterpretNegatedOption(std::string& key, std::string& val) m_negated_args.insert(key); val = bool_val ? "0" : "1"; } else { - // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo + // In an invocation like "pivxd -nofoo -foo" we want to unmark -foo // as negated when we see the second option. m_negated_args.erase(key); } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 86d48540bf46..f1f05a2f562d 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1065,7 +1065,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) "block from time %d, which is after or within %d seconds of key creation, and " "could contain transactions pertaining to the key. As a result, transactions " "and coins using this key may not appear in the wallet. This error could be " - "caused by pruning or data corruption (see bitcoind log for details) and could " + "caused by pruning or data corruption (see pivxd log for details) and could " "be dealt with by downloading and rescanning the relevant blocks (see -reindex " "and -rescan options).", GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));