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
65 changes: 65 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,71 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs)
return true;
}

int RescanSpentFlags()
{
// Believe the chain, not the wallet, about what has already been spent.
//
// fSpent lives in wallet.dat and is written when this node spends
// something. A wallet.dat that was restored from backup, or copied and
// used on another machine, carries whatever that flag was at the moment
// the copy was taken -- so it can say "unspent" about coins the chain
// shows as gone. The balance then reads high, and the error only surfaces
// when a send is attempted against coins that no longer exist.
//
// Issue #47 is about this shape of problem and #40 reports living through
// it. Bitcoin fixed the same thing in 53d508072.
//
// One direction only. Marking spent when the chain says spent is safe;
// clearing the flag because the chain has not caught up yet would offer
// up coins that are already on their way out.
CTxDB txdb("r");
int nCorrected = 0;
int nExamined = 0;
int nAlreadySpent = 0, nNotIndexed = 0;
CRITICAL_BLOCK(cs_mapWallet)
{
foreach(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
CWalletTx& wtx = item.second;
nExamined++;
if (wtx.fSpent)
{ nAlreadySpent++; continue; }

CTxIndex txindex;
if (!txdb.ReadTxIndex(wtx.GetHash(), txindex))
{ nNotIndexed++; continue; }

// fSpent is one flag for the whole transaction, not one per
// output -- CommitTransactionSpent already marks the entire
// previous transaction when it spends any part of it, so matching
// that here keeps the two consistent.
bool fSeenSpent = false;
for (int i = 0; i < (int)txindex.vSpent.size() && i < (int)wtx.vout.size(); i++)
if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine())
fSeenSpent = true;

if (fSeenSpent)
{
wtx.fSpent = true;
wtx.WriteToDisk();
nCorrected++;
printf("RescanSpentFlags() : %s was spent on chain but the wallet did not know\n",
wtx.GetHash().ToString().substr(0,10).c_str());
}
}
}
// Always say it ran. A check that reports only when it finds something is
// indistinguishable from a check that never executed, and this codebase has
// paid for that confusion more than once.
printf("RescanSpentFlags() : examined %d, already-spent %d, not-indexed %d, corrected %d\n",
nExamined, nAlreadySpent, nNotIndexed, nCorrected);
if (nCorrected)
printf("RescanSpentFlags() : this wallet was behind the chain about what it had already spent, "
"which is what a restored backup looks like\n");
return nCorrected;
}


void ReacceptWalletTransactions()
{
// Reaccept any txes of ours that aren't already in a block
Expand Down
2 changes: 2 additions & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ bool AddKey(const CKey& key);
vector<unsigned char> GenerateNewKey();
bool AddToWallet(const CWalletTx& wtxIn);
void ReacceptWalletTransactions();
// Returns how many wallet transactions the chain corrected.
int RescanSpentFlags();
void RelayWalletTransactions();
bool LoadBlockIndex(bool fAllowNew=true);
void PrintBlockTree();
Expand Down
4 changes: 4 additions & 0 deletions src/main_gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ int main(int argc, char* argv[])
try
{
if (!LoadWallet()) { fprintf(stderr, "LoadWallet failed\n"); return 1; }

// After the block index, so there is a chain to compare the wallet
// against, and before anything reports a balance.
RescanSpentFlags();
}
catch (const std::exception& e)
{
Expand Down