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
145 removals
707 lines
171 additions
732 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
// 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 "txdb.h"
#include "main.h"
#include "wallet.h"
#include "wallet.h"
#include "walletdb.h"
#include "walletdb.h"
#include "crypter.h"
#include "crypter.h"
#include "util.h"
#include "ui_interface.h"
#include "ui_interface.h"
#include "base58.h"
#include "base58.h"
#include "kernel.h"
#include "kernel.h"
#include "net.h"
#include "init.h"
#include "coincontrol.h"
#include "coincontrol.h"
#include <boost/algorithm/string/replace.hpp>

#include <boost/algorithm/string.hpp>


using namespace std;
using namespace std;
extern int nMinerSleep;
extern int nStakeMaxAge;




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


struct CompareValueOnly
struct CompareValueOnly
{
{
bool operator()(const pair<int64_t, pair<const CWalletTx*, unsigned int> >& t1,
bool operator()(const pair<int64, pair<const CWalletTx*, unsigned int> >& t1,
const pair<int64_t, pair<const CWalletTx*, unsigned int> >& t2) const
const pair<int64, pair<const CWalletTx*, unsigned int> >& t2) const
{
{
return t1.first < t2.first;
return t1.first < t2.first;
}
}
};
};


CPubKey CWallet::GenerateNewKey()
CPubKey CWallet::GenerateNewKey()
{
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
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);


CPubKey pubkey = key.GetPubKey();

// Create new metadata
int64_t nCreationTime = GetTime();
mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime);
if (!nTimeFirstKey || nCreationTime < nTimeFirstKey)
nTimeFirstKey = nCreationTime;

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)
{
{
AssertLockHeld(cs_wallet); // mapKeyMetadata

CPubKey pubkey = key.GetPubKey();

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(pubkey, key.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]);
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 CPubKey &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, mapKeyMetadata[vchPubKey.GetID()]);
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret);
else
else
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]);
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
}
}
return false;
return false;
}
}


bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey))
nTimeFirstKey = meta.nCreateTime;

mapKeyMetadata[pubkey.GetID()] = meta;
return true;
}

bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
}

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);
}
}


bool CWallet::LoadCScript(const CScript& redeemScript)
{
/* A sanity check was added in pull #3843 to avoid adding redeemScripts
* that never can be redeemed. However, old wallets may still contain
* these. Do not add them to the wallet and warn. */
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
{
std::string strAddr = CBitcoinAddress(redeemScript.GetID()).ToString();
LogPrintf("%s: Warning: This wallet contains a redeemScript of size %u which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n",
__func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
return true;
}

return CCryptoKeyStore::AddCScript(redeemScript);
}


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_t 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;


LogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);
printf("Wallet 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
// clients to crash on startup if reading a private-key-encrypted wallet.
class CCorruptAddress
{
public:
IMPLEMENT_SERIALIZE
(
if (nType & SER_DISK)
READWRITE(nVersion);
)
};


bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
{
{
LOCK(cs_wallet); // nWalletVersion
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)
{
// Versions prior to 0.4.0 did not support the "minversion" record.
// Use a CCorruptAddress to make them crash instead.
CCorruptAddress 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)
{
{
LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion
// 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_t 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;


LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
printf("Encrypting Wallet 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);


}
}
NotifyStatusChanged(this);
NotifyStatusChanged(this);


return true;
return true;
}
}


int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
int64 CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
{
{
AssertLockHeld(cs_wallet); // nOrderPosNext
int64 nRet = nOrderPosNext++;
int64_t nRet = nOrderPosNext++;
if (pwalletdb) {
if (pwalletdb) {
pwalletdb->WriteOrderPosNext(nOrderPosNext);
pwalletdb->WriteOrderPosNext(nOrderPosNext);
} else {
} else {
CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext);
CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext);
}
}
return nRet;
return nRet;
}
}


CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount)
CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount)
{
{
AssertLockHeld(cs_wallet); // mapWallet
CWalletDB walletdb(strWalletFile);
CWalletDB walletdb(strWalletFile);


// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
TxItems txOrdered;
TxItems txOrdered;


// Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
// Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
// would make this much faster for applications that do this a lot.
// would make this much faster for applications that do this a lot.
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
{
CWalletTx* wtx = &((*it).second);
CWalletTx* wtx = &((*it).second);
txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
}
}
acentries.clear();
acentries.clear();
walletdb.ListAccountCreditDebit(strAccount, acentries);
walletdb.ListAccountCreditDebit(strAccount, acentries);
BOOST_FOREACH(CAccountingEntry& entry, acentries)
BOOST_FOREACH(CAccountingEntry& entry, acentries)
{
{
txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
}
}


return txOrdered;
return txOrdered;
}
}


void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock)
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;
if (txin.prevout.n >= wtx.vout.size())
if (txin.prevout.n >= wtx.vout.size())
LogPrintf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString());
printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str());
else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
{
{
LogPrintf("WalletUpdateSpent found spent coin %shbn %s\n", FormatMoney(wtx.GetCredit()), wtx.GetHash().ToString());
printf("WalletUpdateSpent found spent coin %sMNT %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();
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
}
}
}
}
}
}
}
}

if (fBlock)
{
uint256 hash = tx.GetHash();
map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
CWalletTx& wtx = (*mi).second;

BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
if (IsMine(txout))
{
wtx.MarkUnspent(&txout - &tx.vout[0]);
wtx.WriteToDisk();
}
}

}
}
}


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();
wtx.nOrderPos = IncOrderPosNext();
wtx.nOrderPos = IncOrderPosNext();


