Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

Untitled diff

Created Diff never expires
35 removals
748 lines
34 additions
745 lines
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Copyright (c) 2009-2012 The Bitcoin developers
// Copyright (c) 2011-2013 The PPCoin developers
// Copyright (c) 2011-2013 The Peercoin developers
// Copyright (c) 2015-2015 The Decent developers
// Distributed under the MIT/X11 software license, see the accompanying
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// file COPYING or http://www.opensource.org/licenses/mit-license.php.


#include "wallet.h"
#include "wallet.h"
#include "walletdb.h"
#include "walletdb.h"
#include "crypter.h"
#include "crypter.h"
#include "ui_interface.h"
#include "ui_interface.h"
#include "kernel.h"
#include "kernel.h"
#include <boost/algorithm/string/replace.hpp>
#include "bitcoinrpc.h"
#include "base58.h"
#include "encryptionutils.h"


using namespace std;
using namespace std;




//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//
// mapWallet
// mapWallet
//
//


CPubKey CWallet::GenerateNewKey()
std::vector<unsigned char> CWallet::GenerateNewKey()
{
{
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets


RandAddSeedPerfmon();
RandAddSeedPerfmon();
CKey key;
CKey key;
key.MakeNewKey(fCompressed);
key.MakeNewKey(fCompressed);


// Compressed public keys were introduced in version 0.6.0
// Compressed public keys were introduced in version 0.6.0
if (fCompressed)
if (fCompressed)
SetMinVersion(FEATURE_COMPRPUBKEY);
SetMinVersion(FEATURE_COMPRPUBKEY);


if (!AddKey(key))
if (!AddKey(key))
throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed");
throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed");
return key.GetPubKey();
return key.GetPubKey();
}
}


bool CWallet::AddKey(const CKey& key)
bool CWallet::AddKey(const CKey& key)
{
{
if (!CCryptoKeyStore::AddKey(key))
if (!CCryptoKeyStore::AddKey(key))
return false;
return false;
if (!fFileBacked)
if (!fFileBacked)
return true;
return true;
if (!IsCrypted())
if (!IsCrypted())
return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey());
return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey());
return true;
return true;
}
}


bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector<unsigned char> &vchCryptedSecret)
bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector<unsigned char> &vchCryptedSecret)
{
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
return false;
if (!fFileBacked)
if (!fFileBacked)
return true;
return true;
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
if (pwalletdbEncryption)
if (pwalletdbEncryption)
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret);
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret);
else
else
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
}
}
return false;
return false;
}
}


bool CWallet::AddCScript(const CScript& redeemScript)
bool CWallet::AddCScript(const CScript& redeemScript)
{
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
if (!CCryptoKeyStore::AddCScript(redeemScript))
return false;
return false;
if (!fFileBacked)
if (!fFileBacked)
return true;
return true;
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
}
}


// ppcoin: optional setting to unlock wallet for block minting only;
// peercoin: optional setting to unlock wallet for block minting only;
// serves to disable the trivial sendmoney when OS account compromised
// serves to disable the trivial sendmoney when OS account compromised
bool fWalletUnlockMintOnly = false;
bool fWalletUnlockMintOnly = false;


bool CWallet::Unlock(const SecureString& strWalletPassphrase)
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
{
if (!IsLocked())
if (!IsLocked())
return false;
return false;


CCrypter crypter;
CCrypter crypter;
CKeyingMaterial vMasterKey;
CKeyingMaterial vMasterKey;


{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
{
{
if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false;
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
return false;
return false;
if (CCryptoKeyStore::Unlock(vMasterKey))
if (CCryptoKeyStore::Unlock(vMasterKey))
return true;
return true;
}
}
}
}
return false;
return false;
}
}


bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
{
{
bool fWasLocked = IsLocked();
bool fWasLocked = IsLocked();


{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
Lock();
Lock();


CCrypter crypter;
CCrypter crypter;
CKeyingMaterial vMasterKey;
CKeyingMaterial vMasterKey;
BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
{
{
if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false;
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
return false;
return false;
if (CCryptoKeyStore::Unlock(vMasterKey))
if (CCryptoKeyStore::Unlock(vMasterKey))
{
{
int64 nStartTime = GetTimeMillis();
int64 nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)));
pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)));


nStartTime = GetTimeMillis();
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;


if (pMasterKey.second.nDeriveIterations < 25000)
if (pMasterKey.second.nDeriveIterations < 25000)
pMasterKey.second.nDeriveIterations = 25000;
pMasterKey.second.nDeriveIterations = 25000;


printf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);
printf("Portfolio passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);


if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false;
return false;
if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey))
if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey))
return false;
return false;
CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second);
CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second);
if (fWasLocked)
if (fWasLocked)
Lock();
Lock();
return true;
return true;
}
}
}
}
}
}


