Skip to content

Commit

Permalink
Implement targeted rescan to find mrc request transactions
Browse files Browse the repository at this point in the history
The very aggressive ResendWalletTransactions in 5.4.1.0 may have inappropriately
removed MRC request transactions (NOT payments) from wallets. This will
automatically recover those transactions at startup.

An attribute flag is set in the wallet db to indicate this is successful so that
it can be skipped on subsequent startups.
  • Loading branch information
jamescowens committed Mar 3, 2023
1 parent 985ed0d commit 1d0a750
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 2 deletions.
50 changes: 48 additions & 2 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@


#include "chainparams.h"
#include "gridcoin/support/block_finder.h"
#include "util.h"
#include "util/threadnames.h"
#include "net.h"
Expand Down Expand Up @@ -452,6 +453,8 @@ void SetupServerArgs()
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-rescan", "Rescan the block chain for missing wallet transactions",
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-rescanmrcrequests", "Rescan the block chain for missing mrc request transactions",
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet.dat",
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-zapwallettxes", "Delete all wallet transactions and only recover those parts of the blockchain through"
Expand Down Expand Up @@ -1318,16 +1321,59 @@ bool AppInit2(ThreadHandlerPtr threads)
RegisterWallet(pwalletMain);

CBlockIndex *pindexRescan = pindexBest;
bool mrc_request_correction_scan_complete = false;

if (gArgs.GetBoolArg("-rescan"))
pindexRescan = pindexGenesisBlock;
else
{
CWalletDB walletdb(walletFileName.string());

CBlockLocator locator;
if (walletdb.ReadBestBlock(locator))
if (walletdb.ReadBestBlock(locator)) {
pindexRescan = locator.GetBlockIndex();
}

walletdb.ReadAttribute("mrc_request_correction_scan_complete", mrc_request_correction_scan_complete);
}

// If rescan is NOT specified and mrc_request_correction_scan_complete is false and the wallet best height
// is greater than the V12 height, where MRC requests became possible, then perform a correction rescan for
// MRC requests from the BlockV12Height block to the wallet best height - 1. From the wallet best height to
// the block chain height will be handled by the normal rescan block below.
// -rescanmrcrequests can be specified as a startup parameter regardless of the state of the
// mrc_request_correction_scan_complete flag.
if (!gArgs.GetBoolArg("-rescan")
&& (!mrc_request_correction_scan_complete || gArgs.GetBoolArg("-rescanmrcrequests"))
&& pindexRescan->nHeight > Params().GetConsensus().BlockV12Height) {
CBlockIndex *pindexMRCRequestRescan = GRC::BlockFinder::FindByHeight(Params().GetConsensus().BlockV12Height);
uiInterface.InitMessage(_("Rescanning for MRC requests..."));
LogPrintf("INFO: %s: Rescanning from block height %i to %i for missing MRC request transactions",
__func__, pindexMRCRequestRescan->nHeight, pindexRescan->nHeight - 1);

g_timer.GetTimes("mrc request correction scan start", "init");

int mrc_requests_added = 0;

pwalletMain->ScanForMRCRequests(pindexMRCRequestRescan, pindexRescan, true);

LogPrintf("INFO: %u: %i missing MRC request transactions added to wallet.",
__func__, mrc_requests_added);

g_timer.GetTimes("mrc request correction scan complete", "init");

CWalletDB walletdb(walletFileName.string());
mrc_request_correction_scan_complete = true;
if (!walletdb.WriteAttribute("mrc_request_correction_scan_complete", mrc_request_correction_scan_complete)) {
error("%s: Unable to update mrc request correction scan attribute in wallet db.", __func__);
}
}
if (pindexBest != pindexRescan && pindexBest && pindexRescan && pindexBest->nHeight > pindexRescan->nHeight)

// If -rescan was not requested, but the wallet height is less than the block database height, then we
// must rescan from the wallet height to the block database height anyway to catch the wallet up. If rescan
// was requested, then this starts from genesis. If the pindexRescan->nHeight (i.e. wallet best height was
// less than or equal to the BlockV12Height, then the MRC request rescan above did not occur.
if (pindexBest && pindexRescan && pindexBest != pindexRescan && pindexBest->nHeight > pindexRescan->nHeight)
{
uiInterface.InitMessage(_("Rescanning..."));
LogPrintf("Rescanning last %i blocks (from block %i)...", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
Expand Down
43 changes: 43 additions & 0 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,49 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
return ret;
}

// Scan the block chain (starting in pindexStart) for MRC request transactions.
// If fUpdate is true, found transactions that already exist in the wallet will be updated.
// This restores MRC request transactions to the wallet that may have been removed
// do to an overly aggressive ResendWalletTransactions in 5.4.1.0. Note the MRC
// payment transactions were not affected. The change in effective balance due to this
// is very small, since a successful MRC request costs 0.011 GRC.
int CWallet::ScanForMRCRequests(CBlockIndex* pindexStart, CBlockIndex* pindexEnd, bool fUpdate)
{
int ret = 0;

{
LOCK2(cs_main, cs_wallet);
for(CBlockIndex* pindex = pindexStart; pindex && pindex->nHeight < pindexEnd->nHeight;)
{
// no need to read and scan block, if block was created before
// our wallet birthday (as adjusted for block time variability)
if (nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200))) {
pindex = pindex->pnext;
continue;
}

if (pindex->ResearchMRCSubsidy() > 0) {
// If at pindex there were MRC payment(s), then pindex->pprev there
// were MRC requests.
CBlock block;
ReadBlockFromDisk(block, pindex->pprev, Params().GetConsensus());
for (auto const& tx : block.vtx)
{
if (!tx.GetContracts().empty()
&& tx.GetContracts()[0].m_type == GRC::ContractType::MRC
&& mapWallet.find(tx.GetHash()) == mapWallet.end()
&& AddToWalletIfInvolvingMe(tx, &block, fUpdate))
ret++;
}
}

pindex = pindex->pnext;
}
}
return ret;
}


void CWallet::ReacceptWalletTransactions()
{
CTxDB txdb("r");
Expand Down
1 change: 1 addition & 0 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ class CWallet : public CCryptoKeyStore
bool EraseFromWallet(uint256 hash);
void WalletUpdateSpent(const CTransaction &tx, bool fBlock, CWalletDB* pwalletdb);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
int ScanForMRCRequests(CBlockIndex* pindexStart, CBlockIndex* pindexEnd, bool fUpdate = false);
void ReacceptWalletTransactions();


Expand Down
14 changes: 14 additions & 0 deletions src/wallet/walletdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,20 @@ class CWalletDB : public CDB
nWalletDBUpdated++;
return Write(std::string("backuptime"), backup_time);
}

template<typename T>
bool WriteAttribute(const std::string& attribute, const T& value)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("attribute"), attribute), value);
}

template<typename T>
bool ReadAttribute(const std::string& attribute, T& value)
{
nWalletDBUpdated++;
return Read(std::make_pair(std::string("attribute"), attribute), value);
}
private:
bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry);
public:
Expand Down

0 comments on commit 1d0a750

Please sign in to comment.