wtx.nTimeSmart = wtx.nTimeReceived;
wtx.nTimeSmart = wtx.nTimeReceived;
if (wtxIn.hashBlock != 0)
if (wtxIn.hashBlock != 0)
{
{
if (mapBlockIndex.count(wtxIn.hashBlock))
if (mapBlockIndex.count(wtxIn.hashBlock))
{
{
unsigned int latestNow = wtx.nTimeReceived;
unsigned int latestNow = wtx.nTimeReceived;
unsigned int latestEntry = 0;
unsigned int latestEntry = 0;
{
{
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
int64_t latestTolerated = latestNow + 300;
int64 latestTolerated = latestNow + 300;
std::list<CAccountingEntry> acentries;
std::list<CAccountingEntry> acentries;
TxItems txOrdered = OrderedTxItems(acentries);
TxItems txOrdered = OrderedTxItems(acentries);
for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
{
CWalletTx *const pwtx = (*it).second.first;
CWalletTx *const pwtx = (*it).second.first;
if (pwtx == &wtx)
if (pwtx == &wtx)
continue;
continue;
CAccountingEntry *const pacentry = (*it).second.second;
CAccountingEntry *const pacentry = (*it).second.second;
int64_t nSmartTime;
int64 nSmartTime;
if (pwtx)
if (pwtx)
{
{
nSmartTime = pwtx->nTimeSmart;
nSmartTime = pwtx->nTimeSmart;
if (!nSmartTime)
if (!nSmartTime)
nSmartTime = pwtx->nTimeReceived;
nSmartTime = pwtx->nTimeReceived;
}
}
else
else
nSmartTime = pacentry->nTime;
nSmartTime = pacentry->nTime;
if (nSmartTime <= latestTolerated)
if (nSmartTime <= latestTolerated)
{
{
latestEntry = nSmartTime;
latestEntry = nSmartTime;
if (nSmartTime > latestNow)
if (nSmartTime > latestNow)
latestNow = nSmartTime;
latestNow = nSmartTime;
break;
break;
}
}
}
}
}
}


unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime;
unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime;
wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
}
}
else
else
LogPrintf("AddToWallet() : found %s in block %s not in index\n",
printf("AddToWallet() : found %s in block %s not in index\n",
wtxIn.GetHash().ToString().substr(0,10),
wtxIn.GetHash().ToString().substr(0,10).c_str(),
wtxIn.hashBlock.ToString());
wtxIn.hashBlock.ToString().c_str());
}
}
}
}


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
LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10), (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;
if (!fHaveGUI) {
if(!fHaveGUI){
// If default receiving address gets used, replace it with a new one
// If default receiving address gets used, replace it with a new one
if (vchDefaultKey.IsValid()) {
CScript scriptDefaultKey;
CScript scriptDefaultKey;
scriptDefaultKey.SetDestination(vchDefaultKey.GetID());
scriptDefaultKey.SetDestination(vchDefaultKey.GetID());
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
{
if (txout.scriptPubKey == scriptDefaultKey)
if (txout.scriptPubKey == scriptDefaultKey)
{
{
CPubKey newDefaultKey;
CPubKey newDefaultKey;
if (GetKeyFromPool(newDefaultKey, false))
if (GetKeyFromPool(newDefaultKey, false))
{
{
SetDefaultKey(newDefaultKey);
SetDefaultKey(newDefaultKey);
SetAddressBookName(vchDefaultKey.GetID(), "");
SetAddressBookName(vchDefaultKey.GetID(), "");
}
}
}
}
}
}
}
}
}
// 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, (wtxIn.hashBlock != 0));
WalletUpdateSpent(wtx);


// Notify UI of new or updated transaction
// Notify UI of new or updated transaction
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);


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

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

// notify an external script when a wallet transaction is received
std::string strCmdNewNotify = GetArg("-newtxnotify", "");
if ( !strCmdNewNotify.empty() && fInsertedNew)
{
boost::replace_all(strCmdNewNotify, "%s", wtxIn.GetHash().GetHex());
boost::thread t(runCommand, strCmdNewNotify); // thread runs free
}

// notify an external script when a wallet transaction is confirmed
std::string strCmdConfNotify = GetArg("-confirmnotify", "");
if ( !strCmdConfNotify.empty() && fUpdated)
{
boost::replace_all(strCmdConfNotify, "%s", wtxIn.GetHash().GetHex());
boost::thread t(runCommand, strCmdConfNotify); // thread runs free
}
}
}
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_t 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;
CTxDestination 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 (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address))
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
if (!mapAddressBook.count(address))
if (!mapAddressBook.count(address))
return true;
return true;
}
}
return false;
return false;
}
}


int64_t CWalletTx::GetTxTime() const
int64 CWalletTx::GetTxTime() const
{
{
int64_t n = nTimeSmart;
int64 n = nTimeSmart;
return n ? n : nTimeReceived;
return n ? n : 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(list<pair<CTxDestination, int64_t> >& listReceived,
void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CTxDestination, int64> >& listReceived,
list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount) const
list<pair<CTxDestination, int64> >& listSent, int64& nFee, string& strSentAccount) const
{
{
nFee = 0;
nGeneratedImmature = nGeneratedMature = nFee = 0;
listReceived.clear();
listReceived.clear();
listSent.clear();
listSent.clear();
strSen
strSentAccount = strFromAccount;

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

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

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

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

if (pwallet->IsMine(txout))
{
if(IsCoinBaseOrStake())
{
if(GetBlocksToMaturity() > 0)
nGeneratedImmature += txout.nValue;
else
nGeneratedMature += txout.nValue;
}
else
listReceived.push_back(make_pair(address, txout.nValue));
}
}

}

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

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

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

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

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