return false;
return false;
}
}


void CWallet::SetBestChain(const CBlockLocator& loc)
void CWallet::SetBestChain(const CBlockLocator& loc)
{
{
CWalletDB walletdb(strWalletFile);
CWalletDB walletdb(strWalletFile);
walletdb.WriteBestBlock(loc);
walletdb.WriteBestBlock(loc);
}
}


// This class implements an addrIncoming entry that causes pre-0.4
// This class implements an addrIncoming entry that causes pre-0.4
// clients to crash on startup if reading a private-key-encrypted wallet.
// clients to crash on startup if reading a private-key-encrypted wallet.
class CCorruptAddress
class CCorruptAddress
{
{
public:
public:
IMPLEMENT_SERIALIZE
IMPLEMENT_SERIALIZE
(
(
if (nType & SER_DISK)
if (nType & SER_DISK)
READWRITE(nVersion);
READWRITE(nVersion);
)
)
};
};


bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
{
{
if (nWalletVersion >= nVersion)
if (nWalletVersion >= nVersion)
return true;
return true;


// when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way
// when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way
if (fExplicit && nVersion > nWalletMaxVersion)
if (fExplicit && nVersion > nWalletMaxVersion)
nVersion = FEATURE_LATEST;
nVersion = FEATURE_LATEST;


nWalletVersion = nVersion;
nWalletVersion = nVersion;


if (nVersion > nWalletMaxVersion)
if (nVersion > nWalletMaxVersion)
nWalletMaxVersion = nVersion;
nWalletMaxVersion = nVersion;


if (fFileBacked)
if (fFileBacked)
{
{
CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);
CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);
if (nWalletVersion >= 40000)
if (nWalletVersion >= 40000)
{
{
// Versions prior to 0.4.0 did not support the "minversion" record.
// Versions prior to 0.4.0 did not support the "minversion" record.
// Use a CCorruptAddress to make them crash instead.
// Use a CCorruptAddress to make them crash instead.
CCorruptAddress corruptAddress;
CCorruptAddress corruptAddress;
pwalletdb->WriteSetting("addrIncoming", corruptAddress);
pwalletdb->WriteSetting("addrIncoming", corruptAddress);
}
}
if (nWalletVersion > 40000)
if (nWalletVersion > 40000)
pwalletdb->WriteMinVersion(nWalletVersion);
pwalletdb->WriteMinVersion(nWalletVersion);
if (!pwalletdbIn)
if (!pwalletdbIn)
delete pwalletdb;
delete pwalletdb;
}
}


return true;
return true;
}
}


bool CWallet::SetMaxVersion(int nVersion)
bool CWallet::SetMaxVersion(int nVersion)
{
{
// cannot downgrade below current version
// cannot downgrade below current version
if (nWalletVersion > nVersion)
if (nWalletVersion > nVersion)
return false;
return false;


nWalletMaxVersion = nVersion;
nWalletMaxVersion = nVersion;


return true;
return true;
}
}


bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{
{
if (IsCrypted())
if (IsCrypted())
return false;
return false;


CKeyingMaterial vMasterKey;
CKeyingMaterial vMasterKey;
RandAddSeedPerfmon();
RandAddSeedPerfmon();


vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);


CMasterKey kMasterKey;
CMasterKey kMasterKey;


RandAddSeedPerfmon();
RandAddSeedPerfmon();
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);


CCrypter crypter;
CCrypter crypter;
int64 nStartTime = GetTimeMillis();
int64 nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));


nStartTime = GetTimeMillis();
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;


if (kMasterKey.nDeriveIterations < 25000)
if (kMasterKey.nDeriveIterations < 25000)
kMasterKey.nDeriveIterations = 25000;
kMasterKey.nDeriveIterations = 25000;


printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
printf("Encrypting portfolio with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);


if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
return false;
return false;
if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))
if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))
return false;
return false;


