Untitled diff
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)