X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fwallet.cpp;h=20336a380cfc52ba0ea980ade86990797936b8ed;hb=21d9f36781604e4ca9fc35dc65265593423b73e9;hp=93313e7b27a8d7972437e0823f2c0ddf7f0c191d;hpb=7414733beac92ce8ba365def592d0363fb24872c;p=novacoin.git diff --git a/src/wallet.cpp b/src/wallet.cpp index 93313e7..20336a3 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1,10 +1,12 @@ -// 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. +#include + #include "headers.h" #include "db.h" -#include "cryptopp/sha.h" #include "crypter.h" using namespace std; @@ -23,6 +25,7 @@ bool CWallet::AddKey(const CKey& key) return true; if (!IsCrypted()) return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); + return true; } bool CWallet::AddCryptedKey(const vector &vchPubKey, const vector &vchCryptedSecret) @@ -31,25 +34,34 @@ bool CWallet::AddCryptedKey(const vector &vchPubKey, const vector 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 uint160 &hash, const CScript& redeemScript) { - CRITICAL_BLOCK(cs_vMasterKey) - { - if (!IsLocked()) - return false; + if (!CCryptoKeyStore::AddCScript(hash, redeemScript)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteCScript(hash, 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)) @@ -59,16 +71,15 @@ bool CWallet::Unlock(const string& strWalletPassphrase) 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; @@ -77,11 +88,11 @@ bool CWallet::ChangeWalletPassphrase(const string& strOldWalletPassphrase, const { 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)) { - int64 nStartTime = GetTimeMillis(); + int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); @@ -105,6 +116,7 @@ bool CWallet::ChangeWalletPassphrase(const string& strOldWalletPassphrase, const } } } + return false; } @@ -121,47 +133,44 @@ public: ) }; -bool CWallet::EncryptWallet(const string& strWalletPassphrase) +bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { - CRITICAL_BLOCK(cs_mapPubKeys) - CRITICAL_BLOCK(cs_KeyStore) - CRITICAL_BLOCK(cs_vMasterKey) - CRITICAL_BLOCK(cs_pwalletdbEncryption) - { - if (IsCrypted()) - return false; + if (IsCrypted()) + return false; - CKeyingMaterial vMasterKey; - RandAddSeedPerfmon(); + CKeyingMaterial vMasterKey; + RandAddSeedPerfmon(); - vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); - RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); + vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); + RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); - CMasterKey kMasterKey; + CMasterKey kMasterKey; - RandAddSeedPerfmon(); - kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); - RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); + 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)); + CCrypter crypter; + int64_t 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; + 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; + if (kMasterKey.nDeriveIterations < 25000) + kMasterKey.nDeriveIterations = 25000; - printf("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)) - return false; - if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) - return false; + 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) { @@ -189,7 +198,15 @@ bool CWallet::EncryptWallet(const string& strWalletPassphrase) } 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; } @@ -198,7 +215,7 @@ 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) { BOOST_FOREACH(const CTxIn& txin, tx.vin) { @@ -218,15 +235,24 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx) } } +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::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(); @@ -261,16 +287,23 @@ bool CWallet::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); BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (txout.scriptPubKey == scriptDefaultKey) - SetDefaultKey(GetOrReuseKeyFromPool()); + { + std::vector newDefaultKey; + if (GetKeyFromPool(newDefaultKey, false)) + { + SetDefaultKey(newDefaultKey); + SetAddressBookName(CBitcoinAddress(vchDefaultKey), ""); + } + } } - +#endif // Notify UI vWalletUpdated.push_back(hash); @@ -283,21 +316,27 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) 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; } @@ -305,7 +344,7 @@ bool CWallet::EraseFromWallet(uint256 hash) { if (!fFileBacked) return false; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { if (mapWallet.erase(hash)) CWalletDB(strWalletFile).EraseTx(hash); @@ -316,7 +355,7 @@ bool CWallet::EraseFromWallet(uint256 hash) bool CWallet::IsMine(const CTxIn &txin) const { - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) @@ -330,9 +369,9 @@ bool CWallet::IsMine(const CTxIn &txin) const return false; } -int64 CWallet::GetDebit(const CTxIn &txin) const +int64_t CWallet::GetDebit(const CTxIn &txin) const { - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) @@ -346,21 +385,26 @@ int64 CWallet::GetDebit(const CTxIn &txin) const return 0; } -int64 CWalletTx::GetTxTime() const +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, this, address)) + CRITICAL_BLOCK(cs_wallet) + if (!mapAddressBook.count(address)) + return true; + return false; +} + +int64_t 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; } @@ -368,7 +412,7 @@ int CWalletTx::GetRequestCount() const { // Returns -1 if it wasn't being tracked int nRequests = -1; - CRITICAL_BLOCK(pwallet->cs_mapRequestCount) + CRITICAL_BLOCK(pwallet->cs_wallet) { if (IsCoinBase()) { @@ -403,8 +447,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_t& nGeneratedImmature, int64_t& nGeneratedMature, list >& listReceived, + list >& listSent, int64_t& nFee, string& strSentAccount) const { nGeneratedImmature = nGeneratedMature = nFee = 0; listReceived.clear(); @@ -421,25 +465,19 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l } // Compute fee: - int64 nDebit = GetDebit(); + int64_t nDebit = GetDebit(); if (nDebit > 0) // debit>0 means we signed/sent this transaction { - int64 nValueOut = GetValueOut(); + int64_t nValueOut = GetValueOut(); 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 vchPubKey; - if (ExtractHash160(txout.scriptPubKey, hash160)) - address = Hash160ToAddress(hash160); - else if (ExtractPubKey(txout.scriptPubKey, NULL, 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()); @@ -459,33 +497,33 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l } -void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, - int64& nSent, int64& nFee) const +void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nGenerated, int64_t& nReceived, + int64_t& nSent, int64_t& nFee) const { nGenerated = nReceived = nSent = nFee = 0; - int64 allGeneratedImmature, allGeneratedMature, allFee; + int64_t 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_t)& 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_t)& r, listReceived) { if (pwallet->mapAddressBook.count(r.first)) { - map::const_iterator mi = pwallet->mapAddressBook.find(r.first); + map::const_iterator mi = pwallet->mapAddressBook.find(r.first); if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount) nReceived += r.second; } @@ -509,7 +547,7 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb) 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 mapWalletPrev; set setAlreadyDone; @@ -560,12 +598,15 @@ bool CWalletTx::WriteToDisk() 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) { @@ -582,11 +623,20 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) 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 vMissingTx; @@ -672,7 +722,7 @@ void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away // that these are our transactions. - static int64 nNextTime; + static int64_t nNextTime; if (GetTime() < nNextTime) return; bool fFirst = (nNextTime == 0); @@ -681,7 +731,7 @@ void CWallet::ResendWalletTransactions() return; // Only do it if there's been a new block since last time - static int64 nLastTime; + static int64_t nLastTime; if (nTimeBestReceived < nLastTime) return; nLastTime = GetTime(); @@ -689,7 +739,7 @@ void CWallet::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; @@ -698,7 +748,7 @@ void CWallet::ResendWalletTransactions() CWalletTx& wtx = item.second; // Don't rebroadcast until it's had plenty of time that // it should have gotten in already by now. - if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) + if (nTimeBestReceived - (int64_t)wtx.nTimeReceived > 5 * 60) mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); } BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) @@ -720,12 +770,10 @@ void CWallet::ResendWalletTransactions() // -int64 CWallet::GetBalance() const +int64_t CWallet::GetBalance() const { - int64 nStart = GetTimeMillis(); - - int64 nTotal = 0; - CRITICAL_BLOCK(cs_mapWallet) + int64_t nTotal = 0; + CRITICAL_BLOCK(cs_wallet) { for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { @@ -736,24 +784,38 @@ int64 CWallet::GetBalance() const } } - //printf("GetBalance() %"PRI64d"ms\n", GetTimeMillis() - nStart); return nTotal; } +int64_t CWallet::GetUnconfirmedBalance() const +{ + int64_t 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 CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64& nValueRet) const +bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfTheirs, set >& setCoinsRet, int64_t& nValueRet) const { setCoinsRet.clear(); nValueRet = 0; // List of values less than target - pair > coinLowestLarger; - coinLowestLarger.first = INT64_MAX; + pair > coinLowestLarger; + coinLowestLarger.first = std::numeric_limits::max(); coinLowestLarger.second.first = NULL; - vector > > vValue; - int64 nTotalLower = 0; + vector > > vValue; + int64_t nTotalLower = 0; - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { vector vCoins; vCoins.reserve(mapWallet.size()); @@ -778,12 +840,12 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i])) continue; - int64 n = pcoin->vout[i].nValue; + int64_t n = pcoin->vout[i].nValue; 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) { @@ -830,12 +892,12 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe sort(vValue.rbegin(), vValue.rend()); vector vfIncluded; vector vfBest(vValue.size(), true); - int64 nBest = nTotalLower; + int64_t nBest = nTotalLower; for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) { vfIncluded.assign(vValue.size(), false); - int64 nTotal = 0; + int64_t nTotal = 0; bool fReachedTarget = false; for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) { @@ -886,7 +948,7 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe return true; } -bool CWallet::SelectCoins(int64 nTargetValue, set >& setCoinsRet, int64& nValueRet) const +bool CWallet::SelectCoins(int64_t nTargetValue, set >& setCoinsRet, int64_t& nValueRet) const { return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) || @@ -896,10 +958,10 @@ bool CWallet::SelectCoins(int64 nTargetValue, set >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet) { - int64 nValue = 0; - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + int64_t nValue = 0; + BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) { if (nValue < 0) return false; @@ -908,13 +970,13 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW 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 @@ -923,26 +985,35 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW wtxNew.vout.clear(); wtxNew.fFromMe = true; - int64 nTotalValue = nValue + nFeeRet; + int64_t nTotalValue = nValue + nFeeRet; double dPriority = 0; // vouts to the payees - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) wtxNew.vout.push_back(CTxOut(s.second, s.first)); // Choose coins to use set > setCoins; - int64 nValueIn = 0; + int64_t nValueIn = 0; if (!SelectCoins(nTotalValue, setCoins, nValueIn)) return false; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { - int64 nCredit = pcoin.first->vout[pcoin.second].nValue; + int64_t 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_t 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_t 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 @@ -955,12 +1026,11 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW vector 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::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); @@ -986,9 +1056,9 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW dPriority /= nBytes; // Check that enough fee is included - int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); + int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); bool fAllowFree = CTransaction::AllowFree(dPriority); - int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree); + int64_t nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND); if (nFeeRet < max(nPayFee, nMinFee)) { nFeeRet = max(nPayFee, nMinFee); @@ -1006,9 +1076,9 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW return true; } -bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +bool CWallet::CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet) { - vector< pair > vecSend; + vector< pair > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); } @@ -1017,9 +1087,9 @@ bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& w 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 @@ -1038,7 +1108,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) 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()); @@ -1049,8 +1119,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) } // Track how many getdata requests our transaction gets - CRITICAL_BLOCK(cs_mapRequestCount) - mapRequestCount[wtxNew.GetHash()] = 0; + mapRequestCount[wtxNew.GetHash()] = 0; // Broadcast if (!wtxNew.AcceptToMemoryPool()) @@ -1068,29 +1137,26 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -// requires cs_main lock -string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee) { CReserveKey reservekey(this); - int64 nFeeRequired; - CRITICAL_BLOCK(cs_vMasterKey) + int64_t nFeeRequired; + + 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)) @@ -1105,8 +1171,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, -// requires cs_main lock -string CWallet::SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +string CWallet::SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64_t nValue, CWalletTx& wtxNew, bool fAskFee) { // Check amount if (nValue <= 0) @@ -1116,8 +1181,7 @@ string CWallet::SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWall // Parse bitcoin address CScript scriptPubKey; - if (!scriptPubKey.SetBitcoinAddress(strAddress)) - return _("Invalid bitcoin address"); + scriptPubKey.SetBitcoinAddress(address); return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); } @@ -1125,50 +1189,66 @@ string CWallet::SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWall -bool CWallet::LoadWallet(bool& fFirstRunRet) +int CWallet::LoadWallet(bool& fFirstRunRet) { if (!fFileBacked) return false; fFirstRunRet = false; - if (!CWalletDB(strWalletFile,"cr+").LoadWallet(this)) - return 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), "")) - return false; + 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 true; + return DB_LOAD_OK; } -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())) { @@ -1181,7 +1261,7 @@ void CWallet::PrintWallet(const CBlock& block) bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) { - CRITICAL_BLOCK(cs_mapWallet) + CRITICAL_BLOCK(cs_wallet) { map::iterator mi = mapWallet.find(hashTx); if (mi != mapWallet.end()) @@ -1212,12 +1292,37 @@ bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) 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_t nIndex, setKeyPool) + walletdb.ErasePool(nIndex); + setKeyPool.clear(); + + if (IsLocked()) + return false; + + int64_t nKeys = max(GetArg("-keypool", 100), (int64_t)0); + for (int i = 0; i < nKeys; i++) + { + int64_t 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; @@ -1225,10 +1330,10 @@ bool CWallet::TopUpKeyPool() CWalletDB walletdb(strWalletFile); // Top up key pool - int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0); + int64_t nTargetSize = max(GetArg("-keypool", 100), (int64_t)0); while (setKeyPool.size() < nTargetSize+1) { - int64 nEnd = 1; + int64_t nEnd = 1; if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) @@ -1240,13 +1345,11 @@ bool CWallet::TopUpKeyPool() return true; } -void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) { nIndex = -1; keypool.vchPubKey.clear(); - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapWallet) - CRITICAL_BLOCK(cs_setKeyPool) + CRITICAL_BLOCK(cs_wallet) { if (!IsLocked()) TopUpKeyPool(); @@ -1261,49 +1364,75 @@ void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) 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); } } -void CWallet::KeepKey(int64 nIndex) +int64_t CWallet::AddReserveKey(const CKeyPool& keypool) +{ + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_wallet) + { + CWalletDB walletdb(strWalletFile); + + int64_t 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_t 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) +void CWallet::ReturnKey(int64_t nIndex) { // Return to key pool - CRITICAL_BLOCK(cs_setKeyPool) + CRITICAL_BLOCK(cs_wallet) setKeyPool.insert(nIndex); printf("keypool return %"PRI64d"\n", nIndex); } -vector CWallet::GetOrReuseKeyFromPool() +bool CWallet::GetKeyFromPool(vector& result, bool fAllowReuse) { - int64 nIndex = 0; + int64_t 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() +int64_t CWallet::GetOldestKeyPoolTime() { - int64 nIndex = 0; + int64_t nIndex = 0; CKeyPool keypool; ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) @@ -1318,7 +1447,13 @@ vector CReserveKey::GetReservedKey() { CKeyPool keypool; pwallet->ReserveKeyFromKeyPool(nIndex, keypool); - vchPubKey = keypool.vchPubKey; + 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; @@ -1340,3 +1475,23 @@ void CReserveKey::ReturnKey() vchPubKey.clear(); } +void CWallet::GetAllReserveAddresses(set& setAddress) +{ + setAddress.clear(); + + CWalletDB walletdb(strWalletFile); + + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_wallet) + BOOST_FOREACH(const int64_t& 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); + } +}