{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
if (fFileBacked)
if (fFileBacked)
{
{
pwalletdbEncryption = new CWalletDB(strWalletFile);
pwalletdbEncryption = new CWalletDB(strWalletFile);
if (!pwalletdbEncryption->TxnBegin())
if (!pwalletdbEncryption->TxnBegin())
return false;
return false;
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
}
}


if (!EncryptKeys(vMasterKey))
if (!EncryptKeys(vMasterKey))
{
{
if (fFileBacked)
if (fFileBacked)
pwalletdbEncryption->TxnAbort();
pwalletdbEncryption->TxnAbort();
exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet.
exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet.
}
}


// Encryption was introduced in version 0.4.0
// Encryption was introduced in version 0.4.0
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);


if (fFileBacked)
if (fFileBacked)
{
{
if (!pwalletdbEncryption->TxnCommit())
if (!pwalletdbEncryption->TxnCommit())
exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet.
exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet.


delete pwalletdbEncryption;
delete pwalletdbEncryption;
pwalletdbEncryption = NULL;
pwalletdbEncryption = NULL;
}
}


Lock();
Lock();
Unlock(strWalletPassphrase);
Unlock(strWalletPassphrase);
NewKeyPool();
NewKeyPool();
Lock();
Lock();


// Need to completely rewrite the wallet file; if we don't, bdb might keep
// Need to completely rewrite the wallet file; if we don't, bdb might keep
// bits of the unencrypted private key in slack space in the database file.
// bits of the unencrypted private key in slack space in the database file.
CDB::Rewrite(strWalletFile);
CDB::Rewrite(strWalletFile);
}
}


return true;
return true;
}
}


void CWallet::WalletUpdateSpent(const CTransaction &tx)
void CWallet::WalletUpdateSpent(const CTransaction &tx)
{
{
// Anytime a signature is successfully verified, it's proof the outpoint is spent.
// Anytime a signature is successfully verified, it's proof the outpoint is spent.
// Update the wallet spent flag if it doesn't know due to wallet.dat being
// Update the wallet spent flag if it doesn't know due to wallet.dat being
// restored from backup or the user making copies of wallet.dat.
// restored from backup or the user making copies of wallet.dat.
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
BOOST_FOREACH(const CTxIn& txin, tx.vin)
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
{
map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
if (mi != mapWallet.end())
{
{
CWalletTx& wtx = (*mi).second;
CWalletTx& wtx = (*mi).second;
// Decent: skip empty TX
if (wtx.vout.empty())
continue;
if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
{
{
printf("WalletUpdateSpent found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
printf("WalletUpdateSpent found spent unit %sDCT %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkSpent(txin.prevout.n);
wtx.MarkSpent(txin.prevout.n);
wtx.WriteToDisk();
wtx.WriteToDisk();
vWalletUpdated.push_back(txin.prevout.hash);
vWalletUpdated.push_back(txin.prevout.hash);
}
}
}
}
}
}
}
}
}
}


void CWallet::MarkDirty()
void CWallet::MarkDirty()
{
{
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
item.second.MarkDirty();
item.second.MarkDirty();
}
}
}
}


bool CWallet::AddToWallet(const CWalletTx& wtxIn)
bool CWallet::AddToWallet(const CWalletTx& wtxIn)
{
{
uint256 hash = wtxIn.GetHash();
uint256 hash = wtxIn.GetHash();
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
// Inserts only if not already there, returns tx inserted or tx found
// Inserts only if not already there, returns tx inserted or tx found
pair<map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn));
pair<map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn));
CWalletTx& wtx = (*ret.first).second;
CWalletTx& wtx = (*ret.first).second;
wtx.BindWallet(this);
wtx.BindWallet(this);
bool fInsertedNew = ret.second;
bool fInsertedNew = ret.second;
if (fInsertedNew)
if (fInsertedNew)
wtx.nTimeReceived = GetAdjustedTime();
wtx.nTimeReceived = GetAdjustedTime();


