mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-09 10:43:19 -05:00
Avoid pemanent cs_main/cs_wallet lock during wallet rescans
This commit is contained in:
parent
0910cbe4ef
commit
8d0b610fe8
3 changed files with 260 additions and 209 deletions
|
@ -101,61 +101,60 @@ UniValue importprivkey(const JSONRPCRequest& request)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
LOCK2(cs_main, pwallet->cs_wallet);
|
|
||||||
|
|
||||||
EnsureWalletIsUnlocked(pwallet);
|
|
||||||
|
|
||||||
std::string strSecret = request.params[0].get_str();
|
|
||||||
std::string strLabel = "";
|
|
||||||
if (!request.params[1].isNull())
|
|
||||||
strLabel = request.params[1].get_str();
|
|
||||||
|
|
||||||
// Whether to perform rescan after import
|
|
||||||
bool fRescan = true;
|
bool fRescan = true;
|
||||||
if (!request.params[2].isNull())
|
|
||||||
fRescan = request.params[2].get_bool();
|
|
||||||
|
|
||||||
if (fRescan && fPruneMode)
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
|
|
||||||
|
|
||||||
CBitcoinSecret vchSecret;
|
|
||||||
bool fGood = vchSecret.SetString(strSecret);
|
|
||||||
|
|
||||||
if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
|
|
||||||
|
|
||||||
CKey key = vchSecret.GetKey();
|
|
||||||
if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
|
|
||||||
|
|
||||||
CPubKey pubkey = key.GetPubKey();
|
|
||||||
assert(key.VerifyPubKey(pubkey));
|
|
||||||
CKeyID vchAddress = pubkey.GetID();
|
|
||||||
{
|
{
|
||||||
pwallet->MarkDirty();
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
|
|
||||||
// We don't know which corresponding address will be used; label them all
|
EnsureWalletIsUnlocked(pwallet);
|
||||||
for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
|
|
||||||
pwallet->SetAddressBook(dest, strLabel, "receive");
|
std::string strSecret = request.params[0].get_str();
|
||||||
}
|
std::string strLabel = "";
|
||||||
|
if (!request.params[1].isNull())
|
||||||
// Don't throw error in case a key is already there
|
strLabel = request.params[1].get_str();
|
||||||
if (pwallet->HaveKey(vchAddress)) {
|
|
||||||
return NullUniValue;
|
// Whether to perform rescan after import
|
||||||
}
|
if (!request.params[2].isNull())
|
||||||
|
fRescan = request.params[2].get_bool();
|
||||||
pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1;
|
|
||||||
|
if (fRescan && fPruneMode)
|
||||||
if (!pwallet->AddKeyPubKey(key, pubkey)) {
|
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
|
|
||||||
}
|
CBitcoinSecret vchSecret;
|
||||||
pwallet->LearnAllRelatedScripts(pubkey);
|
bool fGood = vchSecret.SetString(strSecret);
|
||||||
|
|
||||||
// whenever a key is imported, we need to scan the whole chain
|
if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
|
||||||
pwallet->UpdateTimeFirstKey(1);
|
|
||||||
|
CKey key = vchSecret.GetKey();
|
||||||
if (fRescan) {
|
if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
|
||||||
pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */);
|
|
||||||
|
CPubKey pubkey = key.GetPubKey();
|
||||||
|
assert(key.VerifyPubKey(pubkey));
|
||||||
|
CKeyID vchAddress = pubkey.GetID();
|
||||||
|
{
|
||||||
|
pwallet->MarkDirty();
|
||||||
|
// We don't know which corresponding address will be used; label them all
|
||||||
|
for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
|
||||||
|
pwallet->SetAddressBook(dest, strLabel, "receive");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't throw error in case a key is already there
|
||||||
|
if (pwallet->HaveKey(vchAddress)) {
|
||||||
|
return NullUniValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// whenever a key is imported, we need to scan the whole chain
|
||||||
|
pwallet->UpdateTimeFirstKey(1);
|
||||||
|
pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1;
|
||||||
|
|
||||||
|
if (!pwallet->AddKeyPubKey(key, pubkey)) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
|
||||||
|
}
|
||||||
|
pwallet->LearnAllRelatedScripts(pubkey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (fRescan) {
|
||||||
|
pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */);
|
||||||
|
}
|
||||||
|
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
@ -268,21 +267,22 @@ UniValue importaddress(const JSONRPCRequest& request)
|
||||||
if (!request.params[3].isNull())
|
if (!request.params[3].isNull())
|
||||||
fP2SH = request.params[3].get_bool();
|
fP2SH = request.params[3].get_bool();
|
||||||
|
|
||||||
LOCK2(cs_main, pwallet->cs_wallet);
|
{
|
||||||
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
|
|
||||||
CTxDestination dest = DecodeDestination(request.params[0].get_str());
|
CTxDestination dest = DecodeDestination(request.params[0].get_str());
|
||||||
if (IsValidDestination(dest)) {
|
if (IsValidDestination(dest)) {
|
||||||
if (fP2SH) {
|
if (fP2SH) {
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
|
||||||
|
}
|
||||||
|
ImportAddress(pwallet, dest, strLabel);
|
||||||
|
} else if (IsHex(request.params[0].get_str())) {
|
||||||
|
std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
|
||||||
|
ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH);
|
||||||
|
} else {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
|
||||||
}
|
}
|
||||||
ImportAddress(pwallet, dest, strLabel);
|
|
||||||
} else if (IsHex(request.params[0].get_str())) {
|
|
||||||
std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
|
|
||||||
ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH);
|
|
||||||
} else {
|
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fRescan)
|
if (fRescan)
|
||||||
{
|
{
|
||||||
pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */);
|
pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */);
|
||||||
|
@ -436,14 +436,15 @@ UniValue importpubkey(const JSONRPCRequest& request)
|
||||||
if (!pubKey.IsFullyValid())
|
if (!pubKey.IsFullyValid())
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
|
||||||
|
|
||||||
LOCK2(cs_main, pwallet->cs_wallet);
|
{
|
||||||
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
|
|
||||||
for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
|
for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
|
||||||
ImportAddress(pwallet, dest, strLabel);
|
ImportAddress(pwallet, dest, strLabel);
|
||||||
|
}
|
||||||
|
ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
|
||||||
|
pwallet->LearnAllRelatedScripts(pubKey);
|
||||||
}
|
}
|
||||||
ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
|
|
||||||
pwallet->LearnAllRelatedScripts(pubKey);
|
|
||||||
|
|
||||||
if (fRescan)
|
if (fRescan)
|
||||||
{
|
{
|
||||||
pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */);
|
pwallet->RescanFromTime(TIMESTAMP_MIN, true /* update */);
|
||||||
|
@ -479,90 +480,92 @@ UniValue importwallet(const JSONRPCRequest& request)
|
||||||
if (fPruneMode)
|
if (fPruneMode)
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
|
||||||
|
|
||||||
LOCK2(cs_main, pwallet->cs_wallet);
|
int64_t nTimeBegin = 0;
|
||||||
|
|
||||||
EnsureWalletIsUnlocked(pwallet);
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
bool fGood = true;
|
bool fGood = true;
|
||||||
|
{
|
||||||
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
|
|
||||||
int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
|
EnsureWalletIsUnlocked(pwallet);
|
||||||
file.seekg(0, file.beg);
|
|
||||||
|
|
||||||
pwallet->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
|
std::ifstream file;
|
||||||
while (file.good()) {
|
file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate);
|
||||||
pwallet->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))));
|
if (!file.is_open()) {
|
||||||
std::string line;
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
|
||||||
std::getline(file, line);
|
|
||||||
if (line.empty() || line[0] == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::vector<std::string> vstr;
|
|
||||||
boost::split(vstr, line, boost::is_any_of(" "));
|
|
||||||
if (vstr.size() < 2)
|
|
||||||
continue;
|
|
||||||
CBitcoinSecret vchSecret;
|
|
||||||
if (vchSecret.SetString(vstr[0])) {
|
|
||||||
CKey key = vchSecret.GetKey();
|
|
||||||
CPubKey pubkey = key.GetPubKey();
|
|
||||||
assert(key.VerifyPubKey(pubkey));
|
|
||||||
CKeyID keyid = pubkey.GetID();
|
|
||||||
if (pwallet->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++) {
|
|
||||||
if (boost::algorithm::starts_with(vstr[nStr], "#"))
|
|
||||||
break;
|
|
||||||
if (vstr[nStr] == "change=1")
|
|
||||||
fLabel = false;
|
|
||||||
if (vstr[nStr] == "reserve=1")
|
|
||||||
fLabel = false;
|
|
||||||
if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
|
|
||||||
strLabel = DecodeDumpString(vstr[nStr].substr(6));
|
|
||||||
fLabel = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrintf("Importing %s...\n", EncodeDestination(keyid));
|
|
||||||
if (!pwallet->AddKeyPubKey(key, pubkey)) {
|
|
||||||
fGood = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pwallet->mapKeyMetadata[keyid].nCreateTime = nTime;
|
|
||||||
if (fLabel)
|
|
||||||
pwallet->SetAddressBook(keyid, strLabel, "receive");
|
|
||||||
nTimeBegin = std::min(nTimeBegin, nTime);
|
|
||||||
} else if(IsHex(vstr[0])) {
|
|
||||||
std::vector<unsigned char> vData(ParseHex(vstr[0]));
|
|
||||||
CScript script = CScript(vData.begin(), vData.end());
|
|
||||||
if (pwallet->HaveCScript(script)) {
|
|
||||||
LogPrintf("Skipping import of %s (script already present)\n", vstr[0]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(!pwallet->AddCScript(script)) {
|
|
||||||
LogPrintf("Error importing script %s\n", vstr[0]);
|
|
||||||
fGood = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int64_t birth_time = DecodeDumpTime(vstr[1]);
|
|
||||||
if (birth_time > 0) {
|
|
||||||
pwallet->m_script_metadata[CScriptID(script)].nCreateTime = birth_time;
|
|
||||||
nTimeBegin = std::min(nTimeBegin, birth_time);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
nTimeBegin = chainActive.Tip()->GetBlockTime();
|
||||||
|
|
||||||
|
int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
|
||||||
|
file.seekg(0, file.beg);
|
||||||
|
|
||||||
|
pwallet->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
|
||||||
|
while (file.good()) {
|
||||||
|
pwallet->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<std::string> vstr;
|
||||||
|
boost::split(vstr, line, boost::is_any_of(" "));
|
||||||
|
if (vstr.size() < 2)
|
||||||
|
continue;
|
||||||
|
CBitcoinSecret vchSecret;
|
||||||
|
if (vchSecret.SetString(vstr[0])) {
|
||||||
|
CKey key = vchSecret.GetKey();
|
||||||
|
CPubKey pubkey = key.GetPubKey();
|
||||||
|
assert(key.VerifyPubKey(pubkey));
|
||||||
|
CKeyID keyid = pubkey.GetID();
|
||||||
|
if (pwallet->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++) {
|
||||||
|
if (boost::algorithm::starts_with(vstr[nStr], "#"))
|
||||||
|
break;
|
||||||
|
if (vstr[nStr] == "change=1")
|
||||||
|
fLabel = false;
|
||||||
|
if (vstr[nStr] == "reserve=1")
|
||||||
|
fLabel = false;
|
||||||
|
if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
|
||||||
|
strLabel = DecodeDumpString(vstr[nStr].substr(6));
|
||||||
|
fLabel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LogPrintf("Importing %s...\n", EncodeDestination(keyid));
|
||||||
|
if (!pwallet->AddKeyPubKey(key, pubkey)) {
|
||||||
|
fGood = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pwallet->mapKeyMetadata[keyid].nCreateTime = nTime;
|
||||||
|
if (fLabel)
|
||||||
|
pwallet->SetAddressBook(keyid, strLabel, "receive");
|
||||||
|
nTimeBegin = std::min(nTimeBegin, nTime);
|
||||||
|
} else if(IsHex(vstr[0])) {
|
||||||
|
std::vector<unsigned char> vData(ParseHex(vstr[0]));
|
||||||
|
CScript script = CScript(vData.begin(), vData.end());
|
||||||
|
if (pwallet->HaveCScript(script)) {
|
||||||
|
LogPrintf("Skipping import of %s (script already present)\n", vstr[0]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!pwallet->AddCScript(script)) {
|
||||||
|
LogPrintf("Error importing script %s\n", vstr[0]);
|
||||||
|
fGood = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int64_t birth_time = DecodeDumpTime(vstr[1]);
|
||||||
|
if (birth_time > 0) {
|
||||||
|
pwallet->m_script_metadata[CScriptID(script)].nCreateTime = birth_time;
|
||||||
|
nTimeBegin = std::min(nTimeBegin, birth_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
pwallet->ShowProgress("", 100); // hide progress dialog in GUI
|
||||||
|
pwallet->UpdateTimeFirstKey(nTimeBegin);
|
||||||
}
|
}
|
||||||
file.close();
|
|
||||||
pwallet->ShowProgress("", 100); // hide progress dialog in GUI
|
|
||||||
pwallet->UpdateTimeFirstKey(nTimeBegin);
|
|
||||||
pwallet->RescanFromTime(nTimeBegin, false /* update */);
|
pwallet->RescanFromTime(nTimeBegin, false /* update */);
|
||||||
pwallet->MarkDirty();
|
pwallet->MarkDirty();
|
||||||
|
|
||||||
|
@ -685,7 +688,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
|
||||||
file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
|
file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
|
||||||
file << "\n";
|
file << "\n";
|
||||||
|
|
||||||
// add the base58check encoded extended master if the wallet uses HD
|
// add the base58check encoded extended master if the wallet uses HD
|
||||||
CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
|
CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
|
||||||
if (!masterKeyID.IsNull())
|
if (!masterKeyID.IsNull())
|
||||||
{
|
{
|
||||||
|
@ -1135,47 +1138,48 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCK2(cs_main, pwallet->cs_wallet);
|
int64_t now = 0;
|
||||||
EnsureWalletIsUnlocked(pwallet);
|
|
||||||
|
|
||||||
// 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;
|
bool fRunScan = false;
|
||||||
const int64_t minimumTimestamp = 1;
|
|
||||||
int64_t nLowestTimestamp = 0;
|
int64_t nLowestTimestamp = 0;
|
||||||
|
|
||||||
if (fRescan && chainActive.Tip()) {
|
|
||||||
nLowestTimestamp = chainActive.Tip()->GetBlockTime();
|
|
||||||
} else {
|
|
||||||
fRescan = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
UniValue response(UniValue::VARR);
|
UniValue response(UniValue::VARR);
|
||||||
|
{
|
||||||
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
|
EnsureWalletIsUnlocked(pwallet);
|
||||||
|
|
||||||
for (const UniValue& data : requests.getValues()) {
|
// Verify all timestamps are present before importing any keys.
|
||||||
const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
|
now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0;
|
||||||
const UniValue result = ProcessImport(pwallet, data, timestamp);
|
for (const UniValue& data : requests.getValues()) {
|
||||||
response.push_back(result);
|
GetImportTimestamp(data, now);
|
||||||
|
|
||||||
if (!fRescan) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If at least one request was successful then allow rescan.
|
const int64_t minimumTimestamp = 1;
|
||||||
if (result["success"].get_bool()) {
|
|
||||||
fRunScan = true;
|
if (fRescan && chainActive.Tip()) {
|
||||||
|
nLowestTimestamp = chainActive.Tip()->GetBlockTime();
|
||||||
|
} else {
|
||||||
|
fRescan = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the lowest timestamp.
|
for (const UniValue& data : requests.getValues()) {
|
||||||
if (timestamp < nLowestTimestamp) {
|
const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
|
||||||
nLowestTimestamp = timestamp;
|
const UniValue result = ProcessImport(pwallet, 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()) {
|
if (fRescan && fRunScan && requests.size()) {
|
||||||
int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, true /* update */);
|
int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, true /* update */);
|
||||||
pwallet->ReacceptWalletTransactions();
|
pwallet->ReacceptWalletTransactions();
|
||||||
|
|
|
@ -3398,30 +3398,40 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCK2(cs_main, pwallet->cs_wallet);
|
if (pwallet->IsScanning()) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
|
||||||
CBlockIndex *pindexStart = chainActive.Genesis();
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.params[1].isNull()) {
|
CBlockIndex *pindexStart = nullptr;
|
||||||
pindexStop = chainActive[request.params[1].get_int()];
|
CBlockIndex *pindexStop = nullptr;
|
||||||
if (!pindexStop) {
|
CBlockIndex *pChainTip = nullptr;
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't rescan beyond non-pruned blocks, stop and throw an error
|
// We can't rescan beyond non-pruned blocks, stop and throw an error
|
||||||
if (fPruneMode) {
|
if (fPruneMode) {
|
||||||
CBlockIndex *block = pindexStop ? pindexStop : chainActive.Tip();
|
LOCK(cs_main);
|
||||||
|
CBlockIndex *block = pindexStop ? pindexStop : pChainTip;
|
||||||
while (block && block->nHeight >= pindexStart->nHeight) {
|
while (block && block->nHeight >= pindexStart->nHeight) {
|
||||||
if (!(block->nStatus & BLOCK_HAVE_DATA)) {
|
if (!(block->nStatus & BLOCK_HAVE_DATA)) {
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
|
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
|
||||||
|
@ -3436,12 +3446,11 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
|
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
|
||||||
}
|
}
|
||||||
// if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex
|
// if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex
|
||||||
stopBlock = pindexStop ? pindexStop : chainActive.Tip();
|
stopBlock = pindexStop ? pindexStop : pChainTip;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
|
throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue response(UniValue::VOBJ);
|
UniValue response(UniValue::VOBJ);
|
||||||
response.pushKV("start_height", pindexStart->nHeight);
|
response.pushKV("start_height", pindexStart->nHeight);
|
||||||
response.pushKV("stop_height", stopBlock->nHeight);
|
response.pushKV("stop_height", stopBlock->nHeight);
|
||||||
|
|
|
@ -1614,14 +1614,15 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
|
||||||
*/
|
*/
|
||||||
int64_t CWallet::RescanFromTime(int64_t startTime, bool update)
|
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
|
// Find starting block. May be null if nCreateTime is greater than the
|
||||||
// highest blockchain timestamp, in which case there is nothing that needs
|
// highest blockchain timestamp, in which case there is nothing that needs
|
||||||
// to be scanned.
|
// to be scanned.
|
||||||
CBlockIndex* const startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW);
|
CBlockIndex* startBlock = nullptr;
|
||||||
LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0);
|
{
|
||||||
|
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) {
|
if (startBlock) {
|
||||||
const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update);
|
const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update);
|
||||||
|
@ -1643,6 +1644,10 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update)
|
||||||
*
|
*
|
||||||
* If pindexStop is not a nullptr, the scan will stop at the block-index
|
* If pindexStop is not a nullptr, the scan will stop at the block-index
|
||||||
* defined by pindexStop
|
* 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)
|
CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate)
|
||||||
{
|
{
|
||||||
|
@ -1656,24 +1661,49 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
|
||||||
CBlockIndex* pindex = pindexStart;
|
CBlockIndex* pindex = pindexStart;
|
||||||
CBlockIndex* ret = nullptr;
|
CBlockIndex* ret = nullptr;
|
||||||
{
|
{
|
||||||
LOCK2(cs_main, cs_wallet);
|
|
||||||
fAbortRescan = false;
|
fAbortRescan = false;
|
||||||
fScanningWallet = true;
|
fScanningWallet = true;
|
||||||
|
|
||||||
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
|
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
|
||||||
double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
|
CBlockIndex* tip = nullptr;
|
||||||
double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip());
|
double dProgressStart;
|
||||||
|
double dProgressTip;
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
tip = chainActive.Tip();
|
||||||
|
dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
|
||||||
|
dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip);
|
||||||
|
}
|
||||||
while (pindex && !fAbortRescan)
|
while (pindex && !fAbortRescan)
|
||||||
{
|
{
|
||||||
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)
|
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) {
|
||||||
ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
|
double gvp = 0;
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
gvp = GuessVerificationProgress(chainParams.TxData(), pindex);
|
||||||
|
}
|
||||||
|
ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((gvp - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
|
||||||
|
}
|
||||||
if (GetTime() >= nNow + 60) {
|
if (GetTime() >= nNow + 60) {
|
||||||
nNow = GetTime();
|
nNow = GetTime();
|
||||||
|
LOCK(cs_main);
|
||||||
LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
|
LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool readRet = false;
|
||||||
CBlock block;
|
CBlock block;
|
||||||
if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
readRet = ReadBlockFromDisk(block, pindex, Params().GetConsensus());
|
||||||
|
}
|
||||||
|
if (readRet) {
|
||||||
|
LOCK2(cs_main, cs_wallet);
|
||||||
|
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 (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
|
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
|
||||||
AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
|
AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
|
||||||
}
|
}
|
||||||
|
@ -1683,7 +1713,15 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
|
||||||
if (pindex == pindexStop) {
|
if (pindex == pindexStop) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pindex = chainActive.Next(pindex);
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
pindex = chainActive.Next(pindex);
|
||||||
|
if (tip != chainActive.Tip()) {
|
||||||
|
tip = chainActive.Tip();
|
||||||
|
// in case the tip has changed, update progress max
|
||||||
|
dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pindex && fAbortRescan) {
|
if (pindex && fAbortRescan) {
|
||||||
LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
|
LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
|
||||||
|
|
Loading…
Add table
Reference in a new issue