Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/pivxd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
12 changes: 8 additions & 4 deletions src/qt/pivx/settings/settingsbittoolwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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; }");
Expand Down Expand Up @@ -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; }");
Expand Down
2 changes: 1 addition & 1 deletion src/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
4 changes: 3 additions & 1 deletion src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
493 changes: 261 additions & 232 deletions src/wallet/rpcdump.cpp

Large diffs are not rendered by default.

42 changes: 26 additions & 16 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4207,34 +4207,44 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
}

EnsureWallet();
LOCK2(cs_main, pwalletMain->cs_wallet);
WalletRescanReserver reserver(pwalletMain);
if (!reserver.reserve()) {
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");
}
}
}

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.");
}
// 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.");
Expand Down
10 changes: 7 additions & 3 deletions src/wallet/test/wallet_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}
*/

Expand Down
156 changes: 105 additions & 51 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,22 @@ bool CWallet::LoadCryptedKey(const CPubKey& vchPubKey, const std::vector<unsigne
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
}

/**
* Update wallet first key creation time. This should be called whenever keys
* are added to the wallet, with the oldest key creation time.
*/
void CWallet::UpdateTimeFirstKey(int64_t nCreateTime)
{
AssertLockHeld(cs_wallet);
if (nCreateTime <= 1) {
// Cannot determine birthday information, so set the wallet birthday to
// the beginning of time.
nTimeFirstKey = 1;
} else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) {
nTimeFirstKey = nCreateTime;
}
}

bool CWallet::AddCScript(const CScript& redeemScript)
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
Expand Down Expand Up @@ -1805,53 +1821,80 @@ bool CWallet::Upgrade(std::string& error, const int& prevVersion)
return true;
}

/**
* 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.
*
* @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, 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
// to be scanned.
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, reserver, update);
if (failedBlock) {
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1;
}
}
return startTime;
}

/**
* Scan the block chain (starting in pindexStart) for transactions
* 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* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate, bool fromStartup)
{
CBlockIndex* ret = nullptr;
int64_t nNow = GetTime();

assert(reserver.isReserved());
if (pindexStop) {
assert(pindexStop->nHeight >= pindexStart->nHeight);
}

fAbortRescan = false;
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);
}

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<uint256> 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<uint256> 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();
Expand All @@ -1862,26 +1905,14 @@ 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
}

{
if (ReadBlockFromDisk(block, pindex)) {
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);
Expand All @@ -1901,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);
}
}
}

Expand All @@ -1925,7 +1966,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;
}
Expand Down Expand Up @@ -4324,10 +4364,24 @@ 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."));
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());
Expand Down
Loading