bool fUpdated = false;
bool fUpdated = false;
if (!fInsertedNew)
if (!fInsertedNew)
{
{
// Merge
// Merge
if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock)
if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock)
{
{
wtx.hashBlock = wtxIn.hashBlock;
wtx.hashBlock = wtxIn.hashBlock;
fUpdated = true;
fUpdated = true;
}
}
if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex))
if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex))
{
{
wtx.vMerkleBranch = wtxIn.vMerkleBranch;
wtx.vMerkleBranch = wtxIn.vMerkleBranch;
wtx.nIndex = wtxIn.nIndex;
wtx.nIndex = wtxIn.nIndex;
fUpdated = true;
fUpdated = true;
}
}
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
{
{
wtx.fFromMe = wtxIn.fFromMe;
wtx.fFromMe = wtxIn.fFromMe;
fUpdated = true;
fUpdated = true;
}
}
fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent);
fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent);
}
}


//// debug print
//// debug print
printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));


// Write to disk
// Write to disk
if (fInsertedNew || fUpdated)
if (fInsertedNew || fUpdated)
if (!wtx.WriteToDisk())
if (!wtx.WriteToDisk())
return false;
return false;
#ifndef QT_GUI
#ifndef QT_GUI
// If default receiving address gets used, replace it with a new one
// If default receiving address gets used, replace it with a new one
CScript scriptDefaultKey;
CScript scriptDefaultKey;
scriptDefaultKey.SetDestination(vchDefaultKey.GetID());
scriptDefaultKey.SetBitcoinAddress(vchDefaultKey);
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
{
if (txout.scriptPubKey == scriptDefaultKey)
if (txout.scriptPubKey == scriptDefaultKey)
{
{
CPubKey newDefaultKey;
std::vector<unsigned char> newDefaultKey;
if (GetKeyFromPool(newDefaultKey, false))
if (GetKeyFromPool(newDefaultKey, false))
{
{
SetDefaultKey(newDefaultKey);
SetDefaultKey(newDefaultKey);
SetAddressBookName(vchDefaultKey.GetID(), "");
SetAddressBookName(CBitcoinAddress(vchDefaultKey), "");
}
}
}
}
}
}
#endif
#endif
// Notify UI
// Notify UI
vWalletUpdated.push_back(hash);
vWalletUpdated.push_back(hash);


// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
WalletUpdateSpent(wtx);
WalletUpdateSpent(wtx);

// notify an external script when a wallet transaction comes in or is updated
std::string strCmd = GetArg("-walletnotify", "");

if ( !strCmd.empty())
{
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
}
}
}


// Refresh UI
// Refresh UI
MainFrameRepaint();
MainFrameRepaint();
return true;
return true;
}
}


// Add a transaction to the wallet, or update it.
// Add a transaction to the wallet, or update it.
// pblock is optional, but should be provided if the transaction is known to be in a block.
// pblock is optional, but should be provided if the transaction is known to be in a block.
// If fUpdate is true, existing transactions will be updated.
// If fUpdate is true, existing transactions will be updated.
bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock)
bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock)
{
{
uint256 hash = tx.GetHash();
uint256 hash = tx.GetHash();
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
bool fExisted = mapWallet.count(hash);
bool fExisted = mapWallet.count(hash);
if (fExisted && !fUpdate) return false;
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
{
CWalletTx wtx(this,tx);
CWalletTx wtx(this,tx);
// Get merkle branch if transaction was found in a block
// Get merkle branch if transaction was found in a block
if (pblock)
if (pblock)
wtx.SetMerkleBranch(pblock);
wtx.SetMerkleBranch(pblock);
return AddToWallet(wtx);
return AddToWallet(wtx);
}
}
else
else
WalletUpdateSpent(tx);
WalletUpdateSpent(tx);
}
}
return false;
return false;
}
}


bool CWallet::EraseFromWallet(uint256 hash)
bool CWallet::EraseFromWallet(uint256 hash)
{
{
if (!fFileBacked)
if (!fFileBacked)
return false;
return false;
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
if (mapWallet.erase(hash))
if (mapWallet.erase(hash))
CWalletDB(strWalletFile).EraseTx(hash);
CWalletDB(strWalletFile).EraseTx(hash);
}
}
return true;
return true;
}
}




bool CWallet::IsMine(const CTxIn &txin) const
bool CWallet::IsMine(const CTxIn &txin) const
{
{
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
if (mi != mapWallet.end())
{
{
const CWalletTx& prev = (*mi).second;
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n]))
if (IsMine(prev.vout[txin.prevout.n]))
return true;
return true;
}
}
}
}
return false;
return false;
}
}


