-// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "headers.h"
#include "db.h"
-#include "cryptopp/sha.h"
#include "crypter.h"
using namespace std;
// mapWallet
//
+std::vector<unsigned char> CWallet::GenerateNewKey()
+{
+ bool fCompressed = true; // default to compressed public keys
+
+ RandAddSeedPerfmon();
+ CKey key;
+ key.MakeNewKey(fCompressed);
+
+ // Compressed public keys were introduced in version 0.6.0
+ if (fCompressed)
+ SetMinVersion(59900);
+
+ if (!AddKey(key))
+ throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed");
+ return key.GetPubKey();
+}
+
bool CWallet::AddKey(const CKey& key)
{
if (!CCryptoKeyStore::AddKey(key))
return false;
if (!fFileBacked)
return true;
- CRITICAL_BLOCK(cs_pwalletdbEncryption)
+ CRITICAL_BLOCK(cs_wallet)
{
if (pwalletdbEncryption)
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret);
else
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
}
+ return false;
}
-bool CWallet::Unlock(const string& strWalletPassphrase)
+bool CWallet::AddCScript(const CScript& redeemScript)
{
- CRITICAL_BLOCK(cs_vMasterKey)
- {
- if (!IsLocked())
- return false;
+ if (!CCryptoKeyStore::AddCScript(redeemScript))
+ return false;
+ if (!fFileBacked)
+ return true;
+ return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
+}
- CCrypter crypter;
- CKeyingMaterial vMasterKey;
+bool CWallet::Unlock(const SecureString& strWalletPassphrase)
+{
+ if (!IsLocked())
+ return false;
+
+ CCrypter crypter;
+ CKeyingMaterial vMasterKey;
+ CRITICAL_BLOCK(cs_wallet)
BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
{
if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
if (CCryptoKeyStore::Unlock(vMasterKey))
return true;
}
- }
return false;
}
-bool CWallet::ChangeWalletPassphrase(const string& strOldWalletPassphrase, const string& strNewWalletPassphrase)
+bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
{
- CRITICAL_BLOCK(cs_vMasterKey)
- {
- bool fWasLocked = IsLocked();
+ bool fWasLocked = IsLocked();
+ CRITICAL_BLOCK(cs_wallet)
+ {
Lock();
CCrypter crypter;
{
if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false;
- if(!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
+ if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
return false;
if (CCryptoKeyStore::Unlock(vMasterKey))
{
}
}
}
+
return false;
}
)
};
-bool CWallet::EncryptWallet(const string& strWalletPassphrase)
+bool CWallet::SetMinVersion(int nVersion, CWalletDB* pwalletdbIn)
{
- CRITICAL_BLOCK(cs_mapPubKeys)
- CRITICAL_BLOCK(cs_KeyStore)
- CRITICAL_BLOCK(cs_vMasterKey)
- CRITICAL_BLOCK(cs_pwalletdbEncryption)
+ if (nWalletVersion >= nVersion)
+ return true;
+
+ nWalletVersion = nVersion;
+
+ if (fFileBacked)
{
- if (IsCrypted())
- return false;
+ 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)
+ pwalletdb->WriteMinVersion(nWalletVersion);
+ if (!pwalletdbIn)
+ delete pwalletdb;
+ }
- CKeyingMaterial vMasterKey;
- RandAddSeedPerfmon();
+ return true;
+}
- vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
- RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
+bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
+{
+ if (IsCrypted())
+ return false;
- CMasterKey kMasterKey;
+ CKeyingMaterial vMasterKey;
+ RandAddSeedPerfmon();
- RandAddSeedPerfmon();
- kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
- RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
+ vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
+ RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
- CCrypter crypter;
- int64 nStartTime = GetTimeMillis();
- crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
- kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
+ CMasterKey kMasterKey;
- nStartTime = GetTimeMillis();
- crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
- kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
+ RandAddSeedPerfmon();
+ kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
+ RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
- if (kMasterKey.nDeriveIterations < 25000)
- kMasterKey.nDeriveIterations = 25000;
+ CCrypter crypter;
+ int64 nStartTime = GetTimeMillis();
+ crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
+ kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
- printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
+ nStartTime = GetTimeMillis();
+ crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
+ kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
- if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
- return false;
- if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))
- return false;
+ if (kMasterKey.nDeriveIterations < 25000)
+ kMasterKey.nDeriveIterations = 25000;
+
+ printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
+
+ if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
+ return false;
+ if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))
+ return false;
+ CRITICAL_BLOCK(cs_wallet)
+ {
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
if (fFileBacked)
{
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
+ SetMinVersion(40000, pwalletdbEncryption);
+
if (fFileBacked)
{
- CCorruptAddress corruptAddress;
- pwalletdbEncryption->WriteSetting("addrIncoming", corruptAddress);
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.
- pwalletdbEncryption->Close();
+ delete pwalletdbEncryption;
pwalletdbEncryption = NULL;
}
Lock();
+ Unlock(strWalletPassphrase);
+ NewKeyPool();
+ Lock();
+
+ // 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.
+ CDB::Rewrite(strWalletFile);
}
+
return true;
}
// 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
// restored from backup or the user making copies of wallet.dat.
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
}
}
+void CWallet::MarkDirty()
+{
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
+ item.second.MarkDirty();
+ }
+}
+
bool CWallet::AddToWallet(const CWalletTx& wtxIn)
{
uint256 hash = wtxIn.GetHash();
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
// 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));
CWalletTx& wtx = (*ret.first).second;
- wtx.pwallet = this;
+ wtx.BindWallet(this);
bool fInsertedNew = ret.second;
if (fInsertedNew)
wtx.nTimeReceived = GetAdjustedTime();
if (fInsertedNew || fUpdated)
if (!wtx.WriteToDisk())
return false;
-
+#ifndef QT_GUI
// If default receiving address gets used, replace it with a new one
CScript scriptDefaultKey;
scriptDefaultKey.SetBitcoinAddress(vchDefaultKey);
{
if (txout.scriptPubKey == scriptDefaultKey)
{
- SetDefaultKey(GetOrReuseKeyFromPool());
- SetAddressBookName(PubKeyToAddress(vchDefaultKey), "");
+ std::vector<unsigned char> newDefaultKey;
+ if (GetKeyFromPool(newDefaultKey, false))
+ {
+ SetDefaultKey(newDefaultKey);
+ SetAddressBookName(CBitcoinAddress(vchDefaultKey), "");
+ }
}
}
-
+#endif
// Notify UI
vWalletUpdated.push_back(hash);
return true;
}
-bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate)
+// 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.
+// If fUpdate is true, existing transactions will be updated.
+bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock)
{
uint256 hash = tx.GetHash();
- bool fExisted = mapWallet.count(hash);
- if (fExisted && !fUpdate) return false;
- if (fExisted || IsMine(tx) || IsFromMe(tx))
+ CRITICAL_BLOCK(cs_wallet)
{
- CWalletTx wtx(this,tx);
- // Get merkle branch if transaction was found in a block
- if (pblock)
- wtx.SetMerkleBranch(pblock);
- return AddToWallet(wtx);
+ bool fExisted = mapWallet.count(hash);
+ if (fExisted && !fUpdate) return false;
+ if (fExisted || IsMine(tx) || IsFromMe(tx))
+ {
+ CWalletTx wtx(this,tx);
+ // Get merkle branch if transaction was found in a block
+ if (pblock)
+ wtx.SetMerkleBranch(pblock);
+ return AddToWallet(wtx);
+ }
+ else
+ WalletUpdateSpent(tx);
}
- else
- WalletUpdateSpent(tx);
return false;
}
{
if (!fFileBacked)
return false;
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
if (mapWallet.erase(hash))
CWalletDB(strWalletFile).EraseTx(hash);
bool CWallet::IsMine(const CTxIn &txin) const
{
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
int64 CWallet::GetDebit(const CTxIn &txin) const
{
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
return 0;
}
+bool CWallet::IsChange(const CTxOut& txout) const
+{
+ CBitcoinAddress address;
+
+ // 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
+ // is change. That assumption is likely to break when we implement multisignature
+ // wallets that return change back into a multi-signature-protected address;
+ // 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
+ // which output, if any, was change).
+ if (ExtractAddress(txout.scriptPubKey, address) && HaveKey(address))
+ CRITICAL_BLOCK(cs_wallet)
+ if (!mapAddressBook.count(address))
+ return true;
+ return false;
+}
+
int64 CWalletTx::GetTxTime() const
{
- if (!fTimeReceivedIsTxTime && hashBlock != 0)
- {
- // If we did not receive the transaction directly, we rely on the block's
- // time to figure out when it happened. We use the median over a range
- // of blocks to try to filter out inaccurate block times.
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
- if (mi != mapBlockIndex.end())
- {
- CBlockIndex* pindex = (*mi).second;
- if (pindex)
- return pindex->GetMedianTime();
- }
- }
return nTimeReceived;
}
{
// Returns -1 if it wasn't being tracked
int nRequests = -1;
- CRITICAL_BLOCK(pwallet->cs_mapRequestCount)
+ CRITICAL_BLOCK(pwallet->cs_wallet)
{
if (IsCoinBase())
{
return nRequests;
}
-void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<string, int64> >& listReceived,
- list<pair<string, int64> >& listSent, int64& nFee, string& strSentAccount) const
+void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CBitcoinAddress, int64> >& listReceived,
+ list<pair<CBitcoinAddress, int64> >& listSent, int64& nFee, string& strSentAccount) const
{
nGeneratedImmature = nGeneratedMature = nFee = 0;
listReceived.clear();
nFee = nDebit - nValueOut;
}
- // Sent/received. Standard client will never generate a send-to-multiple-recipients,
- // but non-standard clients might (so return a list of address/amount pairs)
+ // Sent/received.
BOOST_FOREACH(const CTxOut& txout, vout)
{
- string address;
- uint160 hash160;
+ CBitcoinAddress address;
vector<unsigned char> vchPubKey;
- if (ExtractHash160(txout.scriptPubKey, hash160))
- address = Hash160ToAddress(hash160);
- else if (ExtractPubKey(txout.scriptPubKey, NULL, vchPubKey))
- address = PubKeyToAddress(vchPubKey);
- else
+ if (!ExtractAddress(txout.scriptPubKey, address))
{
printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString().c_str());
int64 allGeneratedImmature, allGeneratedMature, allFee;
allGeneratedImmature = allGeneratedMature = allFee = 0;
string strSentAccount;
- list<pair<string, int64> > listReceived;
- list<pair<string, int64> > listSent;
+ list<pair<CBitcoinAddress, int64> > listReceived;
+ list<pair<CBitcoinAddress, int64> > listSent;
GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
if (strAccount == "")
nGenerated = allGeneratedMature;
if (strAccount == strSentAccount)
{
- BOOST_FOREACH(const PAIRTYPE(string,int64)& s, listSent)
+ BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& s, listSent)
nSent += s.second;
nFee = allFee;
}
- CRITICAL_BLOCK(pwallet->cs_mapAddressBook)
+ CRITICAL_BLOCK(pwallet->cs_wallet)
{
- BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived)
+ BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
{
if (pwallet->mapAddressBook.count(r.first))
{
- map<string, 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)
nReceived += r.second;
}
vWorkQueue.push_back(txin.prevout.hash);
// This critsect is OK because txdb is already open
- CRITICAL_BLOCK(pwallet->cs_mapWallet)
+ CRITICAL_BLOCK(pwallet->cs_wallet)
{
map<uint256, const CMerkleTx*> mapWalletPrev;
set<uint256> setAlreadyDone;
return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
}
+// Scan the block chain (starting in pindexStart) for transactions
+// from or to us. If fUpdate is true, found transactions that already
+// exist in the wallet will be updated.
int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{
int ret = 0;
CBlockIndex* pindex = pindexStart;
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
while (pindex)
{
return ret;
}
+int CWallet::ScanForWalletTransaction(const uint256& hashTx)
+{
+ CTransaction tx;
+ tx.ReadFromDisk(COutPoint(hashTx, 0));
+ if (AddToWalletIfInvolvingMe(tx, NULL, true, true))
+ return 1;
+ return 0;
+}
+
void CWallet::ReacceptWalletTransactions()
{
CTxDB txdb("r");
bool fRepeat = true;
- while (fRepeat) CRITICAL_BLOCK(cs_mapWallet)
+ while (fRepeat) CRITICAL_BLOCK(cs_wallet)
{
fRepeat = false;
vector<CDiskTxPos> vMissingTx;
// Rebroadcast any of our txes that aren't in a block yet
printf("ResendWalletTransactions()\n");
CTxDB txdb("r");
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
// Sort them in chronological order
multimap<unsigned int, CWalletTx*> mapSorted;
int64 CWallet::GetBalance() const
{
int64 nTotal = 0;
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
return nTotal;
}
+int64 CWallet::GetUnconfirmedBalance() const
+{
+ int64 nTotal = 0;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (pcoin->IsFinal() && pcoin->IsConfirmed())
+ continue;
+ nTotal += pcoin->GetAvailableCredit();
+ }
+ }
+ return nTotal;
+}
bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{
// List of values less than target
pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
- coinLowestLarger.first = INT64_MAX;
+ coinLowestLarger.first = std::numeric_limits<int64>::max();
coinLowestLarger.second.first = NULL;
vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue;
int64 nTotalLower = 0;
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
vector<const CWalletTx*> vCoins;
vCoins.reserve(mapWallet.size());
if (vecSend.empty() || nValue < 0)
return false;
- wtxNew.pwallet = this;
+ wtxNew.BindWallet(this);
CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(cs_wallet)
{
// txdb must be opened before the mapWallet lock
CTxDB txdb("r");
- CRITICAL_BLOCK(cs_mapWallet)
{
nFeeRet = nTransactionFee;
loop
dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
}
- // Fill a vout back to self with any change
- int64 nChange = nValueIn - nTotalValue;
- if (nChange >= CENT)
+ int64 nChange = nValueIn - nValue - nFeeRet;
+ // if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE
+ // or until nChange becomes zero
+ // NOTE: this depends on the exact behaviour of GetMinFee
+ if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT)
+ {
+ int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet);
+ nChange -= nMoveToFee;
+ nFeeRet += nMoveToFee;
+ }
+
+ if (nChange > 0)
{
// Note: We use a new key here to keep it from being obvious which side is the change.
// The drawback is that by not reusing a previous key, the change may be lost if a
vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
// assert(mapKeys.count(vchPubKey));
- // Fill a vout to ourself, using same address type as the payment
+ // Fill a vout to ourself
+ // TODO: pass in scriptChange instead of reservekey so
+ // change transaction isn't always pay-to-bitcoin-address
CScript scriptChange;
- if (vecSend[0].first.GetBitcoinAddressHash160() != 0)
- scriptChange.SetBitcoinAddress(vchPubKey);
- else
- scriptChange << vchPubKey << OP_CHECKSIG;
+ scriptChange.SetBitcoinAddress(vchPubKey);
// Insert change txn at random position:
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
// Check that enough fee is included
int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
bool fAllowFree = CTransaction::AllowFree(dPriority);
- int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree);
+ int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND);
if (nFeeRet < max(nPayFee, nMinFee))
{
nFeeRet = max(nPayFee, nMinFee);
bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
{
CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(cs_wallet)
{
printf("CommitTransaction:\n%s", wtxNew.ToString().c_str());
- CRITICAL_BLOCK(cs_mapWallet)
{
// This is only to keep the database open to defeat the auto-flush for the
// duration of this scope. This is the only place where this optimization
BOOST_FOREACH(const CTxIn& txin, wtxNew.vin)
{
CWalletTx &coin = mapWallet[txin.prevout.hash];
- coin.pwallet = this;
+ coin.BindWallet(this);
coin.MarkSpent(txin.prevout.n);
coin.WriteToDisk();
vWalletUpdated.push_back(coin.GetHash());
}
// Track how many getdata requests our transaction gets
- CRITICAL_BLOCK(cs_mapRequestCount)
- mapRequestCount[wtxNew.GetHash()] = 0;
+ mapRequestCount[wtxNew.GetHash()] = 0;
// Broadcast
if (!wtxNew.AcceptToMemoryPool())
-// requires cs_main lock
string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
{
CReserveKey reservekey(this);
int64 nFeeRequired;
- CRITICAL_BLOCK(cs_vMasterKey)
+
+ if (IsLocked())
{
- if (IsLocked())
- {
- string strError = _("Error: Wallet locked, unable to create transaction ");
- printf("SendMoney() : %s", strError.c_str());
- return strError;
- }
- if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
- {
- string strError;
- if (nValue + nFeeRequired > GetBalance())
- strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str());
- else
- strError = _("Error: Transaction creation failed ");
- printf("SendMoney() : %s", strError.c_str());
- return strError;
- }
+ string strError = _("Error: Wallet locked, unable to create transaction ");
+ printf("SendMoney() : %s", strError.c_str());
+ return strError;
+ }
+ if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
+ {
+ string strError;
+ if (nValue + nFeeRequired > GetBalance())
+ strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str());
+ else
+ strError = _("Error: Transaction creation failed ");
+ printf("SendMoney() : %s", strError.c_str());
+ return strError;
}
if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."), NULL))
-// requires cs_main lock
-string CWallet::SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
+string CWallet::SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee)
{
// Check amount
if (nValue <= 0)
// Parse bitcoin address
CScript scriptPubKey;
- if (!scriptPubKey.SetBitcoinAddress(strAddress))
- return _("Invalid bitcoin address");
+ scriptPubKey.SetBitcoinAddress(address);
return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee);
}
return false;
fFirstRunRet = false;
int nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);
+ if (nLoadWalletRet == DB_NEED_REWRITE)
+ {
+ if (CDB::Rewrite(strWalletFile, "\x04pool"))
+ {
+ setKeyPool.clear();
+ // Note: can't top-up keypool here, because wallet is locked.
+ // User will be prompted to unlock wallet the next operation
+ // the requires a new key.
+ }
+ nLoadWalletRet = DB_NEED_REWRITE;
+ }
+
if (nLoadWalletRet != DB_LOAD_OK)
return nLoadWalletRet;
fFirstRunRet = vchDefaultKey.empty();
- if (!HaveKey(vchDefaultKey))
+ if (!HaveKey(Hash160(vchDefaultKey)))
{
// Create new keyUser and set as default key
RandAddSeedPerfmon();
- SetDefaultKey(GetOrReuseKeyFromPool());
- if (!SetAddressBookName(PubKeyToAddress(vchDefaultKey), ""))
+ std::vector<unsigned char> newDefaultKey;
+ if (!GetKeyFromPool(newDefaultKey, false))
+ return DB_LOAD_FAIL;
+ SetDefaultKey(newDefaultKey);
+ if (!SetAddressBookName(CBitcoinAddress(vchDefaultKey), ""))
return DB_LOAD_FAIL;
}
}
-bool CWallet::SetAddressBookName(const string& strAddress, const string& strName)
+bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName)
{
- mapAddressBook[strAddress] = strName;
+ mapAddressBook[address] = strName;
if (!fFileBacked)
return false;
- return CWalletDB(strWalletFile).WriteName(strAddress, strName);
+ return CWalletDB(strWalletFile).WriteName(address.ToString(), strName);
}
-bool CWallet::DelAddressBookName(const string& strAddress)
+bool CWallet::DelAddressBookName(const CBitcoinAddress& address)
{
- mapAddressBook.erase(strAddress);
+ mapAddressBook.erase(address);
if (!fFileBacked)
return false;
- return CWalletDB(strWalletFile).EraseName(strAddress);
+ return CWalletDB(strWalletFile).EraseName(address.ToString());
}
void CWallet::PrintWallet(const CBlock& block)
{
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
if (mapWallet.count(block.vtx[0].GetHash()))
{
bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx)
{
- CRITICAL_BLOCK(cs_mapWallet)
+ CRITICAL_BLOCK(cs_wallet)
{
map<uint256, CWalletTx>::iterator mi = mapWallet.find(hashTx);
if (mi != mapWallet.end())
return true;
}
+//
+// Mark old keypool keys as used,
+// and generate all new keys
+//
+bool CWallet::NewKeyPool()
+{
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ CWalletDB walletdb(strWalletFile);
+ BOOST_FOREACH(int64 nIndex, setKeyPool)
+ walletdb.ErasePool(nIndex);
+ setKeyPool.clear();
+
+ if (IsLocked())
+ return false;
+
+ int64 nKeys = max(GetArg("-keypool", 100), (int64)0);
+ for (int i = 0; i < nKeys; i++)
+ {
+ int64 nIndex = i+1;
+ walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey()));
+ setKeyPool.insert(nIndex);
+ }
+ printf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys);
+ }
+ return true;
+}
+
bool CWallet::TopUpKeyPool()
{
- CRITICAL_BLOCK(cs_main)
- CRITICAL_BLOCK(cs_mapWallet)
- CRITICAL_BLOCK(cs_setKeyPool)
- CRITICAL_BLOCK(cs_vMasterKey)
+ CRITICAL_BLOCK(cs_wallet)
{
if (IsLocked())
return false;
{
nIndex = -1;
keypool.vchPubKey.clear();
- CRITICAL_BLOCK(cs_main)
- CRITICAL_BLOCK(cs_mapWallet)
- CRITICAL_BLOCK(cs_setKeyPool)
+ CRITICAL_BLOCK(cs_wallet)
{
if (!IsLocked())
TopUpKeyPool();
setKeyPool.erase(setKeyPool.begin());
if (!walletdb.ReadPool(nIndex, keypool))
throw runtime_error("ReserveKeyFromKeyPool() : read failed");
- if (!HaveKey(keypool.vchPubKey))
+ if (!HaveKey(Hash160(keypool.vchPubKey)))
throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool");
assert(!keypool.vchPubKey.empty());
printf("keypool reserve %"PRI64d"\n", nIndex);
}
}
+int64 CWallet::AddReserveKey(const CKeyPool& keypool)
+{
+ CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ CWalletDB walletdb(strWalletFile);
+
+ int64 nIndex = 1 + *(--setKeyPool.end());
+ if (!walletdb.WritePool(nIndex, keypool))
+ throw runtime_error("AddReserveKey() : writing added key failed");
+ setKeyPool.insert(nIndex);
+ return nIndex;
+ }
+ return -1;
+}
+
void CWallet::KeepKey(int64 nIndex)
{
// Remove from key pool
if (fFileBacked)
{
CWalletDB walletdb(strWalletFile);
- CRITICAL_BLOCK(cs_main)
- {
- walletdb.ErasePool(nIndex);
- }
+ walletdb.ErasePool(nIndex);
}
printf("keypool keep %"PRI64d"\n", nIndex);
}
void CWallet::ReturnKey(int64 nIndex)
{
// Return to key pool
- CRITICAL_BLOCK(cs_setKeyPool)
+ CRITICAL_BLOCK(cs_wallet)
setKeyPool.insert(nIndex);
printf("keypool return %"PRI64d"\n", nIndex);
}
-vector<unsigned char> CWallet::GetOrReuseKeyFromPool()
+bool CWallet::GetKeyFromPool(vector<unsigned char>& result, bool fAllowReuse)
{
int64 nIndex = 0;
CKeyPool keypool;
- ReserveKeyFromKeyPool(nIndex, keypool);
- if(nIndex == -1)
- return vchDefaultKey;
- KeepKey(nIndex);
- return keypool.vchPubKey;
+ CRITICAL_BLOCK(cs_wallet)
+ {
+ ReserveKeyFromKeyPool(nIndex, keypool);
+ if (nIndex == -1)
+ {
+ if (fAllowReuse && !vchDefaultKey.empty())
+ {
+ result = vchDefaultKey;
+ return true;
+ }
+ if (IsLocked()) return false;
+ result = GenerateNewKey();
+ return true;
+ }
+ KeepKey(nIndex);
+ result = keypool.vchPubKey;
+ }
+ return true;
}
int64 CWallet::GetOldestKeyPoolTime()
vchPubKey.clear();
}
+void CWallet::GetAllReserveAddresses(set<CBitcoinAddress>& setAddress)
+{
+ setAddress.clear();
+
+ CWalletDB walletdb(strWalletFile);
+
+ CRITICAL_BLOCK(cs_main)
+ CRITICAL_BLOCK(cs_wallet)
+ BOOST_FOREACH(const int64& id, setKeyPool)
+ {
+ CKeyPool keypool;
+ if (!walletdb.ReadPool(id, keypool))
+ throw runtime_error("GetAllReserveKeyHashes() : read failed");
+ CBitcoinAddress address(keypool.vchPubKey);
+ assert(!keypool.vchPubKey.empty());
+ if (!HaveKey(address))
+ throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool");
+ setAddress.insert(address);
+ }
+}