diff options
Diffstat (limited to 'src/wallet.cpp')
-rw-r--r-- | src/wallet.cpp | 367 |
1 files changed, 279 insertions, 88 deletions
diff --git a/src/wallet.cpp b/src/wallet.cpp index 27dbf61c2b..b51c4d4b14 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin developers +// 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. @@ -26,6 +26,7 @@ using namespace std; * Settings */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); +CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; unsigned int nTxConfirmTarget = 1; bool bSpendZeroConfChange = true; bool fSendFreeTransactions = false; @@ -70,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); @@ -88,7 +88,7 @@ CPubKey CWallet::GenerateNewKey() nTimeFirstKey = nCreationTime; if (!AddKeyPubKey(secret, pubkey)) - throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); + throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); return pubkey; } @@ -555,7 +555,7 @@ void CWallet::MarkDirty() } } -bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) +bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb) { uint256 hash = wtxIn.GetHash(); @@ -576,10 +576,10 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) if (fInsertedNew) { wtx.nTimeReceived = GetAdjustedTime(); - wtx.nOrderPos = IncOrderPosNext(); + wtx.nOrderPos = IncOrderPosNext(pwalletdb); wtx.nTimeSmart = wtx.nTimeReceived; - if (wtxIn.hashBlock != 0) + if (!wtxIn.hashBlock.IsNull()) { if (mapBlockIndex.count(wtxIn.hashBlock)) { @@ -619,7 +619,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } else - LogPrintf("AddToWallet() : found %s in block %s not in index\n", + LogPrintf("AddToWallet(): found %s in block %s not in index\n", wtxIn.GetHash().ToString(), wtxIn.hashBlock.ToString()); } @@ -630,7 +630,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) 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; @@ -653,7 +653,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) // Write to disk if (fInsertedNew || fUpdated) - if (!wtx.WriteToDisk()) + if (!wtx.WriteToDisk(pwalletdb)) return false; // Break debit/credit balance caches: @@ -689,10 +689,16 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl 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); + + // Do not flush the wallet here for performance reasons + // this is safe, as in case of a crash, we rescan the necessary blocks on startup through our SetBestChain-mechanism + CWalletDB walletdb(strWalletFile, "r+", false); + + return AddToWallet(wtx, false, &walletdb); } } return false; @@ -795,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()) @@ -811,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()) @@ -916,9 +922,9 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, CAmount& nReceived, } -bool CWalletTx::WriteToDisk() +bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb) { - return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); + return pwalletdb->WriteTx(GetHash(), *this); } /** @@ -1009,6 +1015,193 @@ set<uint256> CWalletTx::GetConflicts() const return result; } +CAmount CWalletTx::GetDebit(const isminefilter& filter) const +{ + if (vin.empty()) + return 0; + + CAmount debit = 0; + if(filter & ISMINE_SPENDABLE) + { + if (fDebitCached) + debit += nDebitCached; + else + { + nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE); + fDebitCached = true; + debit += nDebitCached; + } + } + if(filter & ISMINE_WATCH_ONLY) + { + if(fWatchDebitCached) + debit += nWatchDebitCached; + else + { + nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY); + fWatchDebitCached = true; + debit += nWatchDebitCached; + } + } + return debit; +} + +CAmount CWalletTx::GetCredit(const isminefilter& filter) const +{ + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + int64_t credit = 0; + if (filter & ISMINE_SPENDABLE) + { + // GetBalance can assume transactions in mapWallet won't change + if (fCreditCached) + credit += nCreditCached; + else + { + nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fCreditCached = true; + credit += nCreditCached; + } + } + if (filter & ISMINE_WATCH_ONLY) + { + if (fWatchCreditCached) + credit += nWatchCreditCached; + else + { + nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fWatchCreditCached = true; + credit += nWatchCreditCached; + } + } + return credit; +} + +CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const +{ + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureCreditCached) + return nImmatureCreditCached; + nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fImmatureCreditCached = true; + return nImmatureCreditCached; + } + + return 0; +} + +CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const +{ + if (pwallet == 0) + return 0; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableCreditCached) + return nAvailableCreditCached; + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!pwallet->IsSpent(hashTx, i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + return nCredit; +} + +CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const +{ + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureWatchCreditCached) + return nImmatureWatchCreditCached; + nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fImmatureWatchCreditCached = true; + return nImmatureWatchCreditCached; + } + + return 0; +} + +CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const +{ + if (pwallet == 0) + return 0; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableWatchCreditCached) + return nAvailableWatchCreditCached; + + CAmount nCredit = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!pwallet->IsSpent(GetHash(), i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableWatchCreditCached = nCredit; + fAvailableWatchCreditCached = true; + return nCredit; +} + +CAmount CWalletTx::GetChange() const +{ + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*this); + fChangeCached = true; + return nChangeCached; +} + +bool CWalletTx::IsTrusted() const +{ + // Quick answer in most cases + if (!IsFinalTx(*this)) + return false; + int nDepth = GetDepthInMainChain(); + if (nDepth >= 1) + return true; + if (nDepth < 0) + return false; + if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit + return false; + + // Trusted if all inputs are from us and are in the mempool: + BOOST_FOREACH(const CTxIn& txin, vin) + { + // Transactions not sent by us: not trusted + const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); + if (parent == NULL) + return false; + const CTxOut& parentOut = parent->vout[txin.prevout.n]; + if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) + return false; + } + return true; +} + void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away @@ -1382,6 +1575,28 @@ 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); { @@ -1417,10 +1632,14 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend, BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { CAmount nCredit = pcoin.first->vout[pcoin.second].nValue; - //The priority after the next block (depth+1) is used instead of the current, + //The coin age after the next block (depth+1) is used instead of the current, //reflecting an assumption the user would accept a bit more delay for //a chance at a free transaction. - dPriority += (double)nCredit * (pcoin.first->GetDepthInMainChain()+1); + //But mempool inputs might still be in the mempool, so their age stays 0 + int age = pcoin.first->GetDepthInMainChain(); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; } CAmount nChange = nValueIn - nValue - nFeeRet; @@ -1475,8 +1694,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; @@ -1499,27 +1722,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 (!fSendFreeTransactions || 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; @@ -1550,14 +1778,14 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) // 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* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : NULL; + CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r+") : NULL; // Take key pair from key pool so it won't be used again reservekey.KeepKey(); // Add tx to wallet, because if it has change it's also ours, // otherwise just for transaction history. - AddToWallet(wtxNew); + AddToWallet(wtxNew, false, pwalletdb); // Notify that old coins are spent set<CWalletTx*> setCoins; @@ -1579,7 +1807,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) if (!wtxNew.AcceptToMemoryPool(false)) { // This must not fail. The transaction has already been signed and recorded. - LogPrintf("CommitTransaction() : Error: Transaction not valid"); + LogPrintf("CommitTransaction(): Error: Transaction not valid"); return false; } wtxNew.RelayWalletTransaction(); @@ -1587,53 +1815,10 @@ 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); - // prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee - if (nFeeNeeded > 0 && nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes)) - nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes); // user selected total at least (default=true) if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK()) nFeeNeeded = payTxFee.GetFeePerK(); @@ -1644,6 +1829,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; } @@ -1809,7 +2000,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) - throw runtime_error("TopUpKeyPool() : writing generated key failed"); + throw runtime_error("TopUpKeyPool(): writing generated key failed"); setKeyPool.insert(nEnd); LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); } @@ -1836,9 +2027,9 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) nIndex = *(setKeyPool.begin()); setKeyPool.erase(setKeyPool.begin()); if (!walletdb.ReadPool(nIndex, keypool)) - throw runtime_error("ReserveKeyFromKeyPool() : read failed"); + throw runtime_error("ReserveKeyFromKeyPool(): read failed"); if (!HaveKey(keypool.vchPubKey.GetID())) - throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); + throw runtime_error("ReserveKeyFromKeyPool(): unknown key in key pool"); assert(keypool.vchPubKey.IsValid()); LogPrintf("keypool reserve %d\n", nIndex); } @@ -2086,11 +2277,11 @@ void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress) const { CKeyPool keypool; if (!walletdb.ReadPool(id, keypool)) - throw runtime_error("GetAllReserveKeyHashes() : read failed"); + throw runtime_error("GetAllReserveKeyHashes(): read failed"); assert(keypool.vchPubKey.IsValid()); CKeyID keyID = keypool.vchPubKey.GetID(); if (!HaveKey(keyID)) - throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool"); + throw runtime_error("GetAllReserveKeyHashes(): unknown key in key pool"); setAddress.insert(keyID); } } @@ -2303,7 +2494,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block) { vMerkleBranch.clear(); nIndex = -1; - LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); + LogPrintf("ERROR: SetMerkleBranch(): couldn't find tx in block\n"); return 0; } @@ -2323,7 +2514,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block) int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const { - if (hashBlock == 0 || nIndex == -1) + if (hashBlock.IsNull() || nIndex == -1) return 0; AssertLockHeld(cs_main); @@ -2365,9 +2556,9 @@ int CMerkleTx::GetBlocksToMaturity() const } -bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee) +bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) { CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectInsaneFee); + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectAbsurdFee); } |