int64 CWallet::GetDebit(const CTxIn &txin) const
int64 CWallet::GetDebit(const CTxIn &txin) const
{
{
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
if (mi != mapWallet.end())
{
{
const CWalletTx& prev = (*mi).second;
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n]))
if (IsMine(prev.vout[txin.prevout.n]))
return prev.vout[txin.prevout.n].nValue;
return prev.vout[txin.prevout.n].nValue;
}
}
}
}
return 0;
return 0;
}
}


bool CWallet::IsChange(const CTxOut& txout) const
bool CWallet::IsChange(const CTxOut& txout) const
{
{
CTxDestination address;
CBitcoinAddress address;


// TODO: fix handling of 'change' outputs. The assumption is that any
// TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a TX_PUBKEYHASH that is mine but isn't in the address book
// payment to a TX_PUBKEYHASH that is mine but isn't in the address book
// is change. That assumption is likely to break when we implement multisignature
// is change. That assumption is likely to break when we implement multisignature
// wallets that return change back into a multi-signature-protected address;
// wallets that return change back into a multi-signature-protected address;
// a better way of identifying which outputs are 'the send' and which are
// a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change).
// which output, if any, was change).
if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address))
if (ExtractAddress(txout.scriptPubKey, address) && HaveKey(address))
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
if (!mapAddressBook.count(address))
if (!mapAddressBook.count(address))
return true;
return true;
}
}
return false;
return false;
}
}


int64 CWalletTx::GetTxTime() const
int64 CWalletTx::GetTxTime() const
{
{
return nTimeReceived;
return nTimeReceived;
}
}


int CWalletTx::GetRequestCount() const
int CWalletTx::GetRequestCount() const
{
{
// Returns -1 if it wasn't being tracked
// Returns -1 if it wasn't being tracked
int nRequests = -1;
int nRequests = -1;
{
{
LOCK(pwallet->cs_wallet);
LOCK(pwallet->cs_wallet);
if (IsCoinBase() || IsCoinStake())
if (IsCoinBase() || IsCoinStake())
{
{
// Generated block
// Generated block
if (hashBlock != 0)
if (hashBlock != 0)
{
{
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end())
if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second;
nRequests = (*mi).second;
}
}
}
}
else
else
{
{
// Did anyone request this transaction?
// Did anyone request this transaction?
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());
if (mi != pwallet->mapRequestCount.end())
if (mi != pwallet->mapRequestCount.end())
{
{
nRequests = (*mi).second;
nRequests = (*mi).second;


// How about the block it's in?
// How about the block it's in?
if (nRequests == 0 && hashBlock != 0)
if (nRequests == 0 && hashBlock != 0)
{
{
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end())
if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second;
nRequests = (*mi).second;
else
else
nRequests = 1; // If it's in someone else's block it must have got out
nRequests = 1; // If it's in someone else's block it must have got out
}
}
}
}
}
}
}
}
return nRequests;
return nRequests;
}
}


void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CTxDestination, int64> >& listReceived,
void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CBitcoinAddress, int64> >& listReceived,
list<pair<CTxDestination, int64> >& listSent, int64& nFee, string& strSentAccount) const
list<pair<CBitcoinAddress, int64> >& listSent, int64& nFee, string& strSentAccount) const
{
{
nGeneratedImmature = nGeneratedMature = nFee = 0;
nGeneratedImmature = nGeneratedMature = nFee = 0;
listReceived.clear();
listReceived.clear();
listSent.clear();
listSent.clear();
strSentAccount = strFromAccount;
strSentAccount = strFromAccount;


if (IsCoinBase() || IsCoinStake())
if (IsCoinBase() || IsCoinStake())
{
{
if (GetBlocksToMaturity() > 0)
if (GetBlocksToMaturity() > 0)
nGeneratedImmature = pwallet->GetCredit(*this) - pwallet->GetDebit(*this);
nGeneratedImmature = pwallet->GetCredit(*this) - pwallet->GetDebit(*this);
else
else
nGeneratedMature = GetCredit() - GetDebit();
nGeneratedMature = GetCredit() - GetDebit();
return;
return;
}
}


// Compute fee:
// Compute fee:
int64 nDebit = GetDebit();
int64 nDebit = GetDebit();
if (nDebit > 0) // debit>0 means we signed/sent this transaction
if (nDebit > 0) // debit>0 means we signed/sent this transaction
{
{
int64 nValueOut = GetValueOut();
int64 nValueOut = GetValueOut();
nFee = nDebit - nValueOut;
nFee = nDebit - nValueOut;
}
}


// Sent/received.
// Sent/received.
BOOST_FOREACH(const CTxOut& txout, vout)
BOOST_FOREACH(const CTxOut& txout, vout)
{
{
CTxDestination address;
CBitcoinAddress address;
vector<unsigned char> vchPubKey;
vector<unsigned char> vchPubKey;
if (!ExtractDestination(txout.scriptPubKey, address))
if (!ExtractAddress(txout.scriptPubKey, address))
{
{
printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString().c_str());
this->GetHash().ToString().c_str());
address = " unknown ";
}
}


// Don't report 'change' txouts
// Don't report 'change' txouts
if (nDebit > 0 && pwallet->IsChange(txout))
if (nDebit > 0 && pwallet->IsChange(txout))
continue;
continue;


if (nDebit > 0)
if (nDebit > 0)
listSent.push_back(make_pair(address, txout.nValue));
listSent.push_back(make_pair(address, txout.nValue));


if (pwallet->IsMine(txout))
if (pwallet->IsMine(txout))
listReceived.push_back(make_pair(address, txout.nValue));
listReceived.push_back(make_pair(address, txout.nValue));
}
}


}
}


