X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fwallet.cpp;h=3ffad27e82db00c10e33b017cc22dee786fe78d9;hb=3b36da6d277c6f5ad671343e724e0336ce55c893;hp=a9fc92fd78cef2aa1190866368d2b758d1cecf99;hpb=e89b9f6a2abaa120ff0fc3cea9ae364e8cbd25e4;p=novacoin.git diff --git a/src/wallet.cpp b/src/wallet.cpp index a9fc92f..3ffad27 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1,50 +1,239 @@ -// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2011 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. +// file COPYING 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 // -void WalletUpdateSpent(const COutPoint& prevout) +bool CWallet::AddKey(const CKey& key) +{ + if (!CCryptoKeyStore::AddKey(key)) + return false; + if (!fFileBacked) + return true; + if (!IsCrypted()) + return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); + return true; +} + +bool CWallet::AddCryptedKey(const vector &vchPubKey, const vector &vchCryptedSecret) +{ + if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; + if (!fFileBacked) + return true; + CRITICAL_BLOCK(cs_wallet) + { + if (pwalletdbEncryption) + return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret); + else + return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret); + } + return false; +} + +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)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + return false; + if (CCryptoKeyStore::Unlock(vMasterKey)) + return true; + } + return false; +} + +bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) +{ + bool fWasLocked = IsLocked(); + + CRITICAL_BLOCK(cs_wallet) + { + Lock(); + + CCrypter crypter; + CKeyingMaterial vMasterKey; + BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) + { + if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + return false; + if (CCryptoKeyStore::Unlock(vMasterKey)) + { + int64 nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); + pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); + + nStartTime = GetTimeMillis(); + 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; + + if (pMasterKey.second.nDeriveIterations < 25000) + pMasterKey.second.nDeriveIterations = 25000; + + 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)) + return false; + if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) + return false; + CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); + if (fWasLocked) + Lock(); + return true; + } + } + } + + return false; +} + + +// 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::EncryptWallet(const SecureString& strWalletPassphrase) +{ + if (IsCrypted()) + return false; + + CKeyingMaterial vMasterKey; + RandAddSeedPerfmon(); + + vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); + RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); + + CMasterKey kMasterKey; + + RandAddSeedPerfmon(); + kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); + RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); + + CCrypter crypter; + int64 nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); + kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); + + nStartTime = GetTimeMillis(); + crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); + kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + + 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) + { + pwalletdbEncryption = new CWalletDB(strWalletFile); + if (!pwalletdbEncryption->TxnBegin()) + return false; + pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); + } + + if (!EncryptKeys(vMasterKey)) + { + if (fFileBacked) + 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. + } + + 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. + + 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; +} + +void CWallet::WalletUpdateSpent(const CTransaction &tx) { // 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) { - map::iterator mi = mapWallet.find(prevout.hash); - if (mi != mapWallet.end()) + BOOST_FOREACH(const CTxIn& txin, tx.vin) { - CWalletTx& wtx = (*mi).second; - if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine()) + map::iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) { - printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); - wtx.MarkSpent(prevout.n); - wtx.WriteToDisk(); - vWalletUpdated.push_back(prevout.hash); + CWalletTx& wtx = (*mi).second; + if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) + { + printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + wtx.MarkSpent(txin.prevout.n); + wtx.WriteToDisk(); + vWalletUpdated.push_back(txin.prevout.hash); + } } } } } -bool AddToWallet(const CWalletTx& wtxIn) +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::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); CWalletTx& wtx = (*ret.first).second; + wtx.pwallet = this; bool fInsertedNew = ret.second; if (fInsertedNew) wtx.nTimeReceived = GetAdjustedTime(); @@ -79,7 +268,7 @@ bool AddToWallet(const CWalletTx& wtxIn) 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); @@ -87,15 +276,20 @@ bool AddToWallet(const CWalletTx& wtxIn) { if (txout.scriptPubKey == scriptDefaultKey) { - CWalletDB walletdb; - vchDefaultKey = GetKeyFromKeyPool(); - walletdb.WriteDefaultKey(vchDefaultKey); - walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); + std::vector newDefaultKey; + if (GetKeyFromPool(newDefaultKey, false)) + { + SetDefaultKey(newDefaultKey); + SetAddressBookName(CBitcoinAddress(vchDefaultKey), ""); + } } } - +#endif // Notify UI vWalletUpdated.push_back(hash); + + // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins + WalletUpdateSpent(wtx); } // Refresh UI @@ -103,60 +297,70 @@ bool AddToWallet(const CWalletTx& wtxIn) return true; } -bool 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) { uint256 hash = tx.GetHash(); - bool fExisted = mapWallet.count(hash); - if (fExisted && !fUpdate) return false; - if (fExisted || tx.IsMine() || tx.IsFromMe()) + CRITICAL_BLOCK(cs_wallet) { - CWalletTx wtx(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); } return false; } -bool EraseFromWallet(uint256 hash) +bool CWallet::EraseFromWallet(uint256 hash) { - CRITICAL_BLOCK(cs_mapWallet) + if (!fFileBacked) + return false; + CRITICAL_BLOCK(cs_wallet) { if (mapWallet.erase(hash)) - CWalletDB().EraseTx(hash); + CWalletDB(strWalletFile).EraseTx(hash); } return true; } -bool CTxIn::IsMine() const +bool CWallet::IsMine(const CTxIn &txin) const { - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { - map::iterator mi = mapWallet.find(prevout.hash); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; - if (prevout.n < prev.vout.size()) - if (prev.vout[prevout.n].IsMine()) + if (txin.prevout.n < prev.vout.size()) + if (IsMine(prev.vout[txin.prevout.n])) return true; } } return false; } -int64 CTxIn::GetDebit() const +int64 CWallet::GetDebit(const CTxIn &txin) const { - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { - map::iterator mi = mapWallet.find(prevout.hash); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; - if (prevout.n < prev.vout.size()) - if (prev.vout[prevout.n].IsMine()) - return prev.vout[prevout.n].nValue; + if (txin.prevout.n < prev.vout.size()) + if (IsMine(prev.vout[txin.prevout.n])) + return prev.vout[txin.prevout.n].nValue; } } return 0; @@ -164,19 +368,6 @@ int64 CTxIn::GetDebit() const 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::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex) - return pindex->GetMedianTime(); - } - } return nTimeReceived; } @@ -184,31 +375,31 @@ int CWalletTx::GetRequestCount() const { // Returns -1 if it wasn't being tracked int nRequests = -1; - CRITICAL_BLOCK(cs_mapRequestCount) + CRITICAL_BLOCK(pwallet->cs_wallet) { if (IsCoinBase()) { // Generated block if (hashBlock != 0) { - map::iterator mi = mapRequestCount.find(hashBlock); - if (mi != mapRequestCount.end()) + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) nRequests = (*mi).second; } } else { // Did anyone request this transaction? - map::iterator mi = mapRequestCount.find(GetHash()); - if (mi != mapRequestCount.end()) + map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); + if (mi != pwallet->mapRequestCount.end()) { nRequests = (*mi).second; // How about the block it's in? if (nRequests == 0 && hashBlock != 0) { - map::iterator mi = mapRequestCount.find(hashBlock); - if (mi != mapRequestCount.end()) + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) nRequests = (*mi).second; else nRequests = 1; // If it's in someone else's block it must have got out @@ -219,8 +410,8 @@ int CWalletTx::GetRequestCount() const return nRequests; } -void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, - list >& listSent, int64& nFee, string& strSentAccount) const +void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list >& listReceived, + list >& listSent, int64& nFee, string& strSentAccount) const { nGeneratedImmature = nGeneratedMature = nFee = 0; listReceived.clear(); @@ -230,7 +421,7 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l if (IsCoinBase()) { if (GetBlocksToMaturity() > 0) - nGeneratedImmature = CTransaction::GetCredit(); + nGeneratedImmature = pwallet->GetCredit(*this); else nGeneratedMature = GetCredit(); return; @@ -248,14 +439,9 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l // but non-standard clients might (so return a list of address/amount pairs) BOOST_FOREACH(const CTxOut& txout, vout) { - string address; - uint160 hash160; + CBitcoinAddress address; vector vchPubKey; - if (ExtractHash160(txout.scriptPubKey, hash160)) - address = Hash160ToAddress(hash160); - else if (ExtractPubKey(txout.scriptPubKey, false, vchPubKey)) - address = PubKeyToAddress(vchPubKey); - else + if (!ExtractAddress(txout.scriptPubKey, NULL, address)) { printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString().c_str()); @@ -263,13 +449,13 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l } // Don't report 'change' txouts - if (nDebit > 0 && txout.IsChange()) + if (nDebit > 0 && pwallet->IsChange(txout)) continue; if (nDebit > 0) listSent.push_back(make_pair(address, txout.nValue)); - if (txout.IsMine()) + if (pwallet->IsMine(txout)) listReceived.push_back(make_pair(address, txout.nValue)); } @@ -283,28 +469,27 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, i int64 allGeneratedImmature, allGeneratedMature, allFee; allGeneratedImmature = allGeneratedMature = allFee = 0; string strSentAccount; - list > listReceived; - list > listSent; + list > listReceived; + list > 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(cs_mapAddressBook) + CRITICAL_BLOCK(pwallet->cs_wallet) { - BOOST_FOREACH(const PAIRTYPE(string,int64)& r, listReceived) + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived) { - if (mapAddressBook.count(r.first)) + if (pwallet->mapAddressBook.count(r.first)) { - if (mapAddressBook[r.first] == strAccount) - { + map::const_iterator mi = pwallet->mapAddressBook.find(r.first); + if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount) nReceived += r.second; - } } else if (strAccount.empty()) { @@ -326,11 +511,11 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb) vWorkQueue.push_back(txin.prevout.hash); // This critsect is OK because txdb is already open - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(pwallet->cs_wallet) { map mapWalletPrev; set setAlreadyDone; - for (int i = 0; i < vWorkQueue.size(); i++) + for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hash = vWorkQueue[i]; if (setAlreadyDone.count(hash)) @@ -338,10 +523,11 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb) setAlreadyDone.insert(hash); CMerkleTx tx; - if (mapWallet.count(hash)) + map::const_iterator mi = pwallet->mapWallet.find(hash); + if (mi != pwallet->mapWallet.end()) { - tx = mapWallet[hash]; - BOOST_FOREACH(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev) + tx = (*mi).second; + BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev) mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; } else if (mapWalletPrev.count(hash)) @@ -362,8 +548,10 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb) vtxPrev.push_back(tx); if (nDepth < COPY_DEPTH) + { BOOST_FOREACH(const CTxIn& txin, tx.vin) vWorkQueue.push_back(txin.prevout.hash); + } } } } @@ -373,15 +561,18 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb) bool CWalletTx::WriteToDisk() { - return CWalletDB().WriteTx(GetHash(), *this); + return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); } -int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +// 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) { @@ -398,11 +589,11 @@ int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) return ret; } -void ReacceptWalletTransactions() +void CWallet::ReacceptWalletTransactions() { CTxDB txdb("r"); bool fRepeat = true; - while (fRepeat) CRITICAL_BLOCK(cs_mapWallet) + while (fRepeat) CRITICAL_BLOCK(cs_wallet) { fRepeat = false; vector vMissingTx; @@ -422,11 +613,11 @@ void ReacceptWalletTransactions() printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size()); continue; } - for (int i = 0; i < txindex.vSpent.size(); i++) + for (unsigned int i = 0; i < txindex.vSpent.size(); i++) { if (wtx.IsSpent(i)) continue; - if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine()) + if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i])) { wtx.MarkSpent(i); fUpdated = true; @@ -484,7 +675,7 @@ void CWalletTx::RelayWalletTransaction() RelayWalletTransaction(txdb); } -void ResendWalletTransactions() +void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away // that these are our transactions. @@ -505,7 +696,7 @@ void ResendWalletTransactions() // 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 mapSorted; @@ -536,48 +727,60 @@ void ResendWalletTransactions() // -int64 GetBalance() +int64 CWallet::GetBalance() const { - int64 nStart = GetTimeMillis(); - int64 nTotal = 0; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &(*it).second; if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) continue; nTotal += pcoin->GetAvailableCredit(); } } - //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart); return nTotal; } +int64 CWallet::GetUnconfirmedBalance() const +{ + int64 nTotal = 0; + CRITICAL_BLOCK(cs_wallet) + { + for (map::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 SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) +bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) const { setCoinsRet.clear(); nValueRet = 0; // List of values less than target - pair > coinLowestLarger; + pair > coinLowestLarger; coinLowestLarger.first = INT64_MAX; coinLowestLarger.second.first = NULL; - vector > > vValue; + vector > > vValue; int64 nTotalLower = 0; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { - vector vCoins; + vector vCoins; vCoins.reserve(mapWallet.size()); - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) vCoins.push_back(&(*it).second); random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); - BOOST_FOREACH(CWalletTx* pcoin, vCoins) + BOOST_FOREACH(const CWalletTx* pcoin, vCoins) { if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) continue; @@ -589,9 +792,9 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set< if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) continue; - for (int i = 0; i < pcoin->vout.size(); i++) + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine()) + if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i])) continue; int64 n = pcoin->vout[i].nValue; @@ -599,7 +802,7 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set< if (n <= 0) continue; - pair > coin = make_pair(n,make_pair(pcoin,i)); + pair > coin = make_pair(n,make_pair(pcoin,i)); if (n == nTargetValue) { @@ -622,7 +825,7 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set< if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT) { - for (int i = 0; i < vValue.size(); ++i) + for (unsigned int i = 0; i < vValue.size(); ++i) { setCoinsRet.insert(vValue[i].second); nValueRet += vValue[i].first; @@ -655,7 +858,7 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set< bool fReachedTarget = false; for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) { - for (int i = 0; i < vValue.size(); i++) + for (unsigned int i = 0; i < vValue.size(); i++) { if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) { @@ -684,7 +887,7 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set< nValueRet += coinLowestLarger.first; } else { - for (int i = 0; i < vValue.size(); i++) + for (unsigned int i = 0; i < vValue.size(); i++) if (vfBest[i]) { setCoinsRet.insert(vValue[i].second); @@ -693,7 +896,7 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set< //// debug print printf("SelectCoins() best subset: "); - for (int i = 0; i < vValue.size(); i++) + for (unsigned int i = 0; i < vValue.size(); i++) if (vfBest[i]) printf("%s ", FormatMoney(vValue[i].first).c_str()); printf("total %s\n", FormatMoney(nBest).c_str()); @@ -702,7 +905,7 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set< return true; } -bool SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) +bool CWallet::SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) const { return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || @@ -712,7 +915,7 @@ bool SelectCoins(int64 nTargetValue, set >& setCoi -bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) { int64 nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) @@ -724,11 +927,13 @@ bool CreateTransaction(const vector >& vecSend, CWalletTx& if (vecSend.empty() || nValue < 0) return false; + wtxNew.pwallet = 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 @@ -744,19 +949,27 @@ bool CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew.vout.push_back(CTxOut(s.second, s.first)); // Choose coins to use - set > setCoins; + set > setCoins; int64 nValueIn = 0; if (!SelectCoins(nTotalValue, setCoins, nValueIn)) return false; - BOOST_FOREACH(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins) + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { int64 nCredit = pcoin.first->vout[pcoin.second].nValue; 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 + 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 @@ -767,11 +980,11 @@ bool CreateTransaction(const vector >& vecSend, CWalletTx& // Reserve a new key pair from key pool vector vchPubKey = reservekey.GetReservedKey(); - assert(mapKeys.count(vchPubKey)); + // assert(mapKeys.count(vchPubKey)); // Fill a vout to ourself, using same address type as the payment CScript scriptChange; - if (vecSend[0].first.GetBitcoinAddressHash160() != 0) + if (vecSend[0].first.GetBitcoinAddress().IsValid()) scriptChange.SetBitcoinAddress(vchPubKey); else scriptChange << vchPubKey << OP_CHECKSIG; @@ -784,13 +997,13 @@ bool CreateTransaction(const vector >& vecSend, CWalletTx& reservekey.ReturnKey(); // Fill vin - BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); // Sign int nIn = 0; - BOOST_FOREACH(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins) - if (!SignSignature(*coin.first, wtxNew, nIn++)) + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) + if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) return false; // Limit size @@ -820,7 +1033,7 @@ bool CreateTransaction(const vector >& vecSend, CWalletTx& return true; } -bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) { vector< pair > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); @@ -828,17 +1041,17 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CR } // Call after CreateTransaction unless you want to abort -bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) +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 // maybe makes sense; please don't do it anywhere else. - CWalletDB walletdb("r"); + CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : NULL; // Take key pair from key pool so it won't be used again reservekey.KeepKey(); @@ -851,16 +1064,19 @@ bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) set setCoins; BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) { - CWalletTx &pcoin = mapWallet[txin.prevout.hash]; - pcoin.MarkSpent(txin.prevout.n); - pcoin.WriteToDisk(); - vWalletUpdated.push_back(pcoin.GetHash()); + CWalletTx &coin = mapWallet[txin.prevout.hash]; + coin.pwallet = this; + coin.MarkSpent(txin.prevout.n); + coin.WriteToDisk(); + vWalletUpdated.push_back(coin.GetHash()); } + + if (fFileBacked) + delete pwalletdb; } // Track how many getdata requests our transaction gets - CRITICAL_BLOCK(cs_mapRequestCount) - mapRequestCount[wtxNew.GetHash()] = 0; + mapRequestCount[wtxNew.GetHash()] = 0; // Broadcast if (!wtxNew.AcceptToMemoryPool()) @@ -878,11 +1094,17 @@ bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -// requires cs_main lock -string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) { - CReserveKey reservekey; + CReserveKey reservekey(this); int64 nFeeRequired; + + 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; @@ -906,8 +1128,7 @@ string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAs -// requires cs_main lock -string 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) @@ -917,8 +1138,7 @@ string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtx // Parse bitcoin address CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - return _("Invalid bitcoin address"); + scriptPubKey.SetBitcoinAddress(address); return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); } @@ -926,42 +1146,146 @@ string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtx -bool LoadWallet(bool& fFirstRunRet) +int CWallet::LoadWallet(bool& fFirstRunRet) { - fFirstRunRet = false; - if (!CWalletDB("cr+").LoadWallet()) + if (!fFileBacked) return false; - fFirstRunRet = vchDefaultKey.empty(); - - if (mapKeys.count(vchDefaultKey)) + fFirstRunRet = false; + int nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); + if (nLoadWalletRet == DB_NEED_REWRITE) { - // Set keyUser - keyUser.SetPubKey(vchDefaultKey); - keyUser.SetPrivKey(mapKeys[vchDefaultKey]); + 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; } - else + + if (nLoadWalletRet != DB_LOAD_OK) + return nLoadWalletRet; + fFirstRunRet = vchDefaultKey.empty(); + + if (!HaveKey(Hash160(vchDefaultKey))) { // Create new keyUser and set as default key RandAddSeedPerfmon(); - CWalletDB walletdb; - vchDefaultKey = GetKeyFromKeyPool(); - walletdb.WriteDefaultKey(vchDefaultKey); - walletdb.WriteName(PubKeyToAddress(vchDefaultKey), ""); + std::vector newDefaultKey; + if (!GetKeyFromPool(newDefaultKey, false)) + return DB_LOAD_FAIL; + SetDefaultKey(newDefaultKey); + if (!SetAddressBookName(CBitcoinAddress(vchDefaultKey), "")) + return DB_LOAD_FAIL; + } + + CreateThread(ThreadFlushWalletDB, &strWalletFile); + return DB_LOAD_OK; +} + + +bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName) +{ + mapAddressBook[address] = strName; + if (!fFileBacked) + return false; + return CWalletDB(strWalletFile).WriteName(address.ToString(), strName); +} + +bool CWallet::DelAddressBookName(const CBitcoinAddress& address) +{ + mapAddressBook.erase(address); + if (!fFileBacked) + return false; + return CWalletDB(strWalletFile).EraseName(address.ToString()); +} + + +void CWallet::PrintWallet(const CBlock& block) +{ + CRITICAL_BLOCK(cs_wallet) + { + if (mapWallet.count(block.vtx[0].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; + printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); + } + } + printf("\n"); +} + +bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) +{ + CRITICAL_BLOCK(cs_wallet) + { + map::iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) + { + wtx = (*mi).second; + return true; + } + } + return false; +} + +bool CWallet::SetDefaultKey(const std::vector &vchPubKey) +{ + if (fFileBacked) + { + if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) + return false; } + vchDefaultKey = vchPubKey; + return true; +} - CreateThread(ThreadFlushWalletDB, NULL); +bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) +{ + if (!pwallet->fFileBacked) + return false; + strWalletFileOut = pwallet->strWalletFile; return true; } -void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +// +// Mark old keypool keys as used, +// and generate all new keys +// +bool CWallet::NewKeyPool() { - nIndex = -1; - keypool.vchPubKey.clear(); - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_setKeyPool) + 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_wallet) { + if (IsLocked()) + return false; + + CWalletDB walletdb(strWalletFile); + // Top up key pool int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); while (setKeyPool.size() < nTargetSize+1) @@ -969,71 +1293,108 @@ void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) int64 nEnd = 1; if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; - if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey()))) - throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed"); + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) + throw runtime_error("TopUpKeyPool() : writing generated key failed"); setKeyPool.insert(nEnd); printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); } + } + return true; +} + +void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +{ + nIndex = -1; + keypool.vchPubKey.clear(); + CRITICAL_BLOCK(cs_wallet) + { + if (!IsLocked()) + TopUpKeyPool(); // Get the oldest key - assert(!setKeyPool.empty()); + if(setKeyPool.empty()) + return; + + CWalletDB walletdb(strWalletFile); + nIndex = *(setKeyPool.begin()); setKeyPool.erase(setKeyPool.begin()); - if (!Read(make_pair(string("pool"), nIndex), keypool)) + if (!walletdb.ReadPool(nIndex, keypool)) throw runtime_error("ReserveKeyFromKeyPool() : read failed"); - if (!mapKeys.count(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); } } -void CWalletDB::KeepKey(int64 nIndex) +void CWallet::KeepKey(int64 nIndex) { // Remove from key pool - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) + if (fFileBacked) { - Erase(make_pair(string("pool"), nIndex)); + CWalletDB walletdb(strWalletFile); + walletdb.ErasePool(nIndex); } printf("keypool keep %"PRI64d"\n", nIndex); } -void CWalletDB::ReturnKey(int64 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 GetKeyFromKeyPool() +bool CWallet::GetKeyFromPool(vector& result, bool fAllowReuse) { - CWalletDB walletdb; int64 nIndex = 0; CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.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 GetOldestKeyPoolTime() +int64 CWallet::GetOldestKeyPoolTime() { - CWalletDB walletdb; int64 nIndex = 0; CKeyPool keypool; - walletdb.ReserveKeyFromKeyPool(nIndex, keypool); - walletdb.ReturnKey(nIndex); + ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex == -1) + return GetTime(); + ReturnKey(nIndex); return keypool.nTime; } -std::vector CReserveKey::GetReservedKey() +vector CReserveKey::GetReservedKey() { if (nIndex == -1) { CKeyPool keypool; - CWalletDB().ReserveKeyFromKeyPool(nIndex, keypool); - vchPubKey = keypool.vchPubKey; + pwallet->ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex != -1) + vchPubKey = keypool.vchPubKey; + else + { + printf("CReserveKey::GetReservedKey(): Warning: using default key instead of a new key, top up your keypool."); + vchPubKey = pwallet->vchDefaultKey; + } } assert(!vchPubKey.empty()); return vchPubKey; @@ -1042,7 +1403,7 @@ std::vector CReserveKey::GetReservedKey() void CReserveKey::KeepKey() { if (nIndex != -1) - CWalletDB().KeepKey(nIndex); + pwallet->KeepKey(nIndex); nIndex = -1; vchPubKey.clear(); } @@ -1050,7 +1411,8 @@ void CReserveKey::KeepKey() void CReserveKey::ReturnKey() { if (nIndex != -1) - CWalletDB::ReturnKey(nIndex); + pwallet->ReturnKey(nIndex); nIndex = -1; vchPubKey.clear(); } +