diff options
Diffstat (limited to 'src/wallet.cpp')
-rw-r--r-- | src/wallet.cpp | 268 |
1 files changed, 160 insertions, 108 deletions
diff --git a/src/wallet.cpp b/src/wallet.cpp index caf1f67f51..1fe0552109 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "wallet.h" @@ -15,23 +15,33 @@ #include "util.h" #include "utilmoneystr.h" +#include <assert.h> + #include <boost/algorithm/string/replace.hpp> #include <boost/thread.hpp> using namespace std; -// Settings +/** + * Settings + */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); +CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; unsigned int nTxConfirmTarget = 1; bool bSpendZeroConfChange = true; +bool fSendFreeTransactions = false; +bool fPayAtLeastCustomFee = true; -/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ -CFeeRate CWallet::minTxFee = CFeeRate(10000); // Override with -mintxfee +/** + * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) + * Override with -mintxfee + */ +CFeeRate CWallet::minTxFee = CFeeRate(1000); -////////////////////////////////////////////////////////////////////////////// -// -// mapWallet -// +/** @defgroup mapWallet + * + * @{ + */ struct CompareValueOnly { @@ -61,7 +71,6 @@ CPubKey CWallet::GenerateNewKey() AssertLockHeld(cs_wallet); // mapKeyMetadata bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets - RandAddSeedPerfmon(); CKey secret; secret.MakeNewKey(fCompressed); @@ -70,6 +79,7 @@ CPubKey CWallet::GenerateNewKey() SetMinVersion(FEATURE_COMPRPUBKEY); CPubKey pubkey = secret.GetPubKey(); + assert(secret.VerifyPubKey(pubkey)); // Create new metadata int64_t nCreationTime = GetTime(); @@ -87,6 +97,13 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) AssertLockHeld(cs_wallet); // mapKeyMetadata if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) return false; + + // check if we need to remove from watch-only + CScript script; + script = GetScriptForDestination(pubkey.GetID()); + if (HaveWatchOnly(script)) + RemoveWatchOnly(script); + if (!fFileBacked) return true; if (!IsCrypted()) { @@ -149,7 +166,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript) * these. Do not add them to the wallet and warn. */ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { - std::string strAddr = CBitcoinAddress(redeemScript.GetID()).ToString(); + std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString(); LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); return true; @@ -169,6 +186,20 @@ bool CWallet::AddWatchOnly(const CScript &dest) return CWalletDB(strWalletFile).WriteWatchOnly(dest); } +bool CWallet::RemoveWatchOnly(const CScript &dest) +{ + AssertLockHeld(cs_wallet); + if (!CCryptoKeyStore::RemoveWatchOnly(dest)) + return false; + if (!HaveWatchOnly()) + NotifyWatchonlyChanged(false); + if (fFileBacked) + if (!CWalletDB(strWalletFile).EraseWatchOnly(dest)) + return false; + + return true; +} + bool CWallet::LoadWatchOnly(const CScript &dest) { return CCryptoKeyStore::AddWatchOnly(dest); @@ -344,8 +375,10 @@ void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range) } } -// Outpoint is spent if any non-conflicted transaction -// spends it: +/** + * Outpoint is spent if any non-conflicted transaction + * spends it: + */ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const { const COutPoint outpoint(hash, n); @@ -392,15 +425,13 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) RandAddSeedPerfmon(); vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); - if (!GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE)) - return false; + GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); CMasterKey kMasterKey; RandAddSeedPerfmon(); kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); - if (!GetRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE)) - return false; + GetRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); CCrypter crypter; int64_t nStartTime = GetTimeMillis(); @@ -426,17 +457,25 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; if (fFileBacked) { + assert(!pwalletdbEncryption); pwalletdbEncryption = new CWalletDB(strWalletFile); - if (!pwalletdbEncryption->TxnBegin()) + if (!pwalletdbEncryption->TxnBegin()) { + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; return false; + } pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); } if (!EncryptKeys(vMasterKey)) { - if (fFileBacked) + 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. + delete pwalletdbEncryption; + } + // We now probably have half of our keys encrypted in memory, and half not... + // die and let the user reload their unencrypted wallet. + assert(false); } // Encryption was introduced in version 0.4.0 @@ -444,8 +483,12 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) if (fFileBacked) { - 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. + if (!pwalletdbEncryption->TxnCommit()) { + delete pwalletdbEncryption; + // We now have keys encrypted in memory, but not on disk... + // die to avoid confusion and let the user reload their unencrypted wallet. + assert(false); + } delete pwalletdbEncryption; pwalletdbEncryption = NULL; @@ -536,7 +579,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD wtx.nOrderPos = IncOrderPosNext(pwalletdb); wtx.nTimeSmart = wtx.nTimeReceived; - if (wtxIn.hashBlock != 0) + if (!wtxIn.hashBlock.IsNull()) { if (mapBlockIndex.count(wtxIn.hashBlock)) { @@ -587,7 +630,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD if (!fInsertedNew) { // Merge - if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) + if (!wtxIn.hashBlock.IsNull() && wtxIn.hashBlock != wtx.hashBlock) { wtx.hashBlock = wtxIn.hashBlock; fUpdated = true; @@ -632,9 +675,11 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD return true; } -// 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. +/** + * 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) { { @@ -756,7 +801,7 @@ int CWalletTx::GetRequestCount() const if (IsCoinBase()) { // Generated block - if (hashBlock != 0) + if (!hashBlock.IsNull()) { map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) @@ -772,7 +817,7 @@ int CWalletTx::GetRequestCount() const nRequests = (*mi).second; // How about the block it's in? - if (nRequests == 0 && hashBlock != 0) + if (nRequests == 0 && !hashBlock.IsNull()) { map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) @@ -882,9 +927,11 @@ bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb) return pwalletdb->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. +/** + * 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; @@ -1006,15 +1053,15 @@ void CWallet::ResendWalletTransactions() } } +/** @} */ // end of mapWallet - -////////////////////////////////////////////////////////////////////////////// -// -// Actions -// +/** @defgroup Actions + * + * @{ + */ CAmount CWallet::GetBalance() const @@ -1074,7 +1121,7 @@ CAmount CWallet::GetWatchOnlyBalance() const nTotal += pcoin->GetAvailableWatchOnlyCredit(); } } - + return nTotal; } @@ -1107,7 +1154,9 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const return nTotal; } -// populate vCoins with vector of available COutputs. +/** + * populate vCoins with vector of available COutputs. + */ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { vCoins.clear(); @@ -1165,7 +1214,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns //The solver here uses a randomized algorithm, //the randomness serves no real security purpose but is just //needed to prevent degenerate behavior and it is important - //that the rng fast. We do not use a constant random sequence, + //that the rng is fast. We do not use a constant random sequence, //because there may be some privacy improvement by making //the selection random. if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) @@ -1339,10 +1388,32 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend, wtxNew.BindWallet(this); CMutableTransaction txNew; + // Discourage fee sniping. + // + // However because of a off-by-one-error in previous versions we need to + // neuter it by setting nLockTime to at least one less than nBestHeight. + // Secondly currently propagation of transactions created for block heights + // corresponding to blocks that were just mined may be iffy - transactions + // aren't re-accepted into the mempool - we additionally neuter the code by + // going ten blocks back. Doesn't yet do anything for sniping, but does act + // to shake out wallet bugs like not showing nLockTime'd transactions at + // all. + txNew.nLockTime = std::max(0, chainActive.Height() - 10); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + { LOCK2(cs_main, cs_wallet); { - nFeeRet = payTxFee.GetFeePerK(); + nFeeRet = 0; while (true) { txNew.vin.clear(); @@ -1432,8 +1503,12 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend, reservekey.ReturnKey(); // Fill vin + // + // Note how the sequence number is set to max()-1 so that the + // nLockTime set above actually works. BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) - txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); + txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(), + std::numeric_limits<unsigned int>::max()-1)); // Sign int nIn = 0; @@ -1456,27 +1531,32 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend, } dPriority = wtxNew.ComputePriority(dPriority, nBytes); - CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) + { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); + // Not enough mempool history to estimate: use hard-coded AllowFree. + if (dPriorityNeeded <= 0 && AllowFree(dPriority)) + break; + + // Small enough, and priority high enough, to send for free + if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded) + break; + } - if (nFeeRet >= nFeeNeeded) - break; // Done, enough fee included. + CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); - // Too big to send for free? Include more fee and try again: - if (nBytes > MAX_FREE_TRANSACTION_CREATE_SIZE) + // If we made it here and we aren't even able to meet the relay fee on the next pass, give up + // because we must be at the maximum allowed fee. + if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) { - nFeeRet = nFeeNeeded; - continue; + strFailReason = _("Transaction too large for fee policy"); + return false; } - // Not enough fee: enough priority? - double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); - // Not enough mempool history to estimate: use hard-coded AllowFree. - if (dPriorityNeeded <= 0 && AllowFree(dPriority)) - break; - - // Small enough, and priority high enough, to send for free - if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded) - break; + if (nFeeRet >= nFeeNeeded) + break; // Done, enough fee included. // Include more fee and try again. nFeeRet = nFeeNeeded; @@ -1495,7 +1575,9 @@ bool CWallet::CreateTransaction(CScript scriptPubKey, const CAmount& nValue, return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason, coinControl); } -// Call after CreateTransaction unless you want to abort +/** + * Call after CreateTransaction unless you want to abort + */ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) { { @@ -1542,50 +1624,13 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) return true; } - - - -string CWallet::SendMoney(const CTxDestination &address, CAmount nValue, CWalletTx& wtxNew) -{ - // Check amount - if (nValue <= 0) - return _("Invalid amount"); - if (nValue > GetBalance()) - return _("Insufficient funds"); - - string strError; - if (IsLocked()) - { - strError = _("Error: Wallet locked, unable to create transaction!"); - LogPrintf("SendMoney() : %s", strError); - return strError; - } - - // Parse Bitcoin address - CScript scriptPubKey = GetScriptForDestination(address); - - // Create and send the transaction - CReserveKey reservekey(this); - CAmount nFeeRequired; - if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, 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)); - LogPrintf("SendMoney() : %s\n", strError); - return strError; - } - if (!CommitTransaction(wtxNew, reservekey)) - return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); - - return ""; -} - - - CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) { // payTxFee is user-set "I want to pay this much" CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); + // user selected total at least (default=true) + if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK()) + nFeeNeeded = payTxFee.GetFeePerK(); // User didn't set: use -txconfirmtarget to estimate... if (nFeeNeeded == 0) nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes); @@ -1593,6 +1638,12 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge // back to a hard-coded fee if (nFeeNeeded == 0) nFeeNeeded = minTxFee.GetFee(nTxBytes); + // prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee + if (nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes)) + nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes); + // But always obey the maximum + if (nFeeNeeded > maxTxFee) + nFeeNeeded = maxTxFee; return nFeeNeeded; } @@ -1640,7 +1691,7 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) 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. + // that requires a new key. } } @@ -1707,10 +1758,10 @@ bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) return true; } -// -// Mark old keypool keys as used, -// and generate all new keys -// +/** + * Mark old keypool keys as used, + * and generate all new keys + */ bool CWallet::NewKeyPool() { { @@ -1979,7 +2030,7 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings() set<CTxDestination> CWallet::GetAccountAddresses(string strAccount) const { - AssertLockHeld(cs_wallet); // mapWallet + LOCK(cs_wallet); set<CTxDestination> result; BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, mapAddressBook) { @@ -2091,6 +2142,7 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) } } +/** @} */ // end of Actions class CAffectedKeysVisitor : public boost::static_visitor<void> { private: @@ -2269,9 +2321,9 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block) return chainActive.Height() - pindex->nHeight + 1; } -int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const +int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const { - if (hashBlock == 0 || nIndex == -1) + if (hashBlock.IsNull() || nIndex == -1) return 0; AssertLockHeld(cs_main); @@ -2295,7 +2347,7 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const return chainActive.Height() - pindex->nHeight + 1; } -int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const +int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const { AssertLockHeld(cs_main); int nResult = GetDepthInMainChainINTERNAL(pindexRet); |