void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived,
void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived,
int64& nSent, int64& nFee) const
int64& nSent, int64& nFee) const
{
{
nGenerated = nReceived = nSent = nFee = 0;
nGenerated = nReceived = nSent = nFee = 0;


int64 allGeneratedImmature, allGeneratedMature, allFee;
int64 allGeneratedImmature, allGeneratedMature, allFee;
allGeneratedImmature = allGeneratedMature = allFee = 0;
allGeneratedImmature = allGeneratedMature = allFee = 0;
string strSentAccount;
string strSentAccount;
list<pair<CTxDestination, int64> > listReceived;
list<pair<CBitcoinAddress, int64> > listReceived;
list<pair<CTxDestination, int64> > listSent;
list<pair<CBitcoinAddress, int64> > listSent;
GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);


if (strAccount == "")
if (strAccount == "")
nGenerated = allGeneratedMature;
nGenerated = allGeneratedMature;
if (strAccount == strSentAccount)
if (strAccount == strSentAccount)
{
{
BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& s, listSent)
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& s, listSent)
nSent += s.second;
nSent += s.second;
nFee = allFee;
nFee = allFee;
}
}
{
{
LOCK(pwallet->cs_wallet);
LOCK(pwallet->cs_wallet);
BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
{
{
if (pwallet->mapAddressBook.count(r.first))
if (pwallet->mapAddressBook.count(r.first))
{
{
map<CTxDestination, string>::const_iterator mi = pwallet->mapAddressBook.find(r.first);
map<CBitcoinAddress, string>::const_iterator mi = pwallet->mapAddressBook.find(r.first);
if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount)
if (mi != pwallet->mapAddressBook.end() && ((*mi).second == strAccount || (*mi).first.ToString() == strAccount))
nReceived += r.second;
nReceived += r.second;
}
}
else if (strAccount.empty())
else if (strAccount.empty())
{
{
nReceived += r.second;
nReceived += r.second;
}
}
}
}
}
}
}
}


void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
{
{
vtxPrev.clear();
vtxPrev.clear();


const int COPY_DEPTH = 3;
const int COPY_DEPTH = 3;
if (SetMerkleBranch() < COPY_DEPTH)
if (SetMerkleBranch() < COPY_DEPTH)
{
{
vector<uint256> vWorkQueue;
vector<uint256> vWorkQueue;
BOOST_FOREACH(const CTxIn& txin, vin)
BOOST_FOREACH(const CTxIn& txin, vin)
vWorkQueue.push_back(txin.prevout.hash);
vWorkQueue.push_back(txin.prevout.hash);


// This critsect is OK because txdb is already open
// This critsect is OK because txdb is already open
{
{
LOCK(pwallet->cs_wallet);
LOCK(pwallet->cs_wallet);
map<uint256, const CMerkleTx*> mapWalletPrev;
map<uint256, const CMerkleTx*> mapWalletPrev;
set<uint256> setAlreadyDone;
set<uint256> setAlreadyDone;
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
{
uint256 hash = vWorkQueue[i];
uint256 hash = vWorkQueue[i];
if (setAlreadyDone.count(hash))
if (setAlreadyDone.count(hash))
continue;
continue;
setAlreadyDone.insert(hash);
setAlreadyDone.insert(hash);


CMerkleTx tx;
CMerkleTx tx;
map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(hash);
map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(hash);
if (mi != pwallet->mapWallet.end())
if (mi != pwallet->mapWallet.end())
{
{
tx = (*mi).second;
tx = (*mi).second;
BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev)
BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev)
mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev;
mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev;
}
}
else if (mapWalletPrev.count(hash))
else if (mapWalletPrev.count(hash))
{
{
tx = *mapWalletPrev[hash];
tx = *mapWalletPrev[hash];
}
}
else if (!fClient && txdb.ReadDiskTx(hash, tx))
else if (!fClient && txdb.ReadDiskTx(hash, tx))
{
{
;
;
}
}
else
else
{
{
printf("ERROR: AddSupportingTransactions() : unsupported transaction\n");
printf("ERROR: AddSupportingTransactions() : unsupported transaction\n");
continue;
continue;
}
}


int nDepth = tx.SetMerkleBranch();
int nDepth = tx.SetMerkleBranch();
vtxPrev.push_back(tx);
vtxPrev.push_back(tx);


if (nDepth < COPY_DEPTH)
if (nDepth < COPY_DEPTH)
{
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
BOOST_FOREACH(const CTxIn& txin, tx.vin)
vWorkQueue.push_back(txin.prevout.hash);
vWorkQueue.push_back(txin.prevout.hash);
}
}
}
}
}
}
}
}


reverse(vtxPrev.begin(), vtxPrev.end());
reverse(vtxPrev.begin(), vtxPrev.end());
}
}


bool CWalletTx::WriteToDisk()
bool CWalletTx::WriteToDisk()
{
{
return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
}
}


// Scan the block chain (starting in pindexStart) for transactions
// Scan the block chain (starting in pindexStart) for transactions
// from or to us. If fUpdate is true, found transactions that already
// from or to us. If fUpdate is true, found transactions that already
// exist in the wallet will be updated.
// exist in the wallet will be updated.
int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{
{
int ret = 0;
int ret = 0;


CBlockIndex* pindex = pindexStart;
CBlockIndex* pindex = pindexStart;
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
while (pindex)
while (pindex)
{
{
CBlock block;
CBlock block;
block.ReadFromDisk(pindex, true);
block.ReadFromDisk(pindex, true);
BOOST_FOREACH(CTransaction& tx, block.vtx)
BOOST_FOREACH(CTransaction& tx, block.vtx)
{
{
if (AddToWalletIfInvolvingMe(tx, &block, fUpdate))
if (AddToWalletIfInvolvingMe(tx, &block, fUpdate))
ret++;
ret++;
}
}
pindex = pindex->pnext;
pindex = pindex->pnext;
}
}
}
}
return ret;
return ret;
}
}


int CWallet::ScanForWalletTransaction(const uint256& hashTx)
int CWallet::ScanForWalletTransaction(const uint256& hashTx)
{
{
CTransaction tx;
CTransaction tx;
tx.ReadFromDisk(COutPoint(hashTx, 0));
tx.ReadFromDisk(COutPoint(hashTx, 0));
if (AddToWalletIfInvolvingMe(tx, NULL, true, true))
if (AddToWalletIfInvolvingMe(tx, NULL, true, true))
return 1;
return 1;
return 0;
return 0;
}
}


void CWallet::ReacceptWalletTransactions()
void CWallet::ReacceptWalletTransactions()
{
{
CTxDB txdb("r");
CTxDB txdb("r");
bool fRepeat = true;
bool fRepeat = true;
while (fRepeat)
while (fRepeat)
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
fRepeat = false;
fRepeat = false;
vector<CDiskTxPos> vMissingTx;
vector<CDiskTxPos> vMissingTx;
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
{
CWalletTx& wtx = item.second;
CWalletTx& wtx = item.second;
if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1)))
if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1)))
continue;
continue;


CTxIndex txindex;
CTxIndex txindex;
bool fUpdated = false;
bool fUpdated = false;
if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
{
{
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
if (txindex.vSpent.size() != wtx.vout.size())
if (txindex.vSpent.size() != wtx.vout.size())
{
{
printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size());
printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size());
continue;
continue;
}
for (unsigned int i = 0; i < txindex.v